mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
Kernel V3.0.0
This commit is contained in:
parent
ca997072ce
commit
73a34edd0e
81 changed files with 272886 additions and 13685 deletions
16
Makefile
16
Makefile
|
|
@ -83,6 +83,7 @@ $(BUILD_DIR)/process_asm.o: $(SRC_DIR)/process_asm.asm | $(BUILD_DIR)
|
||||||
# Link Kernel
|
# Link Kernel
|
||||||
$(KERNEL_ELF): $(OBJ_FILES)
|
$(KERNEL_ELF): $(OBJ_FILES)
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
|
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
|
||||||
|
$(MAKE) -C $(SRC_DIR)/userland
|
||||||
|
|
||||||
# Create ISO
|
# Create ISO
|
||||||
$(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
|
$(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
|
||||||
|
|
@ -92,13 +93,18 @@ $(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup
|
||||||
|
|
||||||
# Copy Kernel and Config
|
# Copy Kernel and Config
|
||||||
cp $(KERNEL_ELF) $(ISO_DIR)/
|
cp $(KERNEL_ELF) $(ISO_DIR)/
|
||||||
|
# Build ISO limine.cfg natively with modules
|
||||||
cp limine.cfg $(ISO_DIR)/
|
cp limine.cfg $(ISO_DIR)/
|
||||||
|
mkdir -p $(ISO_DIR)/bin
|
||||||
|
@for f in $(SRC_DIR)/userland/*.elf; do \
|
||||||
|
if [ -f "$$f" ]; then \
|
||||||
|
basename=$$(basename "$$f"); \
|
||||||
|
cp "$$f" $(ISO_DIR)/bin/; \
|
||||||
|
echo " MODULE_PATH=boot:///bin/$$basename" >> $(ISO_DIR)/limine.cfg; \
|
||||||
|
fi \
|
||||||
|
done
|
||||||
|
|
||||||
# Copy README
|
# Copy README
|
||||||
cp README.md $(ISO_DIR)/
|
|
||||||
|
|
||||||
# Copy user file.c if it exists
|
|
||||||
@if [ -f file.c ]; then cp file.c $(ISO_DIR)/; fi
|
|
||||||
|
|
||||||
# Copy Wallpaper (if it exists)
|
# Copy Wallpaper (if it exists)
|
||||||
@if [ -f src/kernel/wallpaper.ppm ]; then cp src/kernel/wallpaper.ppm $(ISO_DIR)/; fi
|
@if [ -f src/kernel/wallpaper.ppm ]; then cp src/kernel/wallpaper.ppm $(ISO_DIR)/; fi
|
||||||
|
|
@ -126,6 +132,8 @@ clean:
|
||||||
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
|
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
|
||||||
|
|
||||||
run: $(ISO_IMAGE)
|
run: $(ISO_IMAGE)
|
||||||
|
dd if=/dev/zero of=disk.img bs=1M count=64
|
||||||
|
mformat -i disk.img -F ::
|
||||||
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
|
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
|
||||||
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
|
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
|
||||||
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
|
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
|
||||||
|
|
|
||||||
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
BIN
build/about.o
BIN
build/about.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
Binary file not shown.
BIN
build/editor.o
BIN
build/editor.o
Binary file not shown.
BIN
build/explorer.o
BIN
build/explorer.o
Binary file not shown.
BIN
build/fat32.o
BIN
build/fat32.o
Binary file not shown.
BIN
build/graphics.o
BIN
build/graphics.o
Binary file not shown.
BIN
build/icmp.o
BIN
build/icmp.o
Binary file not shown.
BIN
build/idt.o
BIN
build/idt.o
Binary file not shown.
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
BIN
build/markdown.o
BIN
build/markdown.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/notepad.o
BIN
build/notepad.o
Binary file not shown.
BIN
build/paint.o
BIN
build/paint.o
Binary file not shown.
BIN
build/ps2.o
BIN
build/ps2.o
Binary file not shown.
BIN
build/viewer.o
BIN
build/viewer.o
Binary file not shown.
BIN
build/vm.o
BIN
build/vm.o
Binary file not shown.
Binary file not shown.
BIN
build/wm.o
BIN
build/wm.o
Binary file not shown.
BIN
disk.img
BIN
disk.img
Binary file not shown.
|
|
@ -1,166 +0,0 @@
|
||||||
# BoredOS
|
|
||||||
BoredOS has now exited Beta stage and is "stable enough" to be put out as a "stable" product.
|
|
||||||
|
|
||||||
<img src="bored.svg" width="200" /> </br>
|
|
||||||
BoredOS is a simple x86_64 hobbyist operating system.
|
|
||||||
It features a DE (and WM), a FAT32 filesystem, customizable UI and much much more!
|
|
||||||
|
|
||||||

|
|
||||||
*this screenshot might be outdated*
|
|
||||||
|
|
||||||
## Features
|
|
||||||
- JPG image support
|
|
||||||
- Disk manager
|
|
||||||
- Drag and drop mouse centered UI
|
|
||||||
- Customizable UI
|
|
||||||
- Basic Networking Stack
|
|
||||||
- Bored WM
|
|
||||||
- Fat 32 FS
|
|
||||||
- 64-bit long mode support
|
|
||||||
- Multiboot2 compliant
|
|
||||||
- Text editor
|
|
||||||
- Markdown Viewer
|
|
||||||
- Minesweeper
|
|
||||||
- Markdown Viewer
|
|
||||||
- GUI Text editor
|
|
||||||
- Paint application
|
|
||||||
- IDT
|
|
||||||
- Ability to run on actual x86_64 hardware
|
|
||||||
- CLI
|
|
||||||
- (Limited) C Compiler
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
To build BoredOS, you'll need the following tools installed:
|
|
||||||
|
|
||||||
- **x86_64 ELF Toolchain**: `x86_64-elf-gcc`, `x86_64-elf-ld`
|
|
||||||
- **NASM**: Netwide Assembler for compiling assembly code
|
|
||||||
- **xorriso**: For creating bootable ISO images
|
|
||||||
- **QEMU** (optional): For testing the kernel in an emulator
|
|
||||||
|
|
||||||
On macOS, you can install these using Homebrew:
|
|
||||||
```sh
|
|
||||||
brew install x86_64-elf-binutils x86_64-elf-gcc nasm xorriso qemu
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
Simply run `make` from the project root:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
This will:
|
|
||||||
1. Compile all kernel C sources and assembly files
|
|
||||||
2. Link the kernel ELF binary
|
|
||||||
3. Generate a bootable ISO image (`boredos.iso`)
|
|
||||||
|
|
||||||
The build output is organized as follows:
|
|
||||||
- Compiled object files: `build/`
|
|
||||||
- ISO root filesystem: `iso_root/`
|
|
||||||
- Final ISO image: `boredos.iso`
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
### QEMU Emulation
|
|
||||||
|
|
||||||
Run the kernel in QEMU:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make run
|
|
||||||
```
|
|
||||||
|
|
||||||
Or manually:
|
|
||||||
```sh
|
|
||||||
qemu-system-x86_64 -m 2G -serial stdio -cdrom boredos.iso -boot d
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running on Real Hardware
|
|
||||||
|
|
||||||
*Warning: This is at YOUR OWN RISK. This software comes with ZERO warranty and may break your system.*
|
|
||||||
|
|
||||||
1. **Create bootable USB**: Use [Balena Etcher](https://www.balena.io/etcher/) to flash `boredos.iso` to a USB drive
|
|
||||||
|
|
||||||
2. **Prepare the system**:
|
|
||||||
- Enable legacy (BIOS) boot in your system BIOS/UEFI settings
|
|
||||||
- Disable Secure Boot if needed
|
|
||||||
|
|
||||||
3. **Boot**: Insert the USB drive and select it in the boot menu during startup
|
|
||||||
|
|
||||||
**Networking requires an Intel E1000 network card or similar while using Ethernet.**
|
|
||||||
|
|
||||||
4. **Tested Hardware**:
|
|
||||||
- HP EliteDesk 705 G4 DM (AMD Ryzen 5 PRO 2400G, Radeon Vega) **Tested, no networking.**
|
|
||||||
- Lenovo ThinkPad A475 20KL002VMH (AMD Pro A12-8830B, Radeon R7) **Tested, no networking.**
|
|
||||||
- Acer Aspire E5-573-311M (Intel Core i3-5005U, Intel HD Graphics) **Tested, no networking.**
|
|
||||||
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
- `src/kernel/` - Main kernel implementation
|
|
||||||
- `boot.asm` - Boot assembly code
|
|
||||||
- `main.c` - Kernel entry point
|
|
||||||
- `*.c / *.h` - Core kernel modules (graphics, interrupts, filesystem, etc.)
|
|
||||||
- `cli_apps/` - Command-line applications
|
|
||||||
- `build/` - Compiled object files (generated during build)
|
|
||||||
- `iso_root/` - ISO filesystem layout (generated during build)
|
|
||||||
- `limine/` - Limine bootloader files (downloaded automatically)
|
|
||||||
- `linker.ld` - Linker script for x86_64 ELF
|
|
||||||
- `limine.cfg` - Limine bootloader configuration
|
|
||||||
- `Makefile` - Build configuration and targets
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
###
|
|
||||||
|
|
||||||
<h2 align="left">Help me brew some coffee! ☕️</h2>
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
<p align="left">
|
|
||||||
If you enjoy this project, and like what i'm doing here, consider buying me a coffee!
|
|
||||||
<br><br>
|
|
||||||
<a href="https://buymeacoffee.com/boreddevnl" target="_blank">
|
|
||||||
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
|
|
||||||
## This project was previously labeled as "BrewKernel"
|
|
||||||
Brewkernel was a text only very simple (and messy) project i started 3 years ago. It was my first work in OSDev and i absolutely loved it. It sadly just got too messy and i myself couldn't understand my own code anymore. About a year ago i started work on BoredOS, and pushed a *"working"* version of it a few days ago as of writing this *(Feb. 10 2026)*
|
|
||||||
Brewkernel has already been deprecated and will not be accepting any pull requests or fix any issues as it is now a public archive.
|
|
||||||
Thanks to everyone who helped me with Brewkernel, even if it were just ideas, and intend to keep working on this for the forseeable future!
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Copyright (C) 2024-2026 boreddevnl
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
NOTICE
|
|
||||||
------
|
|
||||||
|
|
||||||
This product includes software developed by Chris ("boreddevnl") as part of the BoredOS (Previously Brewkernel/BrewOS) project.
|
|
||||||
|
|
||||||
Copyright (C) 2024–2026 Chris / boreddevnl (previously boreddevhq)
|
|
||||||
|
|
||||||
All source files in this repository contain copyright and license
|
|
||||||
headers that must be preserved in redistributions and derivative works.
|
|
||||||
|
|
||||||
If you distribute or modify this project (in whole or in part),
|
|
||||||
you MUST:
|
|
||||||
|
|
||||||
- Retain all copyright and license headers at the top of each file.
|
|
||||||
- Include this NOTICE file along with any redistributions or
|
|
||||||
derivative works.
|
|
||||||
- Provide clear attribution to the original author in documentation
|
|
||||||
or credits where appropriate.
|
|
||||||
|
|
||||||
The above attribution requirements are informational and intended to
|
|
||||||
ensure proper credit is given. They do not alter or supersede the
|
|
||||||
terms of the GNU General Public License (GPL), which governs this work.
|
|
||||||
BIN
iso_root/bin/calculator.elf
Executable file
BIN
iso_root/bin/calculator.elf
Executable file
Binary file not shown.
BIN
iso_root/bin/crash.elf
Executable file
BIN
iso_root/bin/crash.elf
Executable file
Binary file not shown.
BIN
iso_root/bin/hello.elf
Executable file
BIN
iso_root/bin/hello.elf
Executable file
Binary file not shown.
Binary file not shown.
|
|
@ -4,6 +4,8 @@ TIMEOUT=3
|
||||||
PROTOCOL=limine
|
PROTOCOL=limine
|
||||||
|
|
||||||
KERNEL_PATH=boot:///boredos.elf
|
KERNEL_PATH=boot:///boredos.elf
|
||||||
|
|
||||||
#FRAMEBUFFER_WIDTH=1280
|
#FRAMEBUFFER_WIDTH=1280
|
||||||
#FRAMEBUFFER_HEIGHT=720
|
#FRAMEBUFFER_HEIGHT=720
|
||||||
|
MODULE_PATH=boot:///bin/calculator.elf
|
||||||
|
MODULE_PATH=boot:///bin/crash.elf
|
||||||
|
MODULE_PATH=boot:///bin/hello.elf
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,5 @@ TIMEOUT=3
|
||||||
PROTOCOL=limine
|
PROTOCOL=limine
|
||||||
|
|
||||||
KERNEL_PATH=boot:///boredos.elf
|
KERNEL_PATH=boot:///boredos.elf
|
||||||
|
|
||||||
#FRAMEBUFFER_WIDTH=1280
|
#FRAMEBUFFER_WIDTH=1280
|
||||||
#FRAMEBUFFER_HEIGHT=720
|
#FRAMEBUFFER_HEIGHT=720
|
||||||
|
|
|
||||||
284734
qemu-debug.log
284734
qemu-debug.log
File diff suppressed because it is too large
Load diff
|
|
@ -14,8 +14,8 @@ static void about_paint(Window *win) {
|
||||||
|
|
||||||
// Version info
|
// Version info
|
||||||
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_WHITE);
|
||||||
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.63", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.64", COLOR_WHITE);
|
||||||
draw_string(offset_x, offset_y + 135, "Kernel Version 2.5.3", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 135, "Kernel Version 3.0.0", COLOR_WHITE);
|
||||||
|
|
||||||
// Copyright
|
// Copyright
|
||||||
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_WHITE);
|
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_WHITE);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#ifndef CALCULATOR_H
|
|
||||||
#define CALCULATOR_H
|
|
||||||
|
|
||||||
#include "wm.h"
|
|
||||||
|
|
||||||
extern Window win_calculator;
|
|
||||||
|
|
||||||
void calculator_init(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
void cli_cmd_boredver(char *args) {
|
void cli_cmd_boredver(char *args) {
|
||||||
(void)args;
|
(void)args;
|
||||||
cli_write("BoredOS v1.63\n");
|
cli_write("BoredOS v1.64\n");
|
||||||
cli_write("BoredOS Kernel V2.5.3\n");
|
cli_write("BoredOS Kernel V3.0.0\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ extern Window win_editor;
|
||||||
extern Window win_explorer;
|
extern Window win_explorer;
|
||||||
extern Window win_cmd;
|
extern Window win_cmd;
|
||||||
extern Window win_notepad;
|
extern Window win_notepad;
|
||||||
extern Window win_calculator;
|
|
||||||
|
|
||||||
void cli_cmd_txtedit(char *args) {
|
void cli_cmd_txtedit(char *args) {
|
||||||
// Parse the file path argument
|
// Parse the file path argument
|
||||||
|
|
@ -52,7 +51,6 @@ void cli_cmd_txtedit(char *args) {
|
||||||
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
|
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
|
||||||
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
||||||
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
||||||
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
|
|
||||||
win_editor.z_index = max_z + 1;
|
win_editor.z_index = max_z + 1;
|
||||||
|
|
||||||
cli_write("Opening: ");
|
cli_write("Opening: ");
|
||||||
|
|
|
||||||
190
src/kernel/cmd.c
190
src/kernel/cmd.c
|
|
@ -4,13 +4,13 @@
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "rtc.h"
|
#include "rtc.h"
|
||||||
#include "notepad.h"
|
#include "notepad.h"
|
||||||
#include "calculator.h"
|
|
||||||
#include "fat32.h"
|
#include "fat32.h"
|
||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
#include "cli_apps/cli_apps.h"
|
#include "cli_apps/cli_apps.h"
|
||||||
#include "licensewr.h"
|
#include "licensewr.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
|
#include "process.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
@ -61,6 +61,9 @@ static char pager_wrapped_lines[2000][CMD_COLS + 1];
|
||||||
static int pager_total_lines = 0;
|
static int pager_total_lines = 0;
|
||||||
static int pager_top_line = 0;
|
static int pager_top_line = 0;
|
||||||
|
|
||||||
|
// Process Execution State
|
||||||
|
bool cmd_is_waiting_for_process = false;
|
||||||
|
|
||||||
// Boot time for uptime
|
// Boot time for uptime
|
||||||
int boot_time_init = 0;
|
int boot_time_init = 0;
|
||||||
|
|
||||||
|
|
@ -147,6 +150,13 @@ static void cmd_history_add(const char *cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_print_prompt(void) {
|
static void cmd_print_prompt(void) {
|
||||||
|
// Clear the current line to prevent old output from corrupting the new command buffer
|
||||||
|
for (int i = 0; i < CMD_COLS; i++) {
|
||||||
|
screen_buffer[cursor_row][i].c = 0;
|
||||||
|
screen_buffer[cursor_row][i].color = current_color;
|
||||||
|
}
|
||||||
|
cursor_col = 0;
|
||||||
|
|
||||||
char buf[5];
|
char buf[5];
|
||||||
buf[0] = cmd_state ? cmd_state->current_drive : 'A';
|
buf[0] = cmd_state ? cmd_state->current_drive : 'A';
|
||||||
buf[1] = ':';
|
buf[1] = ':';
|
||||||
|
|
@ -235,6 +245,10 @@ void cmd_putchar(char c) {
|
||||||
cmd_scroll_up();
|
cmd_scroll_up();
|
||||||
cursor_row = CMD_ROWS - 1;
|
cursor_row = CMD_ROWS - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger repaint so output from syscalls is immediately visible
|
||||||
|
wm_mark_dirty(win_cmd.x, win_cmd.y, win_cmd.w, win_cmd.h);
|
||||||
|
wm_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public for CLI apps to use
|
// Public for CLI apps to use
|
||||||
|
|
@ -265,6 +279,23 @@ void cmd_write_int(int n) {
|
||||||
cmd_write(buf);
|
cmd_write(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd_write_hex(uint64_t n) {
|
||||||
|
const char* digits = "0123456789ABCDEF";
|
||||||
|
char buf[19];
|
||||||
|
buf[0] = '0';
|
||||||
|
buf[1] = 'x';
|
||||||
|
buf[18] = '\0';
|
||||||
|
for (int i = 17; i >= 2; i--) {
|
||||||
|
buf[i] = digits[n & 0xF];
|
||||||
|
n >>= 4;
|
||||||
|
}
|
||||||
|
cmd_write(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_get_cursor_col(void) {
|
||||||
|
return cursor_col;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Pager Logic ---
|
// --- Pager Logic ---
|
||||||
|
|
||||||
// Public for CLI apps to use - clear the terminal screen
|
// Public for CLI apps to use - clear the terminal screen
|
||||||
|
|
@ -524,7 +555,6 @@ static void internal_cmd_txtedit(char *args) {
|
||||||
extern Window win_explorer;
|
extern Window win_explorer;
|
||||||
extern Window win_cmd;
|
extern Window win_cmd;
|
||||||
extern Window win_notepad;
|
extern Window win_notepad;
|
||||||
extern Window win_calculator;
|
|
||||||
|
|
||||||
win_editor.visible = true;
|
win_editor.visible = true;
|
||||||
win_editor.focused = true;
|
win_editor.focused = true;
|
||||||
|
|
@ -534,7 +564,6 @@ static void internal_cmd_txtedit(char *args) {
|
||||||
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
|
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
|
||||||
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
||||||
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
||||||
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
|
|
||||||
win_editor.z_index = max_z + 1;
|
win_editor.z_index = max_z + 1;
|
||||||
|
|
||||||
cmd_write("Opening: ");
|
cmd_write("Opening: ");
|
||||||
|
|
@ -580,7 +609,59 @@ static void internal_cmd_ls(char *args) {
|
||||||
kfree(files);
|
kfree(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Commands (now delegated to cli_apps/) ---
|
|
||||||
|
void cmd_exec_elf(char *args) {
|
||||||
|
if (!args || args[0] == '\0') {
|
||||||
|
cmd_write("Usage: exec <filename>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char full_exec_path[512] = {0};
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (args[0] && args[1] != ':') {
|
||||||
|
if (cmd_state) {
|
||||||
|
full_exec_path[i++] = cmd_state->current_drive;
|
||||||
|
full_exec_path[i++] = ':';
|
||||||
|
const char *dir = cmd_state->current_dir;
|
||||||
|
while (*dir && i < 509) {
|
||||||
|
full_exec_path[i++] = *dir++;
|
||||||
|
}
|
||||||
|
if (i > 2 && full_exec_path[i-1] != '/') {
|
||||||
|
full_exec_path[i++] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *p = args;
|
||||||
|
while (*p && i < 509) {
|
||||||
|
full_exec_path[i++] = *p++;
|
||||||
|
}
|
||||||
|
full_exec_path[i] = 0;
|
||||||
|
} else {
|
||||||
|
// Just copy what they provided
|
||||||
|
const char *p = args;
|
||||||
|
while (*p && i < 511) {
|
||||||
|
full_exec_path[i++] = *p++;
|
||||||
|
}
|
||||||
|
full_exec_path[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_is_waiting_for_process = true;
|
||||||
|
process_create_elf(full_exec_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public API for syscall exit
|
||||||
|
void cmd_process_finished(void) {
|
||||||
|
if (cmd_is_waiting_for_process) {
|
||||||
|
cmd_is_waiting_for_process = false;
|
||||||
|
|
||||||
|
extern int cursor_col;
|
||||||
|
void cmd_putchar(char c);
|
||||||
|
if (cursor_col > 0) {
|
||||||
|
cmd_putchar('\n');
|
||||||
|
}
|
||||||
|
cmd_print_prompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Command dispatch table
|
// Command dispatch table
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -589,6 +670,8 @@ typedef struct {
|
||||||
} CommandEntry;
|
} CommandEntry;
|
||||||
|
|
||||||
static const CommandEntry commands[] = {
|
static const CommandEntry commands[] = {
|
||||||
|
{"EXEC", cmd_exec_elf},
|
||||||
|
{"exec", cmd_exec_elf},
|
||||||
{"HELP", cli_cmd_help},
|
{"HELP", cli_cmd_help},
|
||||||
{"help", cli_cmd_help},
|
{"help", cli_cmd_help},
|
||||||
{"DATE", cli_cmd_date},
|
{"DATE", cli_cmd_date},
|
||||||
|
|
@ -1060,44 +1143,66 @@ static void cmd_exec_single(char *cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for executable in /Apps/
|
// Check for executable in Current Directory or A:/bin/
|
||||||
char app_path[256];
|
char search_path[512];
|
||||||
int app_idx = 0;
|
|
||||||
|
|
||||||
// Add drive letter if on different drive
|
// Check if the command already ends in .elf (case insensitive)
|
||||||
if (cmd_state && cmd_state->current_drive != 'A') {
|
bool has_elf_ext = false;
|
||||||
app_path[app_idx++] = cmd_state->current_drive;
|
int cmd_len = cmd_strlen(cmd);
|
||||||
app_path[app_idx++] = ':';
|
if (cmd_len > 4) {
|
||||||
}
|
const char *ext = cmd + cmd_len - 4;
|
||||||
|
if ((ext[0] == '.' && (ext[1] == 'e' || ext[1] == 'E') &&
|
||||||
const char *prefix = "/Apps/";
|
(ext[2] == 'l' || ext[2] == 'L') && (ext[3] == 'f' || ext[3] == 'F'))) {
|
||||||
while (*prefix) app_path[app_idx++] = *prefix++;
|
has_elf_ext = true;
|
||||||
char *c = cmd;
|
|
||||||
while (*c && app_idx < 255) app_path[app_idx++] = *c++;
|
|
||||||
app_path[app_idx] = 0;
|
|
||||||
|
|
||||||
FAT32_FileHandle *app_fh = fat32_open(app_path, "r");
|
|
||||||
if (app_fh) {
|
|
||||||
uint8_t *app_buffer = (uint8_t*)kmalloc(VM_MEMORY_SIZE);
|
|
||||||
if (app_buffer) {
|
|
||||||
int size = fat32_read(app_fh, app_buffer, VM_MEMORY_SIZE);
|
|
||||||
fat32_close(app_fh);
|
|
||||||
|
|
||||||
if (size > 0) {
|
|
||||||
int res = vm_exec(app_buffer, size);
|
|
||||||
if (res != 0) {
|
|
||||||
cmd_write("Execution failed (invalid format or runtime error).\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cmd_write("Error: Empty file.\n");
|
|
||||||
}
|
|
||||||
kfree(app_buffer);
|
|
||||||
} else {
|
|
||||||
fat32_close(app_fh);
|
|
||||||
cmd_write("Error: Out of memory.\n");
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Try Current Directory + .elf
|
||||||
|
if (cmd_state) {
|
||||||
|
int idx = 0;
|
||||||
|
search_path[idx++] = cmd_state->current_drive;
|
||||||
|
search_path[idx++] = ':';
|
||||||
|
const char *dir = cmd_state->current_dir;
|
||||||
|
while (*dir && idx < 500) search_path[idx++] = *dir++;
|
||||||
|
if (idx > 2 && search_path[idx-1] != '/') search_path[idx++] = '/';
|
||||||
|
const char *c = cmd;
|
||||||
|
while (*c && idx < 500) search_path[idx++] = *c++;
|
||||||
|
if (!has_elf_ext) {
|
||||||
|
search_path[idx++] = '.'; search_path[idx++] = 'e'; search_path[idx++] = 'l'; search_path[idx++] = 'f';
|
||||||
|
}
|
||||||
|
search_path[idx] = 0;
|
||||||
|
|
||||||
|
FAT32_FileHandle *fh = fat32_open(search_path, "r");
|
||||||
|
if (fh) {
|
||||||
|
fat32_close(fh);
|
||||||
|
cmd_is_waiting_for_process = true;
|
||||||
|
process_create_elf(search_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Try A:/bin/ + .elf
|
||||||
|
{
|
||||||
|
int idx = 0;
|
||||||
|
const char *bin_prefix = "A:/bin/";
|
||||||
|
while (*bin_prefix) search_path[idx++] = *bin_prefix++;
|
||||||
|
const char *c = cmd;
|
||||||
|
while (*c && idx < 500) search_path[idx++] = *c++;
|
||||||
|
if (!has_elf_ext) {
|
||||||
|
search_path[idx++] = '.'; search_path[idx++] = 'e'; search_path[idx++] = 'l'; search_path[idx++] = 'f';
|
||||||
|
}
|
||||||
|
search_path[idx] = 0;
|
||||||
|
|
||||||
|
FAT32_FileHandle *fh = fat32_open(search_path, "r");
|
||||||
|
if (fh) {
|
||||||
|
fat32_close(fh);
|
||||||
|
cmd_is_waiting_for_process = true;
|
||||||
|
process_create_elf(search_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cmd_write("Unknown command: ");
|
cmd_write("Unknown command: ");
|
||||||
cmd_write(cmd);
|
cmd_write(cmd);
|
||||||
|
|
@ -1402,7 +1507,9 @@ static void cmd_key(Window *target, char c) {
|
||||||
|
|
||||||
cmd_exec(cmd_buf);
|
cmd_exec(cmd_buf);
|
||||||
|
|
||||||
cmd_print_prompt();
|
if (!cmd_is_waiting_for_process) {
|
||||||
|
cmd_print_prompt();
|
||||||
|
}
|
||||||
} else if (c == 17) { // UP
|
} else if (c == 17) { // UP
|
||||||
if (history_len > 0) {
|
if (history_len > 0) {
|
||||||
if (history_pos == -1) {
|
if (history_pos == -1) {
|
||||||
|
|
@ -1476,6 +1583,8 @@ static void create_test_files(void) {
|
||||||
if (!fat32_exists("Apps")) fat32_mkdir("Apps");
|
if (!fat32_exists("Apps")) fat32_mkdir("Apps");
|
||||||
if (!fat32_exists("Desktop")) fat32_mkdir("Desktop");
|
if (!fat32_exists("Desktop")) fat32_mkdir("Desktop");
|
||||||
if (!fat32_exists("RecycleBin")) fat32_mkdir("RecycleBin");
|
if (!fat32_exists("RecycleBin")) fat32_mkdir("RecycleBin");
|
||||||
|
if (!fat32_exists("bin")) fat32_mkdir("bin");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Always try to write README to ensure content exists
|
// Always try to write README to ensure content exists
|
||||||
|
|
@ -1676,7 +1785,6 @@ static void create_test_files(void) {
|
||||||
|
|
||||||
|
|
||||||
void cmd_init(void) {
|
void cmd_init(void) {
|
||||||
fat32_init(); // Init FAT32 filesystem
|
|
||||||
create_test_files();
|
create_test_files();
|
||||||
|
|
||||||
win_cmd.title = "Command Prompt";
|
win_cmd.title = "Command Prompt";
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ void cmd_reset(void);
|
||||||
void cmd_write(const char *str);
|
void cmd_write(const char *str);
|
||||||
void cmd_putchar(char c);
|
void cmd_putchar(char c);
|
||||||
void cmd_write_int(int n);
|
void cmd_write_int(int n);
|
||||||
|
void cmd_write_hex(uint64_t n);
|
||||||
|
int cmd_get_cursor_col(void);
|
||||||
void cmd_screen_clear(void);
|
void cmd_screen_clear(void);
|
||||||
|
|
||||||
void cmd_increment_msg_count(void);
|
void cmd_increment_msg_count(void);
|
||||||
|
|
|
||||||
143
src/kernel/elf.c
Normal file
143
src/kernel/elf.c
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
#include "elf.h"
|
||||||
|
#include "fat32.h"
|
||||||
|
#include "memory_manager.h"
|
||||||
|
#include "paging.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
extern void serial_print(const char *s);
|
||||||
|
extern void serial_write(const char *str);
|
||||||
|
static void print_hex(uint64_t n) {
|
||||||
|
char buf[17];
|
||||||
|
char* x = "0123456789ABCDEF";
|
||||||
|
buf[16] = 0;
|
||||||
|
for (int i=15; i>=0; i--) { buf[i] = x[n & 0xF]; n >>= 4; }
|
||||||
|
serial_write(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t elf_load(const char *path, uint64_t user_pml4) {
|
||||||
|
FAT32_FileHandle *file = fat32_open(path, "r");
|
||||||
|
if (!file || !file->valid) {
|
||||||
|
serial_write("[ELF] Error: Failed to open file ");
|
||||||
|
serial_write(path);
|
||||||
|
serial_write("\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ELF Header
|
||||||
|
Elf64_Ehdr ehdr;
|
||||||
|
if (fat32_read(file, &ehdr, sizeof(Elf64_Ehdr)) != sizeof(Elf64_Ehdr)) {
|
||||||
|
serial_write("[ELF] Error: Could not read ELF Header\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate Magic Number & Properties
|
||||||
|
if (ehdr.e_ident[0] != ELFMAG0 || ehdr.e_ident[1] != ELFMAG1 ||
|
||||||
|
ehdr.e_ident[2] != ELFMAG2 || ehdr.e_ident[3] != ELFMAG3) {
|
||||||
|
serial_write("[ELF] Error: Invalid ELF Magic Number\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ehdr.e_ident[4] != ELFCLASS64) {
|
||||||
|
serial_write("[ELF] Error: Not a 64-bit ELF\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ehdr.e_ident[5] != ELFDATA2LSB) {
|
||||||
|
serial_write("[ELF] Error: Not Little Endian\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) {
|
||||||
|
serial_write("[ELF] Error: Not an Executable\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ehdr.e_machine != EM_X86_64) {
|
||||||
|
serial_write("[ELF] Error: Not x86_64 Architecture\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_write("[ELF] Number of program headers: ");
|
||||||
|
print_hex(ehdr.e_phnum);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
// Iterate Over Program Headers
|
||||||
|
for (int i = 0; i < ehdr.e_phnum; i++) {
|
||||||
|
fat32_seek(file, ehdr.e_phoff + (i * ehdr.e_phentsize), 0);
|
||||||
|
Elf64_Phdr phdr;
|
||||||
|
if (fat32_read(file, &phdr, sizeof(Elf64_Phdr)) != sizeof(Elf64_Phdr)) {
|
||||||
|
serial_write("[ELF] Error: Failed to read Program Header\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_write("[ELF] Header type parsed: ");
|
||||||
|
print_hex(phdr.p_type);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
// Only load segments with type PT_LOAD
|
||||||
|
if (phdr.p_type == PT_LOAD) {
|
||||||
|
uint64_t p_vaddr = phdr.p_vaddr;
|
||||||
|
uint64_t p_memsz = phdr.p_memsz;
|
||||||
|
uint64_t p_filesz = phdr.p_filesz;
|
||||||
|
uint64_t p_offset = phdr.p_offset;
|
||||||
|
|
||||||
|
serial_write("[ELF] Loaded PT_LOAD segment vaddr=");
|
||||||
|
print_hex(p_vaddr);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
if (p_memsz == 0) continue;
|
||||||
|
|
||||||
|
// Calculate page-aligned boundaries
|
||||||
|
uint64_t align_offset = p_vaddr & 0xFFF;
|
||||||
|
uint64_t start_page = p_vaddr & ~0xFFF;
|
||||||
|
uint64_t num_pages = (p_memsz + align_offset + 0xFFF) / 4096;
|
||||||
|
|
||||||
|
// Allocate and Map Pages
|
||||||
|
for (uint64_t p = 0; p < num_pages; p++) {
|
||||||
|
uint64_t vaddr = start_page + (p * 4096);
|
||||||
|
void* phys = kmalloc_aligned(4096, 4096);
|
||||||
|
if (!phys) {
|
||||||
|
serial_write("[ELF] Error: Out of memory mapping PT_LOAD\n");
|
||||||
|
fat32_close(file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Determine Flags
|
||||||
|
uint64_t flags = 0x07; // Present, RW, User
|
||||||
|
|
||||||
|
paging_map_page(user_pml4, vaddr, v2p((uint64_t)phys), flags);
|
||||||
|
|
||||||
|
// Initialize page memory
|
||||||
|
uint8_t* dest = (uint8_t*)phys;
|
||||||
|
for (int j=0; j<4096; j++) dest[j] = 0; // zero memory (handles BSS)
|
||||||
|
|
||||||
|
// If loading from file
|
||||||
|
if (p_filesz > 0) {
|
||||||
|
uint64_t copy_start_offset = 0;
|
||||||
|
uint64_t file_seek_pos = p_offset;
|
||||||
|
uint64_t bytes_to_copy = 4096;
|
||||||
|
|
||||||
|
if (p == 0) {
|
||||||
|
copy_start_offset = align_offset;
|
||||||
|
bytes_to_copy = 4096 - align_offset;
|
||||||
|
} else {
|
||||||
|
file_seek_pos += (p * 4096) - align_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_to_copy > p_filesz) {
|
||||||
|
bytes_to_copy = p_filesz;
|
||||||
|
}
|
||||||
|
if (bytes_to_copy > 0) {
|
||||||
|
fat32_seek(file, file_seek_pos, 0);
|
||||||
|
fat32_read(file, dest + copy_start_offset, bytes_to_copy);
|
||||||
|
p_filesz -= bytes_to_copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fat32_close(file);
|
||||||
|
return ehdr.e_entry;
|
||||||
|
}
|
||||||
74
src/kernel/elf.h
Normal file
74
src/kernel/elf.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef ELF_H
|
||||||
|
#define ELF_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint16_t Elf64_Half;
|
||||||
|
typedef uint32_t Elf64_Word;
|
||||||
|
typedef int32_t Elf64_Sword;
|
||||||
|
typedef uint64_t Elf64_Xword;
|
||||||
|
typedef int64_t Elf64_Sxword;
|
||||||
|
typedef uint64_t Elf64_Addr;
|
||||||
|
typedef uint64_t Elf64_Off;
|
||||||
|
|
||||||
|
#define EI_NIDENT 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
|
||||||
|
Elf64_Half e_type; /* Object file type */
|
||||||
|
Elf64_Half e_machine; /* Architecture */
|
||||||
|
Elf64_Word e_version; /* Object file version */
|
||||||
|
Elf64_Addr e_entry; /* Entry point virtual address */
|
||||||
|
Elf64_Off e_phoff; /* Program header table file offset */
|
||||||
|
Elf64_Off e_shoff; /* Section header table file offset */
|
||||||
|
Elf64_Word e_flags; /* Processor-specific flags */
|
||||||
|
Elf64_Half e_ehsize; /* ELF header size in bytes */
|
||||||
|
Elf64_Half e_phentsize; /* Program header table entry size */
|
||||||
|
Elf64_Half e_phnum; /* Program header table entry count */
|
||||||
|
Elf64_Half e_shentsize; /* Section header table entry size */
|
||||||
|
Elf64_Half e_shnum; /* Section header table entry count */
|
||||||
|
Elf64_Half e_shstrndx; /* Section header string table index */
|
||||||
|
} Elf64_Ehdr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Elf64_Word p_type; /* Segment type */
|
||||||
|
Elf64_Word p_flags; /* Segment flags */
|
||||||
|
Elf64_Off p_offset; /* Segment file offset */
|
||||||
|
Elf64_Addr p_vaddr; /* Segment virtual address */
|
||||||
|
Elf64_Addr p_paddr; /* Segment physical address */
|
||||||
|
Elf64_Xword p_filesz; /* Segment size in file */
|
||||||
|
Elf64_Xword p_memsz; /* Segment size in memory */
|
||||||
|
Elf64_Xword p_align; /* Segment alignment */
|
||||||
|
} Elf64_Phdr;
|
||||||
|
|
||||||
|
/* e_ident constants */
|
||||||
|
#define ELFMAG0 0x7f
|
||||||
|
#define ELFMAG1 'E'
|
||||||
|
#define ELFMAG2 'L'
|
||||||
|
#define ELFMAG3 'F'
|
||||||
|
#define ELFCLASS64 2
|
||||||
|
#define ELFDATA2LSB 1
|
||||||
|
#define EV_CURRENT 1
|
||||||
|
|
||||||
|
/* e_type constants */
|
||||||
|
#define ET_EXEC 2
|
||||||
|
#define ET_DYN 3
|
||||||
|
|
||||||
|
/* e_machine constants */
|
||||||
|
#define EM_X86_64 62
|
||||||
|
|
||||||
|
/* p_type constants */
|
||||||
|
#define PT_LOAD 1
|
||||||
|
|
||||||
|
/* p_flags constants */
|
||||||
|
#define PF_X 1
|
||||||
|
#define PF_W 2
|
||||||
|
#define PF_R 4
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Loads the ELF executable at 'path' using fat32 into the pagemap given by user_pml4.
|
||||||
|
// Returns entry point address on success, or 0 on failure.
|
||||||
|
uint64_t elf_load(const char *path, uint64_t user_pml4);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include "markdown.h"
|
#include "markdown.h"
|
||||||
#include "cmd.h"
|
#include "cmd.h"
|
||||||
#include "notepad.h"
|
#include "notepad.h"
|
||||||
#include "calculator.h"
|
#include "process.h"
|
||||||
#include "minesweeper.h"
|
#include "minesweeper.h"
|
||||||
#include "viewer.h"
|
#include "viewer.h"
|
||||||
#include "control_panel.h"
|
#include "control_panel.h"
|
||||||
|
|
@ -816,7 +816,9 @@ static void explorer_open_target(const char *path) {
|
||||||
if (fat32_is_directory(path)) {
|
if (fat32_is_directory(path)) {
|
||||||
explorer_open_directory(path);
|
explorer_open_directory(path);
|
||||||
} else {
|
} else {
|
||||||
if (explorer_is_markdown_file(path)) {
|
if (explorer_str_ends_with(path, ".elf")) {
|
||||||
|
process_create_elf(path);
|
||||||
|
} else if (explorer_is_markdown_file(path)) {
|
||||||
wm_bring_to_front(&win_markdown);
|
wm_bring_to_front(&win_markdown);
|
||||||
markdown_open_file(path);
|
markdown_open_file(path);
|
||||||
} else if (explorer_str_ends_with(path, ".pnt")) {
|
} else if (explorer_str_ends_with(path, ".pnt")) {
|
||||||
|
|
@ -853,7 +855,7 @@ static void explorer_open_item(Window *win, int index) {
|
||||||
if (explorer_strcmp(state->items[index].name, "Notepad.shortcut") == 0) {
|
if (explorer_strcmp(state->items[index].name, "Notepad.shortcut") == 0) {
|
||||||
target = &win_notepad;
|
target = &win_notepad;
|
||||||
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
|
} else if (explorer_strcmp(state->items[index].name, "Calculator.shortcut") == 0) {
|
||||||
target = &win_calculator;
|
process_create_elf("/bin/calculator.elf"); return;
|
||||||
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
|
} else if (explorer_strcmp(state->items[index].name, "Terminal.shortcut") == 0) {
|
||||||
target = &win_cmd; cmd_reset();
|
target = &win_cmd; cmd_reset();
|
||||||
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
|
} else if (explorer_strcmp(state->items[index].name, "Minesweeper.shortcut") == 0) {
|
||||||
|
|
@ -916,6 +918,8 @@ static void explorer_draw_file_icon(int x, int y, bool is_dir, uint32_t color, c
|
||||||
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
if (full_path[explorer_strlen(full_path) - 1] != '/') explorer_strcat(full_path, "/");
|
||||||
explorer_strcat(full_path, filename);
|
explorer_strcat(full_path, filename);
|
||||||
draw_image_icon(x + 5, y + 5, full_path);
|
draw_image_icon(x + 5, y + 5, full_path);
|
||||||
|
} else if (explorer_str_ends_with(filename, ".elf")) {
|
||||||
|
draw_elf_icon(x + 5, y + 5, "");
|
||||||
} else {
|
} else {
|
||||||
draw_document_icon(x + 5, y + 5, "");
|
draw_document_icon(x + 5, y + 5, "");
|
||||||
}
|
}
|
||||||
|
|
@ -1782,7 +1786,6 @@ static void explorer_handle_file_context_menu_click(Window *win, int x, int y) {
|
||||||
for (int i = 0; i < explorer_win_count; i++) if (explorer_wins[i]->z_index > max_z) max_z = explorer_wins[i]->z_index;
|
for (int i = 0; i < explorer_win_count; i++) if (explorer_wins[i]->z_index > max_z) max_z = explorer_wins[i]->z_index;
|
||||||
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
||||||
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
||||||
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
|
|
||||||
if (win_editor.z_index > max_z) max_z = win_editor.z_index;
|
if (win_editor.z_index > max_z) max_z = win_editor.z_index;
|
||||||
if (win_markdown.z_index > max_z) max_z = win_markdown.z_index;
|
if (win_markdown.z_index > max_z) max_z = win_markdown.z_index;
|
||||||
if (win_control_panel.z_index > max_z) max_z = win_control_panel.z_index;
|
if (win_control_panel.z_index > max_z) max_z = win_control_panel.z_index;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ extern Window win_explorer;
|
||||||
extern Window win_editor;
|
extern Window win_editor;
|
||||||
extern Window win_cmd;
|
extern Window win_cmd;
|
||||||
extern Window win_notepad;
|
extern Window win_notepad;
|
||||||
extern Window win_calculator;
|
|
||||||
extern Window win_markdown;
|
extern Window win_markdown;
|
||||||
|
|
||||||
#define EXPLORER_MAX_FILES 64
|
#define EXPLORER_MAX_FILES 64
|
||||||
|
|
|
||||||
|
|
@ -1301,9 +1301,17 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) {
|
||||||
|
|
||||||
handle->position = new_position;
|
handle->position = new_position;
|
||||||
|
|
||||||
// For RealFS, we might need to walk cluster chain to update handle->cluster
|
// Both RealFS and RAMFS must accurately re-walk their cluster chains
|
||||||
// if we seeked far.
|
if (handle->drive == 'A') {
|
||||||
if (handle->drive != 'A') {
|
handle->cluster = handle->start_cluster;
|
||||||
|
uint32_t pos = 0;
|
||||||
|
while (pos + FAT32_CLUSTER_SIZE <= handle->position) {
|
||||||
|
uint32_t next = fat_table[handle->cluster];
|
||||||
|
if (next >= 0xFFFFFFF8) break;
|
||||||
|
handle->cluster = next;
|
||||||
|
pos += FAT32_CLUSTER_SIZE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Re-walk to find current cluster
|
// Re-walk to find current cluster
|
||||||
int vol_idx = handle->drive - 'A';
|
int vol_idx = handle->drive - 'A';
|
||||||
FAT32_Volume *vol = &volumes[vol_idx];
|
FAT32_Volume *vol = &volumes[vol_idx];
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ void draw_char(int x, int y, char c, uint32_t color) {
|
||||||
if (uc > 127) return;
|
if (uc > 127) return;
|
||||||
|
|
||||||
// Fast rejection: if the character is entirely outside the clipping/dirty rect, skip it
|
// Fast rejection: if the character is entirely outside the clipping/dirty rect, skip it
|
||||||
if (g_clip_enabled) {
|
if (g_clip_enabled && !g_render_target) {
|
||||||
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w ||
|
if (x + 8 <= g_clip_x || x >= g_clip_x + g_clip_w ||
|
||||||
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) {
|
y + 8 <= g_clip_y || y >= g_clip_y + g_clip_h) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -460,3 +460,26 @@ void graphics_set_clipping(int x, int y, int w, int h) {
|
||||||
void graphics_clear_clipping(void) {
|
void graphics_clear_clipping(void) {
|
||||||
g_clip_enabled = false;
|
g_clip_enabled = false;
|
||||||
}
|
}
|
||||||
|
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h) {
|
||||||
|
if (!g_fb || !src) return;
|
||||||
|
int sw = get_screen_width();
|
||||||
|
int sh = get_screen_height();
|
||||||
|
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
int vy = dst_y + y;
|
||||||
|
if (vy < 0 || vy >= sh) continue;
|
||||||
|
|
||||||
|
for (int x = 0; x < w; x++) {
|
||||||
|
int vx = dst_x + x;
|
||||||
|
if (vx < 0 || vx >= sw) continue;
|
||||||
|
|
||||||
|
uint32_t pcol = src[y * w + x];
|
||||||
|
// Alpha blending support:
|
||||||
|
// If the alpha byte is 0, we treat it as transparent ONLY if the color is also 0.
|
||||||
|
// This handles common 0xRRGGBB as opaque.
|
||||||
|
if ((pcol & 0xFF000000) != 0 || (pcol & 0xFFFFFF) != 0) {
|
||||||
|
g_back_buffer[vy * sw + vx] = pcol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ void graphics_set_bg_color(uint32_t color);
|
||||||
void graphics_set_bg_pattern(const uint32_t *pattern); // 128x128 pattern
|
void graphics_set_bg_pattern(const uint32_t *pattern); // 128x128 pattern
|
||||||
void graphics_set_bg_image(uint32_t *pixels, int w, int h); // Full-screen wallpaper image
|
void graphics_set_bg_image(uint32_t *pixels, int w, int h); // Full-screen wallpaper image
|
||||||
void graphics_set_render_target(uint32_t *buffer, int w, int h);
|
void graphics_set_render_target(uint32_t *buffer, int w, int h);
|
||||||
|
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h);
|
||||||
|
|
||||||
|
|
||||||
void draw_boredos_logo(int x, int y, int scale);
|
void draw_boredos_logo(int x, int y, int scale);
|
||||||
|
|
|
||||||
23
src/kernel/gui_ipc.h
Normal file
23
src/kernel/gui_ipc.h
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef GUI_IPC_H
|
||||||
|
#define GUI_IPC_H
|
||||||
|
|
||||||
|
#define GUI_CMD_WINDOW_CREATE 1
|
||||||
|
#define GUI_CMD_DRAW_RECT 2
|
||||||
|
#define GUI_CMD_DRAW_STRING 3
|
||||||
|
#define GUI_CMD_MARK_DIRTY 4
|
||||||
|
#define GUI_CMD_GET_EVENT 5
|
||||||
|
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
|
||||||
|
|
||||||
|
#define GUI_EVENT_NONE 0
|
||||||
|
#define GUI_EVENT_PAINT 1
|
||||||
|
#define GUI_EVENT_CLICK 2
|
||||||
|
#define GUI_EVENT_RIGHT_CLICK 3
|
||||||
|
#define GUI_EVENT_CLOSE 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int type;
|
||||||
|
int arg1; // For click: x
|
||||||
|
int arg2; // For click: y
|
||||||
|
} gui_event_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -16,17 +16,43 @@ static void print_hex(uint64_t val) {
|
||||||
serial_write(buf);
|
serial_write(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void exception_handler_c(uint64_t vector, uint64_t err_code, uint64_t rip, uint64_t cr2) {
|
#include "process.h"
|
||||||
|
#include "cmd.h"
|
||||||
|
|
||||||
|
uint64_t exception_handler_c(registers_t *regs) {
|
||||||
|
uint64_t vector = regs->int_no;
|
||||||
|
uint64_t rip = regs->rip;
|
||||||
|
uint64_t cr2;
|
||||||
|
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
serial_write("\n*** EXCEPTION ***\nVector: ");
|
serial_write("\n*** EXCEPTION ***\nVector: ");
|
||||||
print_hex(vector);
|
print_hex(vector);
|
||||||
serial_write("\nError Code: ");
|
serial_write("\nError Code: ");
|
||||||
print_hex(err_code);
|
print_hex(regs->err_code);
|
||||||
serial_write("\nRIP: ");
|
serial_write("\nRIP: ");
|
||||||
print_hex(rip);
|
print_hex(rip);
|
||||||
serial_write("\nCR2: ");
|
serial_write("\nCR2: ");
|
||||||
print_hex(cr2);
|
print_hex(cr2);
|
||||||
serial_write("\nCPU HALTED.\n");
|
|
||||||
|
// Mirror to shell
|
||||||
|
if (cmd_get_cursor_col() != 0) cmd_write("\n");
|
||||||
|
cmd_write("*** EXCEPTION ***\nVector: "); cmd_write_hex(vector);
|
||||||
|
cmd_write("\nError Code: "); cmd_write_hex(regs->err_code);
|
||||||
|
cmd_write("\nRIP: "); cmd_write_hex(rip);
|
||||||
|
cmd_write("\nCR2: "); cmd_write_hex(cr2);
|
||||||
|
cmd_write("\n");
|
||||||
|
|
||||||
|
// Filter by CS selector to check if we are in user mode (RPL=3)
|
||||||
|
if ((regs->cs & 0x3) != 0) {
|
||||||
|
serial_write("\nUSER MODE EXCEPTION - Terminating process.\n");
|
||||||
|
cmd_write("USER MODE EXCEPTION - Terminating process.\n");
|
||||||
|
return process_terminate_current();
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_write("\nKERNEL PANIC - CPU HALTED.\n");
|
||||||
|
cmd_write("KERNEL PANIC - CPU HALTED.\n");
|
||||||
while(1) { asm volatile("cli; hlt"); }
|
while(1) { asm volatile("cli; hlt"); }
|
||||||
|
return (uint64_t)regs; // Unreachable but for completeness
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IDT_ENTRIES 256
|
#define IDT_ENTRIES 256
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,10 @@ send_eoi:
|
||||||
pop rax
|
pop rax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
%macro ISR_NOERRCODE 1
|
%macro ISR_NOERRCODE 2
|
||||||
|
isr%2_wrapper:
|
||||||
|
push 0 ; Dummy error code
|
||||||
|
push %2 ; Vector
|
||||||
push rax
|
push rax
|
||||||
push rbx
|
push rbx
|
||||||
push rcx
|
push rcx
|
||||||
|
|
@ -35,7 +38,7 @@ send_eoi:
|
||||||
push r14
|
push r14
|
||||||
push r15
|
push r15
|
||||||
|
|
||||||
; Pass current RSP as 1st argument
|
; Pass current RSP as 1st argument (registers_t*)
|
||||||
mov rdi, rsp
|
mov rdi, rsp
|
||||||
|
|
||||||
call %1
|
call %1
|
||||||
|
|
@ -58,17 +61,18 @@ send_eoi:
|
||||||
pop rcx
|
pop rcx
|
||||||
pop rbx
|
pop rbx
|
||||||
pop rax
|
pop rax
|
||||||
|
add rsp, 16 ; drop dummy vector and error code
|
||||||
iretq
|
iretq
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
isr0_wrapper:
|
isr0_wrapper:
|
||||||
ISR_NOERRCODE timer_handler
|
ISR_NOERRCODE timer_handler, 32
|
||||||
|
|
||||||
isr1_wrapper:
|
isr1_wrapper:
|
||||||
ISR_NOERRCODE keyboard_handler
|
ISR_NOERRCODE keyboard_handler, 33
|
||||||
|
|
||||||
isr12_wrapper:
|
isr12_wrapper:
|
||||||
ISR_NOERRCODE mouse_handler
|
ISR_NOERRCODE mouse_handler, 44
|
||||||
|
|
||||||
; Common exception macro
|
; Common exception macro
|
||||||
%macro EXCEPTION_ERRCODE 1
|
%macro EXCEPTION_ERRCODE 1
|
||||||
|
|
@ -101,16 +105,14 @@ exception_common:
|
||||||
push r14
|
push r14
|
||||||
push r15
|
push r15
|
||||||
|
|
||||||
; Call C handler: void exception_handler_c(uint64_t vector, uint64_t err_code, uint64_t rip, uint64_t cr2)
|
; Call C handler: uint64_t exception_handler_c(registers_t *regs)
|
||||||
; Stack right now: 15 registers (15*8=120 bytes), then vector (8), then err_code (8), then RIP (8), CS (8), RFLAGS (8), RSP (8), SS (8)
|
mov rdi, rsp
|
||||||
mov rdi, [rsp + 120] ; vector
|
|
||||||
mov rsi, [rsp + 128] ; err_code
|
|
||||||
mov rdx, [rsp + 136] ; RIP
|
|
||||||
mov rcx, cr2 ; CR2
|
|
||||||
|
|
||||||
call exception_handler_c
|
call exception_handler_c
|
||||||
|
|
||||||
; Restore (in case we want to return, but usually we halt)
|
; Switch stack if needed (for process termination)
|
||||||
|
mov rsp, rax
|
||||||
|
|
||||||
|
; Restore (in case we want to return, but usually we halt if kernel)
|
||||||
pop r15
|
pop r15
|
||||||
pop r14
|
pop r14
|
||||||
pop r13
|
pop r13
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "ps2.h"
|
#include "ps2.h"
|
||||||
#include "wm.h"
|
#include "wm.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
#include "fat32.h"
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "wallpaper.h"
|
#include "wallpaper.h"
|
||||||
|
|
@ -32,10 +33,17 @@ static volatile struct limine_memmap_request memmap_request = {
|
||||||
.revision = 0
|
.revision = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".requests")))
|
||||||
|
static volatile struct limine_module_request module_request = {
|
||||||
|
.id = LIMINE_MODULE_REQUEST,
|
||||||
|
.revision = 0
|
||||||
|
};
|
||||||
|
|
||||||
__attribute__((used, section(".requests_start")))
|
__attribute__((used, section(".requests_start")))
|
||||||
static volatile struct limine_request *const requests_start_marker[] = {
|
static volatile struct limine_request *const requests_start_marker[] = {
|
||||||
(struct limine_request *)&framebuffer_request,
|
(struct limine_request *)&framebuffer_request,
|
||||||
(struct limine_request *)&memmap_request,
|
(struct limine_request *)&memmap_request,
|
||||||
|
(struct limine_request *)&module_request,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -107,22 +115,7 @@ void kmain(void) {
|
||||||
serial_write("[DEBUG] idt_init OK\n");
|
serial_write("[DEBUG] idt_init OK\n");
|
||||||
|
|
||||||
process_init();
|
process_init();
|
||||||
int ENABLE_USER_TEST = 1; // Set to 1 to test User Mode ring 3 jump
|
|
||||||
if (ENABLE_USER_TEST) {
|
|
||||||
// Create an isolated PML4 table for this "process"
|
|
||||||
uint64_t user_pml4_phys = paging_create_user_pml4_phys();
|
|
||||||
serial_write("[DEBUG] user_pml4 created OK\n");
|
|
||||||
if (user_pml4_phys) {
|
|
||||||
|
|
||||||
// Debug verify we can allocate
|
|
||||||
void* code_page = kmalloc_aligned(4096, 4096);
|
|
||||||
if (code_page) {
|
|
||||||
extern void user_test_function(void);
|
|
||||||
process_create(user_test_function, true);
|
|
||||||
serial_write("[DEBUG] User Process 1 Created.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
||||||
|
|
||||||
|
|
@ -132,6 +125,7 @@ void kmain(void) {
|
||||||
for (uint64_t i = 0; i < memmap_request.response->entry_count; i++) {
|
for (uint64_t i = 0; i < memmap_request.response->entry_count; i++) {
|
||||||
struct limine_memmap_entry *entry = memmap_request.response->entries[i];
|
struct limine_memmap_entry *entry = memmap_request.response->entries[i];
|
||||||
|
|
||||||
|
|
||||||
// Count usable memory regions
|
// Count usable memory regions
|
||||||
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
||||||
total_usable_memory += entry->length;
|
total_usable_memory += entry->length;
|
||||||
|
|
@ -149,6 +143,30 @@ void kmain(void) {
|
||||||
|
|
||||||
memory_manager_init_with_size(pool_size);
|
memory_manager_init_with_size(pool_size);
|
||||||
|
|
||||||
|
// Initialize FAT32 RAMFS and mount Limine modules
|
||||||
|
fat32_init();
|
||||||
|
if (module_request.response != NULL) {
|
||||||
|
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
|
||||||
|
struct limine_file *mod = module_request.response->modules[i];
|
||||||
|
|
||||||
|
// mod->path typically starts with a '/' from Limine
|
||||||
|
FAT32_FileHandle *fh = fat32_open(mod->path, "w");
|
||||||
|
if (fh && fh->valid) {
|
||||||
|
fat32_write(fh, mod->address, mod->size);
|
||||||
|
fat32_close(fh);
|
||||||
|
serial_write("[DEBUG] Limine Module loaded into RAMFS: ");
|
||||||
|
serial_write(mod->path);
|
||||||
|
serial_write("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ENABLE_USER_TEST = 1; // Set to 1 to test User Mode ring 3 jump
|
||||||
|
#ifdef ENABLE_USER_TEST
|
||||||
|
process_init();
|
||||||
|
// The desktop is PID 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// 3. PS/2 Init (Mouse/Keyboard)
|
// 3. PS/2 Init (Mouse/Keyboard)
|
||||||
asm("cli");
|
asm("cli");
|
||||||
ps2_init();
|
ps2_init();
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,21 @@ static bool initialized = false;
|
||||||
// --- Helper Functions ---
|
// --- Helper Functions ---
|
||||||
|
|
||||||
// Simple memset for internal use
|
// Simple memset for internal use
|
||||||
static void mem_memset(void *dest, int val, size_t len) {
|
void mem_memset(void *dest, int val, size_t len) {
|
||||||
uint8_t *ptr = (uint8_t *)dest;
|
uint8_t *ptr = (uint8_t *)dest;
|
||||||
while (len-- > 0) {
|
while (len-- > 0) {
|
||||||
*ptr++ = (uint8_t)val;
|
*ptr++ = (uint8_t)val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mem_memcpy(void *dest, const void *src, size_t len) {
|
||||||
|
uint8_t *d = (uint8_t *)dest;
|
||||||
|
const uint8_t *s = (const uint8_t *)src;
|
||||||
|
while (len-- > 0) {
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Simple memmove
|
// Simple memmove
|
||||||
static void mem_memmove(void *dest, const void *src, size_t len) {
|
static void mem_memmove(void *dest, const void *src, size_t len) {
|
||||||
uint8_t *d = (uint8_t *)dest;
|
uint8_t *d = (uint8_t *)dest;
|
||||||
|
|
|
||||||
|
|
@ -55,4 +55,7 @@ size_t memory_get_peak_usage(void);
|
||||||
void memory_reset_peak(void);
|
void memory_reset_peak(void);
|
||||||
bool memory_is_valid_ptr(void *ptr);
|
bool memory_is_valid_ptr(void *ptr);
|
||||||
|
|
||||||
|
void mem_memset(void *dest, int val, size_t len);
|
||||||
|
void mem_memcpy(void *dest, const void *src, size_t len);
|
||||||
|
|
||||||
#endif // MEMORY_MANAGER_H
|
#endif // MEMORY_MANAGER_H
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
|
#include "elf.h"
|
||||||
|
#include "wm.h"
|
||||||
|
|
||||||
extern void cmd_write(const char *str);
|
extern void cmd_write(const char *str);
|
||||||
extern void serial_write(const char *str);
|
extern void serial_write(const char *str);
|
||||||
|
|
@ -48,7 +50,7 @@ void process_create(void* entry_point, bool is_user) {
|
||||||
|
|
||||||
// 2. Allocate aligned stack
|
// 2. Allocate aligned stack
|
||||||
void* stack = kmalloc_aligned(4096, 4096);
|
void* stack = kmalloc_aligned(4096, 4096);
|
||||||
void* kernel_stack = kmalloc_aligned(4096, 4096); // Needed for when user interrupts to Ring 0
|
void* kernel_stack = kmalloc_aligned(16384, 16384); // Needed for when user interrupts to Ring 0
|
||||||
|
|
||||||
if (is_user) {
|
if (is_user) {
|
||||||
// Map user stack to 0x800000
|
// Map user stack to 0x800000
|
||||||
|
|
@ -62,18 +64,20 @@ void process_create(void* entry_point, bool is_user) {
|
||||||
|
|
||||||
// Build initial stack frame for iretq
|
// Build initial stack frame for iretq
|
||||||
// Stack grows down, start at top
|
// Stack grows down, start at top
|
||||||
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 4096);
|
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 16384);
|
||||||
|
|
||||||
*(--stack_ptr) = 0x1B; // SS (User Data)
|
*(--stack_ptr) = 0x1B; // SS (User Data)
|
||||||
*(--stack_ptr) = 0x800000 + 4096; // RSP
|
*(--stack_ptr) = 0x800000 + 4096; // RSP
|
||||||
*(--stack_ptr) = 0x202; // RFLAGS (IF=1)
|
*(--stack_ptr) = 0x202; // RFLAGS (IF=1)
|
||||||
*(--stack_ptr) = 0x23; // CS (User Code)
|
*(--stack_ptr) = 0x23; // CS (User Code)
|
||||||
*(--stack_ptr) = 0x400000; // RIP
|
*(--stack_ptr) = 0x400000; // RIP
|
||||||
|
*(--stack_ptr) = 0; // int_no
|
||||||
|
*(--stack_ptr) = 0; // err_code
|
||||||
|
|
||||||
// Push 15 zeros for general purpose registers (r15 -> rax)
|
// Push 15 zeros for general purpose registers (r15 -> rax)
|
||||||
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
||||||
|
|
||||||
new_proc->kernel_stack = (uint64_t)kernel_stack + 4096;
|
new_proc->kernel_stack = (uint64_t)kernel_stack + 16384;
|
||||||
new_proc->rsp = (uint64_t)stack_ptr;
|
new_proc->rsp = (uint64_t)stack_ptr;
|
||||||
} else {
|
} else {
|
||||||
// Kernel thread
|
// Kernel thread
|
||||||
|
|
@ -84,6 +88,8 @@ void process_create(void* entry_point, bool is_user) {
|
||||||
*(--stack_ptr) = 0x202; // RFLAGS
|
*(--stack_ptr) = 0x202; // RFLAGS
|
||||||
*(--stack_ptr) = 0x08; // CS (Kernel Code)
|
*(--stack_ptr) = 0x08; // CS (Kernel Code)
|
||||||
*(--stack_ptr) = (uint64_t)entry_point; // RIP
|
*(--stack_ptr) = (uint64_t)entry_point; // RIP
|
||||||
|
*(--stack_ptr) = 0; // int_no
|
||||||
|
*(--stack_ptr) = 0; // err_code
|
||||||
|
|
||||||
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
||||||
|
|
||||||
|
|
@ -96,6 +102,63 @@ void process_create(void* entry_point, bool is_user) {
|
||||||
current_process->next = new_proc;
|
current_process->next = new_proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void process_create_elf(const char* filepath) {
|
||||||
|
if (process_count >= MAX_PROCESSES) return;
|
||||||
|
|
||||||
|
process_t *new_proc = &processes[process_count];
|
||||||
|
new_proc->pid = next_pid++;
|
||||||
|
new_proc->is_user = true;
|
||||||
|
|
||||||
|
// 1. Setup Page Table
|
||||||
|
new_proc->pml4_phys = paging_create_user_pml4_phys();
|
||||||
|
if (!new_proc->pml4_phys) return;
|
||||||
|
|
||||||
|
// 2. Load ELF executable
|
||||||
|
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys);
|
||||||
|
if (entry_point == 0) {
|
||||||
|
serial_write("[PROCESS] Failed to load ELF: ");
|
||||||
|
serial_write(filepath);
|
||||||
|
serial_write("\n");
|
||||||
|
// We technically leak the page table here, but let's ignore cleanup for now
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Allocate generic User stack and Kernel stack for interrupts
|
||||||
|
void* stack = kmalloc_aligned(4096, 4096);
|
||||||
|
void* kernel_stack = kmalloc_aligned(16384, 16384);
|
||||||
|
|
||||||
|
// Map User stack to 0x800000 -> Note: ELFs might overwrite this if they load there!
|
||||||
|
// But our ELF loader defaults 0x400000 for standard code.
|
||||||
|
paging_map_page(new_proc->pml4_phys, 0x800000, v2p((uint64_t)stack), PT_PRESENT | PT_RW | PT_USER);
|
||||||
|
|
||||||
|
// 4. Build Stack Frame
|
||||||
|
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 16384);
|
||||||
|
*(--stack_ptr) = 0x1B; // SS (User Mode Data)
|
||||||
|
*(--stack_ptr) = 0x800000 + 4096; // RSP
|
||||||
|
*(--stack_ptr) = 0x202; // RFLAGS (Interrupts Enabled)
|
||||||
|
*(--stack_ptr) = 0x23; // CS (User Mode Code)
|
||||||
|
*(--stack_ptr) = entry_point; // RIP
|
||||||
|
*(--stack_ptr) = 0; // int_no
|
||||||
|
*(--stack_ptr) = 0; // err_code
|
||||||
|
|
||||||
|
// 15 General purpose registers
|
||||||
|
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
||||||
|
|
||||||
|
new_proc->kernel_stack = (uint64_t)kernel_stack + 16384;
|
||||||
|
new_proc->rsp = (uint64_t)stack_ptr;
|
||||||
|
|
||||||
|
// We only increment process_count after success
|
||||||
|
process_count++;
|
||||||
|
|
||||||
|
// Add to linked list
|
||||||
|
new_proc->next = current_process->next;
|
||||||
|
current_process->next = new_proc;
|
||||||
|
|
||||||
|
serial_write("[PROCESS] Spawned ELF Executable: ");
|
||||||
|
serial_write(filepath);
|
||||||
|
serial_write("\n");
|
||||||
|
}
|
||||||
|
|
||||||
process_t* process_get_current(void) {
|
process_t* process_get_current(void) {
|
||||||
return current_process;
|
return current_process;
|
||||||
}
|
}
|
||||||
|
|
@ -112,9 +175,11 @@ uint64_t process_schedule(uint64_t current_rsp) {
|
||||||
// Switch process
|
// Switch process
|
||||||
current_process = current_process->next;
|
current_process = current_process->next;
|
||||||
|
|
||||||
// Update Kernel Stack for User Mode interrupts
|
// Update Kernel Stack for User Mode interrupts and System Calls
|
||||||
if (current_process->is_user && current_process->kernel_stack) {
|
if (current_process->is_user && current_process->kernel_stack) {
|
||||||
tss_set_stack(current_process->kernel_stack);
|
tss_set_stack(current_process->kernel_stack);
|
||||||
|
extern uint64_t kernel_syscall_stack;
|
||||||
|
kernel_syscall_stack = current_process->kernel_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch page table
|
// Switch page table
|
||||||
|
|
@ -123,3 +188,58 @@ uint64_t process_schedule(uint64_t current_rsp) {
|
||||||
return current_process->rsp;
|
return current_process->rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t process_terminate_current(void) {
|
||||||
|
if (!current_process) return 0;
|
||||||
|
|
||||||
|
// 1. Cleanup side effects
|
||||||
|
if (current_process->ui_window) {
|
||||||
|
wm_remove_window((Window *)current_process->ui_window);
|
||||||
|
current_process->ui_window = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void cmd_process_finished(void);
|
||||||
|
cmd_process_finished();
|
||||||
|
|
||||||
|
// 2. Find previous process in circular list
|
||||||
|
process_t *prev = current_process;
|
||||||
|
while (prev->next != current_process) {
|
||||||
|
prev = prev->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Remove current from list
|
||||||
|
process_t *to_delete = current_process;
|
||||||
|
|
||||||
|
if (prev == current_process) {
|
||||||
|
// Only one process (should be kernel), cannot terminate.
|
||||||
|
return to_delete->rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev->next = to_delete->next;
|
||||||
|
current_process = to_delete->next;
|
||||||
|
|
||||||
|
// Mark slot as freeish (simple version)
|
||||||
|
to_delete->pid = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
// 4. Load context for the NEXT process
|
||||||
|
if (current_process->is_user && current_process->kernel_stack) {
|
||||||
|
tss_set_stack(current_process->kernel_stack);
|
||||||
|
extern uint64_t kernel_syscall_stack;
|
||||||
|
kernel_syscall_stack = current_process->kernel_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
paging_switch_directory(current_process->pml4_phys);
|
||||||
|
|
||||||
|
return current_process->rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_push_gui_event(process_t *proc, gui_event_t *ev) {
|
||||||
|
if (!proc) return;
|
||||||
|
int next_tail = (proc->gui_event_tail + 1) % MAX_GUI_EVENTS;
|
||||||
|
// Drop event if queue is full
|
||||||
|
if (next_tail == proc->gui_event_head) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proc->gui_events[proc->gui_event_tail] = *ev;
|
||||||
|
proc->gui_event_tail = next_tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "gui_ipc.h"
|
||||||
|
|
||||||
|
#define MAX_GUI_EVENTS 32
|
||||||
|
|
||||||
// Registers saved on the stack by interrupts/exceptions
|
// Registers saved on the stack by interrupts/exceptions
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -18,13 +21,23 @@ typedef struct process {
|
||||||
uint64_t pml4_phys; // Physical address of the page table
|
uint64_t pml4_phys; // Physical address of the page table
|
||||||
uint64_t kernel_stack; // Ring 0 stack pointer for user mode switches
|
uint64_t kernel_stack; // Ring 0 stack pointer for user mode switches
|
||||||
bool is_user;
|
bool is_user;
|
||||||
|
|
||||||
|
gui_event_t gui_events[MAX_GUI_EVENTS];
|
||||||
|
int gui_event_head;
|
||||||
|
int gui_event_tail;
|
||||||
|
void *ui_window; // Pointer to the active Window
|
||||||
|
|
||||||
struct process *next;
|
struct process *next;
|
||||||
} process_t;
|
} process_t;
|
||||||
|
|
||||||
void process_init(void);
|
void process_init(void);
|
||||||
void process_create(void* entry_point, bool is_user);
|
void process_create(void* entry_point, bool is_user);
|
||||||
|
void process_create_elf(const char* filepath);
|
||||||
process_t* process_get_current(void);
|
process_t* process_get_current(void);
|
||||||
uint64_t process_schedule(uint64_t current_rsp);
|
uint64_t process_schedule(uint64_t current_rsp);
|
||||||
|
uint64_t process_terminate_current(void);
|
||||||
|
|
||||||
|
void process_push_gui_event(process_t *proc, gui_event_t *ev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ extern void serial_print(const char *s);
|
||||||
extern void serial_print_hex(uint64_t n);
|
extern void serial_print_hex(uint64_t n);
|
||||||
|
|
||||||
// --- Timer Handler ---
|
// --- Timer Handler ---
|
||||||
uint64_t timer_handler(uint64_t rsp) {
|
uint64_t timer_handler(registers_t *regs) {
|
||||||
wm_timer_tick();
|
wm_timer_tick();
|
||||||
network_process_frames();
|
network_process_frames();
|
||||||
|
|
||||||
extern uint64_t process_schedule(uint64_t current_rsp);
|
extern uint64_t process_schedule(uint64_t current_rsp);
|
||||||
|
|
||||||
outb(0x20, 0x20); // EOI to Master PIC
|
outb(0x20, 0x20); // EOI to Master PIC
|
||||||
rsp = process_schedule(rsp);
|
uint64_t rsp = process_schedule((uint64_t)regs);
|
||||||
|
|
||||||
return rsp;
|
return rsp;
|
||||||
}
|
}
|
||||||
|
|
@ -41,13 +41,13 @@ static char scancode_map_shift[128] = {
|
||||||
0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t keyboard_handler(uint64_t rsp) {
|
uint64_t keyboard_handler(registers_t *regs) {
|
||||||
uint8_t scancode = inb(0x60);
|
uint8_t scancode = inb(0x60);
|
||||||
|
|
||||||
if (scancode == 0xE0) {
|
if (scancode == 0xE0) {
|
||||||
extended_scancode = true;
|
extended_scancode = true;
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
return rsp;
|
return (uint64_t)regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
|
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
|
||||||
|
|
@ -77,7 +77,7 @@ uint64_t keyboard_handler(uint64_t rsp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
outb(0x20, 0x20); // EOI
|
outb(0x20, 0x20); // EOI
|
||||||
return rsp;
|
return (uint64_t)regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Mouse ---
|
// --- Mouse ---
|
||||||
|
|
@ -135,12 +135,12 @@ void mouse_init(void) {
|
||||||
mouse_read();
|
mouse_read();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t mouse_handler(uint64_t rsp) {
|
uint64_t mouse_handler(registers_t *regs) {
|
||||||
uint8_t status = inb(0x64);
|
uint8_t status = inb(0x64);
|
||||||
if (!(status & 0x20)) {
|
if (!(status & 0x20)) {
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
outb(0xA0, 0x20);
|
outb(0xA0, 0x20);
|
||||||
return rsp; // Return rsp here as well
|
return (uint64_t)regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t b = inb(0x60);
|
uint8_t b = inb(0x60);
|
||||||
|
|
@ -169,7 +169,7 @@ uint64_t mouse_handler(uint64_t rsp) {
|
||||||
|
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
outb(0xA0, 0x20); // Slave EOI
|
outb(0xA0, 0x20); // Slave EOI
|
||||||
return rsp;
|
return (uint64_t)regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ps2_init(void) {
|
void ps2_init(void) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void ps2_init(void);
|
void ps2_init(void);
|
||||||
uint64_t timer_handler(uint64_t rsp);
|
#include "process.h"
|
||||||
uint64_t keyboard_handler(uint64_t rsp);
|
|
||||||
uint64_t mouse_handler(uint64_t rsp);
|
uint64_t timer_handler(registers_t *regs);
|
||||||
|
uint64_t keyboard_handler(registers_t *regs);
|
||||||
|
uint64_t mouse_handler(registers_t *regs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
|
#include "gui_ipc.h"
|
||||||
|
#include "process.h"
|
||||||
|
#include "wm.h"
|
||||||
|
|
||||||
// Read MSR
|
// Read MSR
|
||||||
static inline uint64_t rdmsr(uint32_t msr) {
|
static inline uint64_t rdmsr(uint32_t msr) {
|
||||||
|
|
@ -43,13 +46,232 @@ void syscall_init(void) {
|
||||||
wrmsr(MSR_FMASK, 0x200);
|
wrmsr(MSR_FMASK, 0x200);
|
||||||
}
|
}
|
||||||
|
|
||||||
void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
|
static void user_window_close(Window *win) {
|
||||||
|
process_t *proc = (process_t *)win->data;
|
||||||
|
if (!proc) return;
|
||||||
|
gui_event_t ev = { .type = GUI_EVENT_CLOSE };
|
||||||
|
process_push_gui_event(proc, &ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void user_window_paint(Window *win) {
|
||||||
|
process_t *proc = (process_t *)win->data;
|
||||||
|
if (!proc) return;
|
||||||
|
gui_event_t ev = { .type = GUI_EVENT_PAINT };
|
||||||
|
process_push_gui_event(proc, &ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void user_window_click(Window *win, int x, int y) {
|
||||||
|
process_t *proc = (process_t *)win->data;
|
||||||
|
if (!proc) return;
|
||||||
|
gui_event_t ev = { .type = GUI_EVENT_CLICK, .arg1 = x, .arg2 = y };
|
||||||
|
process_push_gui_event(proc, &ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
|
||||||
extern void cmd_write(const char *str);
|
extern void cmd_write(const char *str);
|
||||||
extern void serial_write(const char *str);
|
extern void serial_write(const char *str);
|
||||||
|
extern void cmd_process_finished(void);
|
||||||
|
|
||||||
if (syscall_num == 1) { // SYS_WRITE
|
if (syscall_num == 1) { // SYS_WRITE
|
||||||
// arg2 is the buffer based on our user_test logic
|
// arg2 is the buffer based on our user_test logic
|
||||||
cmd_write((const char*)arg2);
|
cmd_write((const char*)arg2);
|
||||||
serial_write((const char*)arg2);
|
serial_write((const char*)arg2);
|
||||||
|
} else if (syscall_num == 0) { // SYS_EXIT
|
||||||
|
process_t *proc = process_get_current();
|
||||||
|
if (proc && proc->ui_window) {
|
||||||
|
wm_remove_window((Window *)proc->ui_window);
|
||||||
|
proc->ui_window = NULL;
|
||||||
|
}
|
||||||
|
cmd_process_finished();
|
||||||
|
// The actual process termination and scheduling will be handled later
|
||||||
|
// For now this just releases the CMD prompt lock.
|
||||||
|
// We will eventually need to mark the process_t as DEAD here.
|
||||||
|
} else if (syscall_num == 3) { // SYS_GUI
|
||||||
|
int cmd = (int)arg1;
|
||||||
|
process_t *proc = process_get_current();
|
||||||
|
|
||||||
|
if (cmd == GUI_CMD_WINDOW_CREATE) {
|
||||||
|
const char *title = (const char *)arg2;
|
||||||
|
uint64_t *params = (uint64_t *)arg3;
|
||||||
|
if (!params) return 0;
|
||||||
|
|
||||||
|
Window *win = kmalloc(sizeof(Window));
|
||||||
|
if (!win) return 0;
|
||||||
|
|
||||||
|
// Copy title from user space to kernel space so wm.c can access it safely
|
||||||
|
int title_len = 0;
|
||||||
|
while (title[title_len] && title_len < 255) title_len++;
|
||||||
|
char *kernel_title = kmalloc(title_len + 1);
|
||||||
|
if (kernel_title) {
|
||||||
|
for (int i = 0; i < title_len; i++) {
|
||||||
|
kernel_title[i] = title[i];
|
||||||
|
}
|
||||||
|
kernel_title[title_len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic initialization
|
||||||
|
win->title = kernel_title ? kernel_title : "Unknown";
|
||||||
|
win->x = (int)params[0];
|
||||||
|
win->y = (int)params[1];
|
||||||
|
win->w = (int)params[2];
|
||||||
|
win->h = (int)params[3];
|
||||||
|
|
||||||
|
// Sanity checks for dimensions
|
||||||
|
if (win->w <= 0 || win->w > 4096) win->w = 400;
|
||||||
|
if (win->h <= 0 || win->h > 4096) win->h = 400;
|
||||||
|
|
||||||
|
win->visible = true;
|
||||||
|
win->focused = true;
|
||||||
|
win->z_index = 0;
|
||||||
|
win->buf_len = 0;
|
||||||
|
win->buffer[0] = 0;
|
||||||
|
win->data = proc;
|
||||||
|
|
||||||
|
// Safe allocation
|
||||||
|
size_t pixel_size = (size_t)win->w * win->h * 4;
|
||||||
|
win->pixels = kmalloc(pixel_size);
|
||||||
|
win->comp_pixels = kmalloc(pixel_size);
|
||||||
|
if (win->pixels) {
|
||||||
|
extern void mem_memset(void *dest, int val, size_t len);
|
||||||
|
mem_memset(win->pixels, 0, pixel_size);
|
||||||
|
}
|
||||||
|
if (win->comp_pixels) {
|
||||||
|
extern void mem_memset(void *dest, int val, size_t len);
|
||||||
|
mem_memset(win->comp_pixels, 0, pixel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
win->paint = user_window_paint;
|
||||||
|
win->handle_click = user_window_click;
|
||||||
|
win->handle_close = user_window_close;
|
||||||
|
win->handle_key = NULL;
|
||||||
|
win->handle_right_click = NULL;
|
||||||
|
|
||||||
|
proc->ui_window = win;
|
||||||
|
wm_add_window(win);
|
||||||
|
|
||||||
|
return (uint64_t)win;
|
||||||
|
} else if (cmd == GUI_CMD_DRAW_RECT) {
|
||||||
|
Window *win = (Window *)arg2;
|
||||||
|
uint64_t *p = (uint64_t *)arg3;
|
||||||
|
uint32_t color = (uint32_t)arg4;
|
||||||
|
if (win && p) {
|
||||||
|
extern void draw_rect(int x, int y, int w, int h, uint32_t color);
|
||||||
|
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
|
||||||
|
|
||||||
|
uint64_t rflags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
||||||
|
|
||||||
|
if (win->pixels) {
|
||||||
|
// Strict user-to-window relative clamping
|
||||||
|
int rx = (int)p[0]; int ry = (int)p[1];
|
||||||
|
int rw = (int)p[2]; int rh = (int)p[3];
|
||||||
|
if (rx < 0) { rw += rx; rx = 0; }
|
||||||
|
if (ry < 0) { rh += ry; ry = 0; }
|
||||||
|
if (rx + rw > win->w) rw = win->w - rx;
|
||||||
|
if (ry + rh > win->h) rh = win->h - ry;
|
||||||
|
|
||||||
|
if (rw > 0 && rh > 0) {
|
||||||
|
graphics_set_render_target(win->pixels, win->w, win->h);
|
||||||
|
draw_rect(rx, ry, rw, rh, color);
|
||||||
|
graphics_set_render_target(NULL, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
draw_rect(win->x + p[0], win->y + p[1], p[2], p[3], color);
|
||||||
|
}
|
||||||
|
|
||||||
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
|
}
|
||||||
|
} else if (cmd == GUI_CMD_DRAW_ROUNDED_RECT_FILLED) {
|
||||||
|
Window *win = (Window *)arg2;
|
||||||
|
uint64_t *p = (uint64_t *)arg3;
|
||||||
|
uint32_t color = (uint32_t)arg4;
|
||||||
|
if (win && p) {
|
||||||
|
extern void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color);
|
||||||
|
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
|
||||||
|
|
||||||
|
uint64_t rflags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
||||||
|
|
||||||
|
if (win->pixels) {
|
||||||
|
int rx = (int)p[0]; int ry = (int)p[1];
|
||||||
|
int rw = (int)p[2]; int rh = (int)p[3];
|
||||||
|
int rr = (int)p[4];
|
||||||
|
if (rx < 0) { rw += rx; rx = 0; }
|
||||||
|
if (ry < 0) { rh += ry; ry = 0; }
|
||||||
|
if (rx + rw > win->w) rw = win->w - rx;
|
||||||
|
if (ry + rh > win->h) rh = win->h - ry;
|
||||||
|
|
||||||
|
if (rw > 0 && rh > 0) {
|
||||||
|
graphics_set_render_target(win->pixels, win->w, win->h);
|
||||||
|
draw_rounded_rect_filled(rx, ry, rw, rh, rr, color);
|
||||||
|
graphics_set_render_target(NULL, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
draw_rounded_rect_filled(win->x + p[0], win->y + p[1], p[2], p[3], p[4], color);
|
||||||
|
}
|
||||||
|
|
||||||
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
|
}
|
||||||
|
} else if (cmd == GUI_CMD_DRAW_STRING) {
|
||||||
|
Window *win = (Window *)arg2;
|
||||||
|
uint64_t coords = arg3;
|
||||||
|
int ux = coords & 0xFFFFFFFF;
|
||||||
|
int uy = coords >> 32;
|
||||||
|
const char *user_str = (const char *)arg4;
|
||||||
|
uint32_t color = (uint32_t)arg5;
|
||||||
|
if (win && user_str) {
|
||||||
|
extern void draw_string(int x, int y, const char *str, uint32_t color);
|
||||||
|
extern void graphics_set_render_target(uint32_t *buffer, int w, int h);
|
||||||
|
|
||||||
|
// Copy string safely to kernel stack buffer
|
||||||
|
char kernel_str[256];
|
||||||
|
int i = 0;
|
||||||
|
while (i < 255 && user_str[i]) {
|
||||||
|
kernel_str[i] = user_str[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
kernel_str[i] = 0;
|
||||||
|
|
||||||
|
uint64_t rflags;
|
||||||
|
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
||||||
|
|
||||||
|
if (win->pixels) {
|
||||||
|
// String clipping is handled by draw_char -> put_pixel,
|
||||||
|
// but we ensure coordinate sanity here
|
||||||
|
if (ux >= -100 && ux < win->w && uy >= -100 && uy < win->h) {
|
||||||
|
graphics_set_render_target(win->pixels, win->w, win->h);
|
||||||
|
draw_string(ux, uy, kernel_str, color);
|
||||||
|
graphics_set_render_target(NULL, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
draw_string(win->x + ux, win->y + uy, kernel_str, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
|
}
|
||||||
|
} else if (cmd == GUI_CMD_MARK_DIRTY) {
|
||||||
|
Window *win = (Window *)arg2;
|
||||||
|
uint64_t *p = (uint64_t *)arg3;
|
||||||
|
if (win && p) {
|
||||||
|
// Dual-buffer commit: copy pixels to comp_pixels
|
||||||
|
if (win->pixels && win->comp_pixels) {
|
||||||
|
extern void mem_memcpy(void *dest, const void *src, size_t len);
|
||||||
|
mem_memcpy(win->comp_pixels, win->pixels, (size_t)win->w * win->h * 4);
|
||||||
|
}
|
||||||
|
wm_mark_dirty(win->x + p[0], win->y + p[1], p[2], p[3]);
|
||||||
|
}
|
||||||
|
} else if (cmd == GUI_CMD_GET_EVENT) {
|
||||||
|
Window *win = (Window *)arg2;
|
||||||
|
gui_event_t *ev_out = (gui_event_t *)arg3;
|
||||||
|
if (!win || !ev_out) return 0;
|
||||||
|
if (proc->gui_event_head != proc->gui_event_tail) {
|
||||||
|
*ev_out = proc->gui_events[proc->gui_event_head];
|
||||||
|
proc->gui_event_head = (proc->gui_event_head + 1) % MAX_GUI_EVENTS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,6 @@
|
||||||
#define SYS_EXIT 60
|
#define SYS_EXIT 60
|
||||||
|
|
||||||
void syscall_init(void);
|
void syscall_init(void);
|
||||||
void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
|
uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
|
||||||
|
|
||||||
#endif // SYSCALL_H
|
#endif // SYSCALL_H
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,14 @@ section .text
|
||||||
; R9 = arg5
|
; R9 = arg5
|
||||||
|
|
||||||
syscall_entry:
|
syscall_entry:
|
||||||
; We arrived here from Ring 3 via `syscall`.
|
; 1. Switch to Kernel Stack
|
||||||
; RAX = syscall_num
|
; Use scratch temporarily to pivot (Risk: Task switch here is rare but possible)
|
||||||
; RDI, RSI, RDX, R10, R8, R9 = args
|
|
||||||
; RCX = User RIP
|
|
||||||
; R11 = User RFLAGS
|
|
||||||
; Current RSP = User RSP
|
|
||||||
|
|
||||||
; 1. Save User RSP
|
|
||||||
mov [rel user_rsp_scratch], rsp
|
mov [rel user_rsp_scratch], rsp
|
||||||
|
|
||||||
; 2. Switch to Kernel Stack
|
|
||||||
mov rsp, [rel kernel_syscall_stack]
|
mov rsp, [rel kernel_syscall_stack]
|
||||||
|
|
||||||
|
; 2. Save User RSP on per-process kernel stack
|
||||||
|
push qword [rel user_rsp_scratch]
|
||||||
|
|
||||||
; 3. Save preserved registers (System V ABI)
|
; 3. Save preserved registers (System V ABI)
|
||||||
push rbx
|
push rbx
|
||||||
push rbp
|
push rbp
|
||||||
|
|
@ -33,32 +28,18 @@ syscall_entry:
|
||||||
push r14
|
push r14
|
||||||
push r15
|
push r15
|
||||||
|
|
||||||
; We also need to save RCX (RIP) and R11 (RFLAGS) because C functions might clobber them
|
; 4. Save RCX (RIP) and R11 (RFLAGS)
|
||||||
push rcx
|
push rcx
|
||||||
push r11
|
push r11
|
||||||
|
|
||||||
; Syscall convention: argument 4 is passed in R10, but C expects it in RCX
|
|
||||||
mov rcx, r10
|
|
||||||
|
|
||||||
; The syscall number is in RAX, let's put it in RDI (arg 0 for C)
|
|
||||||
; But wait, the ABI expects arg1 in RDI!
|
|
||||||
; Let's change our C handler signature or adapt here.
|
|
||||||
; C handler: void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, ...)
|
|
||||||
; So:
|
|
||||||
; syscall_num -> RDI
|
|
||||||
; arg1 (was RDI) -> RSI
|
|
||||||
; arg2 (was RSI) -> RDX
|
|
||||||
; arg3 (was RDX) -> RCX
|
|
||||||
; arg4 (was R10) -> R8
|
|
||||||
; arg5 (was R8) -> R9
|
|
||||||
; arg6 (was R9) -> stack (if needed, but we have 6 regs)
|
|
||||||
|
|
||||||
; This shuffling is messy. Let's just push everything and call a struct-based handler,
|
|
||||||
; or carefully shuffle.
|
|
||||||
; For now, let's just make sure RAX goes to RDI, RDI to RSI, RSI to RDX, RDX to RCX, R10 to R8.
|
|
||||||
|
|
||||||
; Shuffling for SYS V C ABI:
|
; Shuffling for SYS V C ABI:
|
||||||
; R9 is arg6 -> no room in registers, need to push to stack (but our handler takes 6 args total)
|
; arg5: R9 (remains R9 as 6th arg in C)
|
||||||
|
; arg4: R8 (was R9)
|
||||||
|
; arg3: RCX (was R10)
|
||||||
|
; arg2: RDX (was RSI)
|
||||||
|
; arg1: RSI (was RDI)
|
||||||
|
; num: RDI (was RAX)
|
||||||
|
|
||||||
mov r9, r8 ; arg5
|
mov r9, r8 ; arg5
|
||||||
mov r8, r10 ; arg4
|
mov r8, r10 ; arg4
|
||||||
mov rcx, rdx ; arg3
|
mov rcx, rdx ; arg3
|
||||||
|
|
@ -66,14 +47,14 @@ syscall_entry:
|
||||||
mov rsi, rdi ; arg1
|
mov rsi, rdi ; arg1
|
||||||
mov rdi, rax ; syscall_num
|
mov rdi, rax ; syscall_num
|
||||||
|
|
||||||
; 4. Call C handler
|
; 5. Call C handler
|
||||||
call syscall_handler_c
|
call syscall_handler_c
|
||||||
|
|
||||||
; 5. Restore RCX and R11
|
; 6. Restore RCX and R11
|
||||||
pop r11
|
pop r11
|
||||||
pop rcx
|
pop rcx
|
||||||
|
|
||||||
; 6. Restore preserved registers
|
; 7. Restore preserved registers
|
||||||
pop r15
|
pop r15
|
||||||
pop r14
|
pop r14
|
||||||
pop r13
|
pop r13
|
||||||
|
|
@ -81,14 +62,11 @@ syscall_entry:
|
||||||
pop rbp
|
pop rbp
|
||||||
pop rbx
|
pop rbx
|
||||||
|
|
||||||
; 7. Restore User RSP
|
; 8. Restore User RSP from kernel stack
|
||||||
mov rsp, [rel user_rsp_scratch]
|
pop rsp
|
||||||
|
|
||||||
; 8. Return to User Mode
|
; 9. Return to User Mode (sysret)
|
||||||
; NASM syntax for 64-bit sysret requires the o64 prefix
|
or r11, 0x200 ; Force Interrupts enabled
|
||||||
; Force IF=1 (bit 9) in R11 (restored to RFLAGS) to ensure interrupts stay enabled!
|
|
||||||
or r11, 0x200
|
|
||||||
|
|
||||||
o64 sysret
|
o64 sysret
|
||||||
|
|
||||||
section .bss
|
section .bss
|
||||||
|
|
|
||||||
30
src/kernel/userland/Makefile
Normal file
30
src/kernel/userland/Makefile
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
CC = x86_64-elf-gcc
|
||||||
|
AS = nasm
|
||||||
|
LD = x86_64-elf-ld
|
||||||
|
|
||||||
|
CFLAGS = -Wall -Wextra -std=gnu11 -ffreestanding -O2 -fno-stack-protector -fno-stack-check -fno-lto -fno-pie -m64 -march=x86-64 -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone -Ilibc
|
||||||
|
LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic-linker -z text -z max-page-size=0x1000 -e _start
|
||||||
|
|
||||||
|
LIBC_SOURCES = $(wildcard libc/*.c)
|
||||||
|
LIBC_OBJS = $(LIBC_SOURCES:.c=.o) crt0.o
|
||||||
|
|
||||||
|
APP_SOURCES = $(wildcard *.c)
|
||||||
|
APP_OBJS = $(APP_SOURCES:.c=.o)
|
||||||
|
APP_ELFS = $(APP_SOURCES:.c=.elf)
|
||||||
|
|
||||||
|
all: $(APP_ELFS)
|
||||||
|
|
||||||
|
crt0.o: crt0.asm
|
||||||
|
$(AS) -f elf64 $< -o $@
|
||||||
|
|
||||||
|
libc/%.o: libc/%.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.elf: $(LIBC_OBJS) %.o
|
||||||
|
$(LD) $(LDFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o libc/*.o *.elf
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#include "calculator.h"
|
#include "syscall.h"
|
||||||
#include "graphics.h"
|
#include "libui.h"
|
||||||
#include "wm.h"
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
Window win_calculator;
|
|
||||||
|
|
||||||
#define SCALE 1000000LL
|
#define SCALE 1000000LL
|
||||||
|
|
||||||
|
// Dark Mode Colors mapping to libui
|
||||||
|
#define COLOR_DARK_BG 0xFF1E1E1E
|
||||||
|
#define COLOR_DARK_PANEL 0xFF2D2D2D
|
||||||
|
#define COLOR_DARK_TEXT 0xFFF0F0F0
|
||||||
|
#define COLOR_DARK_BORDER 0xFF3A3A3A
|
||||||
|
|
||||||
|
static ui_window_t win_calculator;
|
||||||
|
|
||||||
static long long calc_acc = 0;
|
static long long calc_acc = 0;
|
||||||
static long long calc_curr = 0;
|
static long long calc_curr = 0;
|
||||||
static char calc_op = 0;
|
static char calc_op = 0;
|
||||||
|
|
@ -15,8 +19,9 @@ static bool calc_new_entry = true;
|
||||||
static bool calc_error = false;
|
static bool calc_error = false;
|
||||||
static bool calc_decimal_mode = false;
|
static bool calc_decimal_mode = false;
|
||||||
static long long calc_decimal_divisor = 10;
|
static long long calc_decimal_divisor = 10;
|
||||||
|
static char display_buffer[1024];
|
||||||
|
static int display_buf_len = 0;
|
||||||
|
|
||||||
// Simple integer square root
|
|
||||||
static long long isqrt(long long n) {
|
static long long isqrt(long long n) {
|
||||||
if (n < 0) return -1;
|
if (n < 0) return -1;
|
||||||
if (n == 0) return 0;
|
if (n == 0) return 0;
|
||||||
|
|
@ -29,45 +34,31 @@ static long long isqrt(long long n) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert fixed point to string
|
|
||||||
static void fixed_to_str(long long n, char *buf) {
|
static void fixed_to_str(long long n, char *buf) {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
buf[0] = '0'; buf[1] = 0; return;
|
buf[0] = '0'; buf[1] = 0; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char temp[64];
|
char temp[64];
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
bool neg = n < 0;
|
bool neg = n < 0;
|
||||||
if (neg) n = -n;
|
if (neg) n = -n;
|
||||||
|
|
||||||
long long int_part = n / SCALE;
|
long long int_part = n / SCALE;
|
||||||
long long frac_part = n % SCALE;
|
long long frac_part = n % SCALE;
|
||||||
|
|
||||||
// Fraction part
|
|
||||||
char frac_buf[16];
|
char frac_buf[16];
|
||||||
int f_idx = 0;
|
int f_idx = 0;
|
||||||
// Fill exactly 6 digits
|
|
||||||
for(int k=0; k<6; k++) {
|
for(int k=0; k<6; k++) {
|
||||||
long long div = 100000;
|
long long div = 100000;
|
||||||
for(int m=0; m<k; m++) div /= 10;
|
for(int m=0; m<k; m++) div /= 10;
|
||||||
frac_buf[f_idx++] = '0' + ((frac_part / div) % 10);
|
frac_buf[f_idx++] = '0' + ((frac_part / div) % 10);
|
||||||
}
|
}
|
||||||
frac_buf[f_idx] = 0;
|
frac_buf[f_idx] = 0;
|
||||||
|
while (f_idx > 0 && frac_buf[f_idx-1] == '0') f_idx--;
|
||||||
// Trim trailing zeros
|
|
||||||
while (f_idx > 0 && frac_buf[f_idx-1] == '0') {
|
|
||||||
f_idx--;
|
|
||||||
}
|
|
||||||
frac_buf[f_idx] = 0;
|
frac_buf[f_idx] = 0;
|
||||||
|
|
||||||
if (f_idx > 0) {
|
if (f_idx > 0) {
|
||||||
for (int i = f_idx - 1; i >= 0; i--) {
|
for (int i = f_idx - 1; i >= 0; i--) temp[pos++] = frac_buf[i];
|
||||||
temp[pos++] = frac_buf[i];
|
|
||||||
}
|
|
||||||
temp[pos++] = '.';
|
temp[pos++] = '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Integer part
|
|
||||||
if (int_part == 0) {
|
if (int_part == 0) {
|
||||||
temp[pos++] = '0';
|
temp[pos++] = '0';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -76,40 +67,33 @@ static void fixed_to_str(long long n, char *buf) {
|
||||||
int_part /= 10;
|
int_part /= 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (neg) temp[pos++] = '-';
|
if (neg) temp[pos++] = '-';
|
||||||
|
|
||||||
// Reverse into buf
|
|
||||||
int j = 0;
|
int j = 0;
|
||||||
while (pos > 0) {
|
while (pos > 0) buf[j++] = temp[--pos];
|
||||||
buf[j++] = temp[--pos];
|
|
||||||
}
|
|
||||||
buf[j] = 0;
|
buf[j] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_display(Window *win) {
|
static void update_display(void) {
|
||||||
if (calc_error) {
|
if (calc_error) {
|
||||||
char *err = "Error";
|
char *err = "Error";
|
||||||
int i = 0; while(err[i]) { win->buffer[i] = err[i]; i++; }
|
int i = 0; while(err[i]) { display_buffer[i] = err[i]; i++; }
|
||||||
win->buffer[i] = 0;
|
display_buffer[i] = 0;
|
||||||
} else {
|
} else {
|
||||||
fixed_to_str(calc_curr, win->buffer);
|
fixed_to_str(calc_curr, display_buffer);
|
||||||
}
|
}
|
||||||
win->buf_len = 0; while(win->buffer[win->buf_len]) win->buf_len++;
|
display_buf_len = 0; while(display_buffer[display_buf_len]) display_buf_len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calculator_paint(Window *win) {
|
static void calculator_paint(void) {
|
||||||
// Background
|
int w = 180;
|
||||||
draw_rect(win->x + 4, win->y + 30, win->w - 8, win->h - 34, COLOR_DARK_BG);
|
int h = 230;
|
||||||
|
ui_draw_rect(win_calculator, 4, 30, w - 8, h - 34, COLOR_DARK_BG);
|
||||||
|
ui_draw_rounded_rect_filled(win_calculator, 10, 36, w - 20, 25, 6, COLOR_DARK_PANEL);
|
||||||
|
|
||||||
// Display Area with dark mode styling
|
int text_w = display_buf_len * 8;
|
||||||
draw_rounded_rect_filled(win->x + 10, win->y + 36, win->w - 20, 25, 6, COLOR_DARK_PANEL);
|
int text_x = w - 15 - text_w;
|
||||||
// Right align text
|
ui_draw_string(win_calculator, text_x, 44, display_buffer, COLOR_DARK_TEXT);
|
||||||
int text_w = win->buf_len * 8;
|
|
||||||
int text_x = win->x + win->w - 15 - text_w;
|
|
||||||
draw_string(text_x, win->y + 44, win->buffer, COLOR_DARK_TEXT);
|
|
||||||
|
|
||||||
// Buttons - macOS style squircle buttons
|
|
||||||
const char *labels[] = {
|
const char *labels[] = {
|
||||||
"C", "sqr", "rt", "/",
|
"C", "sqr", "rt", "/",
|
||||||
"7", "8", "9", "*",
|
"7", "8", "9", "*",
|
||||||
|
|
@ -121,19 +105,16 @@ static void calculator_paint(Window *win) {
|
||||||
int bw = 35;
|
int bw = 35;
|
||||||
int bh = 25;
|
int bh = 25;
|
||||||
int gap = 5;
|
int gap = 5;
|
||||||
int start_x = win->x + 10;
|
int start_x = 10;
|
||||||
int start_y = win->y + 70;
|
int start_y = 70;
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
int r = i / 4;
|
int r = i / 4;
|
||||||
int c = i % 4;
|
int c = i % 4;
|
||||||
|
ui_draw_rounded_rect_filled(win_calculator, start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, 4, COLOR_DARK_BORDER);
|
||||||
// Draw rounded button backgrounds
|
|
||||||
draw_rounded_rect_filled(start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, 4, COLOR_DARK_BORDER);
|
|
||||||
// Draw button text
|
|
||||||
int label_x = start_x + c*(bw+gap) + 5;
|
int label_x = start_x + c*(bw+gap) + 5;
|
||||||
int label_y = start_y + r*(bh+gap) + 9;
|
int label_y = start_y + r*(bh+gap) + 9;
|
||||||
draw_string(label_x, label_y, labels[i], COLOR_DARK_TEXT);
|
ui_draw_string(win_calculator, label_x, label_y, labels[i], COLOR_DARK_TEXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,20 +126,18 @@ static void do_op(void) {
|
||||||
}
|
}
|
||||||
else if (calc_op == '/') {
|
else if (calc_op == '/') {
|
||||||
if (calc_curr == 0) calc_error = true;
|
if (calc_curr == 0) calc_error = true;
|
||||||
else {
|
else calc_acc = (calc_acc * SCALE) / calc_curr;
|
||||||
calc_acc = (calc_acc * SCALE) / calc_curr;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
calc_acc = calc_curr;
|
calc_acc = calc_curr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calculator_click(Window *win, int x, int y) {
|
static void calculator_click(int x, int y) {
|
||||||
int bw = 35;
|
int bw = 35;
|
||||||
int bh = 25;
|
int bh = 25;
|
||||||
int gap = 5;
|
int gap = 5;
|
||||||
int start_x = 10;
|
int start_x = 10;
|
||||||
int start_y = 65;
|
int start_y = 65; // Matches the hitboxes
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
int r = i / 4;
|
int r = i / 4;
|
||||||
|
|
@ -168,14 +147,13 @@ static void calculator_click(Window *win, int x, int y) {
|
||||||
|
|
||||||
if (x >= bx && x < bx + bw && y >= by && y < by + bh) {
|
if (x >= bx && x < bx + bw && y >= by && y < by + bh) {
|
||||||
const char *labels[] = {
|
const char *labels[] = {
|
||||||
"C", "sqr", "rt", "/",
|
"C", "s", "r", "/",
|
||||||
"7", "8", "9", "*",
|
"7", "8", "9", "*",
|
||||||
"4", "5", "6", "-",
|
"4", "5", "6", "-",
|
||||||
"1", "2", "3", "+",
|
"1", "2", "3", "+",
|
||||||
"0", ".", "BS", "="
|
"0", ".", "B", "="
|
||||||
};
|
};
|
||||||
const char *lbl_str = labels[i];
|
char lbl = labels[i][0];
|
||||||
char lbl = lbl_str[0];
|
|
||||||
|
|
||||||
if (lbl >= '0' && lbl <= '9') {
|
if (lbl >= '0' && lbl <= '9') {
|
||||||
if (calc_new_entry || calc_error) {
|
if (calc_new_entry || calc_error) {
|
||||||
|
|
@ -184,7 +162,6 @@ static void calculator_click(Window *win, int x, int y) {
|
||||||
calc_decimal_mode = false;
|
calc_decimal_mode = false;
|
||||||
} else {
|
} else {
|
||||||
if (calc_decimal_mode) {
|
if (calc_decimal_mode) {
|
||||||
// Add digit to fraction
|
|
||||||
if (calc_decimal_divisor <= SCALE) {
|
if (calc_decimal_divisor <= SCALE) {
|
||||||
long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor;
|
long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor;
|
||||||
if (calc_curr >= 0) calc_curr += digit_val;
|
if (calc_curr >= 0) calc_curr += digit_val;
|
||||||
|
|
@ -192,7 +169,6 @@ static void calculator_click(Window *win, int x, int y) {
|
||||||
calc_decimal_divisor *= 10;
|
calc_decimal_divisor *= 10;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Integer shift
|
|
||||||
if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE;
|
if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE;
|
||||||
else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE;
|
else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE;
|
||||||
}
|
}
|
||||||
|
|
@ -208,69 +184,81 @@ static void calculator_click(Window *win, int x, int y) {
|
||||||
calc_decimal_divisor = 10;
|
calc_decimal_divisor = 10;
|
||||||
}
|
}
|
||||||
} else if (lbl == 'C') {
|
} else if (lbl == 'C') {
|
||||||
calc_curr = 0;
|
calc_curr = 0; calc_acc = 0; calc_op = 0;
|
||||||
calc_acc = 0;
|
calc_new_entry = true; calc_error = false; calc_decimal_mode = false;
|
||||||
calc_op = 0;
|
} else if (lbl == 'B') {
|
||||||
calc_new_entry = true;
|
|
||||||
calc_error = false;
|
|
||||||
calc_decimal_mode = false;
|
|
||||||
} else if (lbl == 'B') { // BS (Backspace)
|
|
||||||
if (!calc_new_entry && !calc_error) {
|
if (!calc_new_entry && !calc_error) {
|
||||||
if (calc_decimal_mode) {
|
if (calc_decimal_mode) {
|
||||||
calc_curr = 0;
|
if (calc_decimal_divisor > 10) {
|
||||||
calc_new_entry = true;
|
calc_decimal_divisor /= 10;
|
||||||
|
long long unit = SCALE / calc_decimal_divisor;
|
||||||
|
// This removes the last decimal digit
|
||||||
|
calc_curr = (calc_curr / (unit * 10)) * (unit * 10);
|
||||||
|
} else {
|
||||||
|
// Backspace on the dot itself or first decimal
|
||||||
|
calc_decimal_mode = false;
|
||||||
|
calc_decimal_divisor = 10;
|
||||||
|
// Ensure the fractional part is cleared
|
||||||
|
calc_curr = (calc_curr / SCALE) * SCALE;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
calc_curr /= 10;
|
// Integer backspace: remove last digit of integer part
|
||||||
|
calc_curr = (calc_curr / SCALE / 10) * SCALE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (lbl == 's') { // sqr
|
} else if (lbl == 's') { // sqr
|
||||||
calc_curr = (calc_curr * calc_curr) / SCALE;
|
calc_curr = (calc_curr * calc_curr) / SCALE; calc_new_entry = true;
|
||||||
calc_new_entry = true;
|
} else if (lbl == 'r') { // rt
|
||||||
} else if (lbl == 'r') { // rt (sqrt)
|
|
||||||
long long s = isqrt(calc_curr);
|
long long s = isqrt(calc_curr);
|
||||||
if (s == -1) calc_error = true;
|
if (s == -1) calc_error = true;
|
||||||
else calc_curr = s * 1000;
|
else calc_curr = s * 1000;
|
||||||
calc_new_entry = true;
|
calc_new_entry = true;
|
||||||
} else if (lbl == '=') {
|
} else if (lbl == '=') {
|
||||||
do_op();
|
do_op();
|
||||||
calc_curr = calc_acc;
|
calc_curr = calc_acc; calc_op = 0; calc_new_entry = true; calc_decimal_mode = false;
|
||||||
calc_op = 0;
|
|
||||||
calc_new_entry = true;
|
|
||||||
calc_decimal_mode = false;
|
|
||||||
} else {
|
} else {
|
||||||
// Operator
|
|
||||||
if (!calc_new_entry) {
|
if (!calc_new_entry) {
|
||||||
if (calc_op) do_op();
|
if (calc_op) do_op();
|
||||||
else calc_acc = calc_curr;
|
else calc_acc = calc_curr;
|
||||||
}
|
}
|
||||||
calc_op = lbl;
|
calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false;
|
||||||
calc_new_entry = true;
|
|
||||||
calc_decimal_mode = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_display(win);
|
update_display();
|
||||||
wm_mark_dirty(win->x, win->y, win->w, win->h);
|
calculator_paint();
|
||||||
|
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculator_init(void) {
|
int main(void) {
|
||||||
win_calculator.title = "Calculator";
|
win_calculator = ui_window_create("Calculator", 200, 200, 180, 230);
|
||||||
win_calculator.x = 200;
|
|
||||||
win_calculator.y = 200;
|
|
||||||
win_calculator.w = 180; // Slightly wider for 4 cols with gaps
|
|
||||||
win_calculator.h = 230; // Taller for 5 rows
|
|
||||||
win_calculator.visible = false;
|
|
||||||
win_calculator.focused = false;
|
|
||||||
win_calculator.z_index = 0;
|
|
||||||
win_calculator.paint = calculator_paint;
|
|
||||||
win_calculator.handle_click = calculator_click;
|
|
||||||
win_calculator.handle_right_click = NULL;
|
|
||||||
|
|
||||||
calc_curr = 0;
|
calc_curr = 0;
|
||||||
calc_acc = 0;
|
calc_acc = 0;
|
||||||
calc_op = 0;
|
calc_op = 0;
|
||||||
calc_new_entry = true;
|
calc_new_entry = true;
|
||||||
update_display(&win_calculator);
|
update_display();
|
||||||
}
|
|
||||||
|
// First paint
|
||||||
|
calculator_paint();
|
||||||
|
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
|
||||||
|
|
||||||
|
// Check events from Kernel User Interface queue
|
||||||
|
gui_event_t ev;
|
||||||
|
while (1) {
|
||||||
|
if (ui_get_event(win_calculator, &ev)) {
|
||||||
|
if (ev.type == GUI_EVENT_PAINT) {
|
||||||
|
calculator_paint();
|
||||||
|
ui_mark_dirty(win_calculator, 0, 0, 180, 230);
|
||||||
|
} else if (ev.type == GUI_EVENT_CLICK) {
|
||||||
|
calculator_click(ev.arg1, ev.arg2);
|
||||||
|
} else if (ev.type == GUI_EVENT_CLOSE) {
|
||||||
|
sys_exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
src/kernel/userland/calculator.elf
Executable file
BIN
src/kernel/userland/calculator.elf
Executable file
Binary file not shown.
12
src/kernel/userland/crash.c
Normal file
12
src/kernel/userland/crash.c
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const char* msg = "Attempting to crash via null dereference...\n";
|
||||||
|
sys_write(1, msg, 45);
|
||||||
|
|
||||||
|
// Null pointer dereference
|
||||||
|
volatile int* p = (int*)0;
|
||||||
|
*p = 123;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
src/kernel/userland/crash.elf
Executable file
BIN
src/kernel/userland/crash.elf
Executable file
Binary file not shown.
26
src/kernel/userland/crt0.asm
Normal file
26
src/kernel/userland/crt0.asm
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
; userland/crt0.asm
|
||||||
|
global _start
|
||||||
|
extern main
|
||||||
|
extern sys_exit
|
||||||
|
|
||||||
|
section .text
|
||||||
|
_start:
|
||||||
|
; The kernel loads the ELF and jumps here.
|
||||||
|
; RSP should point to the 0x800000 stack.
|
||||||
|
|
||||||
|
; Align the stack to 16 bytes for C functions (System V ABI)
|
||||||
|
and rsp, -16
|
||||||
|
|
||||||
|
; Call main(argc, argv)
|
||||||
|
; We don't have argc or argv yet, pass 0
|
||||||
|
xor rdi, rdi
|
||||||
|
xor rsi, rsi
|
||||||
|
call main
|
||||||
|
|
||||||
|
; If main returns, call exit(status)
|
||||||
|
mov rdi, rax ; Pass main's return value to exit syscall
|
||||||
|
call sys_exit
|
||||||
|
|
||||||
|
; Fallback halt if exit miraculously returns
|
||||||
|
.hang:
|
||||||
|
jmp .hang
|
||||||
BIN
src/kernel/userland/crt0.o
Normal file
BIN
src/kernel/userland/crt0.o
Normal file
Binary file not shown.
7
src/kernel/userland/hello.c
Normal file
7
src/kernel/userland/hello.c
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const char* msg = "Hello from Userland ELF!\n";
|
||||||
|
sys_write(1, msg, 25);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
src/kernel/userland/hello.elf
Executable file
BIN
src/kernel/userland/hello.elf
Executable file
Binary file not shown.
BIN
src/kernel/userland/hello.o
Normal file
BIN
src/kernel/userland/hello.o
Normal file
Binary file not shown.
40
src/kernel/userland/libc/libui.c
Normal file
40
src/kernel/userland/libc/libui.c
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "libui.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
extern uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3);
|
||||||
|
extern uint64_t syscall4(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
|
||||||
|
extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
|
||||||
|
|
||||||
|
// sys_gui uses syscall #3
|
||||||
|
#define SYS_GUI 3
|
||||||
|
|
||||||
|
ui_window_t ui_window_create(const char *title, int x, int y, int w, int h) {
|
||||||
|
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
|
||||||
|
return (ui_window_t)syscall3(SYS_GUI, GUI_CMD_WINDOW_CREATE, (uint64_t)title, (uint64_t)params);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ui_get_event(ui_window_t win, gui_event_t *ev) {
|
||||||
|
int res = (int)syscall3(SYS_GUI, GUI_CMD_GET_EVENT, (uint64_t)win, (uint64_t)ev);
|
||||||
|
return res != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_draw_rect(ui_window_t win, int x, int y, int w, int h, uint32_t color) {
|
||||||
|
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
|
||||||
|
syscall4(SYS_GUI, GUI_CMD_DRAW_RECT, (uint64_t)win, (uint64_t)params, (uint64_t)color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_draw_rounded_rect_filled(ui_window_t win, int x, int y, int w, int h, int radius, uint32_t color) {
|
||||||
|
uint64_t params[5] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h, (uint64_t)radius };
|
||||||
|
syscall4(SYS_GUI, GUI_CMD_DRAW_ROUNDED_RECT_FILLED, (uint64_t)win, (uint64_t)params, (uint64_t)color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t color) {
|
||||||
|
uint64_t coords = ((uint64_t)x & 0xFFFFFFFF) | ((uint64_t)y << 32);
|
||||||
|
syscall5(SYS_GUI, GUI_CMD_DRAW_STRING, (uint64_t)win, coords, (uint64_t)str, (uint64_t)color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h) {
|
||||||
|
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
|
||||||
|
syscall3(SYS_GUI, GUI_CMD_MARK_DIRTY, (uint64_t)win, (uint64_t)params);
|
||||||
|
}
|
||||||
40
src/kernel/userland/libc/libui.h
Normal file
40
src/kernel/userland/libc/libui.h
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef LIBUI_H
|
||||||
|
#define LIBUI_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// GUI Command IDs
|
||||||
|
#define GUI_CMD_WINDOW_CREATE 1
|
||||||
|
#define GUI_CMD_DRAW_RECT 2
|
||||||
|
#define GUI_CMD_DRAW_STRING 3
|
||||||
|
#define GUI_CMD_MARK_DIRTY 4
|
||||||
|
#define GUI_CMD_GET_EVENT 5
|
||||||
|
#define GUI_CMD_DRAW_ROUNDED_RECT_FILLED 6
|
||||||
|
|
||||||
|
// Event Types
|
||||||
|
#define GUI_EVENT_NONE 0
|
||||||
|
#define GUI_EVENT_PAINT 1
|
||||||
|
#define GUI_EVENT_CLICK 2
|
||||||
|
#define GUI_EVENT_RIGHT_CLICK 3
|
||||||
|
#define GUI_EVENT_CLOSE 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int type;
|
||||||
|
int arg1; // For click: x
|
||||||
|
int arg2; // For click: y
|
||||||
|
} gui_event_t;
|
||||||
|
|
||||||
|
// Window Handle
|
||||||
|
typedef int ui_window_t;
|
||||||
|
|
||||||
|
// libui API
|
||||||
|
ui_window_t ui_window_create(const char *title, int x, int y, int w, int h);
|
||||||
|
bool ui_get_event(ui_window_t win, gui_event_t *ev);
|
||||||
|
|
||||||
|
void ui_draw_rect(ui_window_t win, int x, int y, int w, int h, uint32_t color);
|
||||||
|
void ui_draw_rounded_rect_filled(ui_window_t win, int x, int y, int w, int h, int radius, uint32_t color);
|
||||||
|
void ui_draw_string(ui_window_t win, int x, int y, const char *str, uint32_t color);
|
||||||
|
void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h);
|
||||||
|
|
||||||
|
#endif
|
||||||
71
src/kernel/userland/libc/syscall.c
Normal file
71
src/kernel/userland/libc/syscall.c
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t syscall0(uint64_t sys_num) {
|
||||||
|
uint64_t ret;
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(sys_num)
|
||||||
|
: "rcx", "r11", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall1(uint64_t sys_num, uint64_t arg1) {
|
||||||
|
uint64_t ret;
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(sys_num), "D"(arg1)
|
||||||
|
: "rcx", "r11", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall2(uint64_t sys_num, uint64_t arg1, uint64_t arg2) {
|
||||||
|
uint64_t ret;
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(sys_num), "D"(arg1), "S"(arg2)
|
||||||
|
: "rcx", "r11", "memory");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
|
||||||
|
uint64_t ret;
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(sys_num), "D"(arg1), "S"(arg2), "d"(arg3)
|
||||||
|
: "rcx", "r11", "memory", "r10", "r8", "r9");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall4(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) {
|
||||||
|
uint64_t ret;
|
||||||
|
register uint64_t r10 asm("r10") = arg4;
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(sys_num), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10)
|
||||||
|
: "rcx", "r11", "memory", "r8", "r9");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
|
||||||
|
uint64_t ret;
|
||||||
|
register uint64_t r10 asm("r10") = arg4;
|
||||||
|
register uint64_t r8 asm("r8") = arg5;
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a"(ret)
|
||||||
|
: "a"(sys_num), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8)
|
||||||
|
: "rcx", "r11", "memory", "r9");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// C-Friendly Wrappers
|
||||||
|
|
||||||
|
void sys_exit(int status) {
|
||||||
|
syscall1(SYS_EXIT, (uint64_t)status);
|
||||||
|
while (1); // Halt
|
||||||
|
}
|
||||||
|
|
||||||
|
int sys_write(int fd, const char *buf, int len) {
|
||||||
|
return (int)syscall3(SYS_WRITE, (uint64_t)fd, (uint64_t)buf, (uint64_t)len);
|
||||||
|
}
|
||||||
22
src/kernel/userland/libc/syscall.h
Normal file
22
src/kernel/userland/libc/syscall.h
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef SYSCALL_H
|
||||||
|
#define SYSCALL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Standard syscalls available from Kernel mode
|
||||||
|
#define SYS_EXIT 0
|
||||||
|
#define SYS_WRITE 1
|
||||||
|
|
||||||
|
// Internal assembly entry into Ring 0
|
||||||
|
extern uint64_t syscall0(uint64_t sys_num);
|
||||||
|
extern uint64_t syscall1(uint64_t sys_num, uint64_t arg1);
|
||||||
|
extern uint64_t syscall2(uint64_t sys_num, uint64_t arg1, uint64_t arg2);
|
||||||
|
extern uint64_t syscall3(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3);
|
||||||
|
extern uint64_t syscall4(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
|
||||||
|
extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
void sys_exit(int status);
|
||||||
|
int sys_write(int fd, const char *buf, int len);
|
||||||
|
|
||||||
|
#endif
|
||||||
BIN
src/kernel/userland/libc/syscall.o
Normal file
BIN
src/kernel/userland/libc/syscall.o
Normal file
Binary file not shown.
131
src/kernel/wm.c
131
src/kernel/wm.c
|
|
@ -2,7 +2,7 @@
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "cmd.h"
|
#include "cmd.h"
|
||||||
#include "calculator.h"
|
#include "process.h"
|
||||||
#include "cli_apps/cli_utils.h"
|
#include "cli_apps/cli_utils.h"
|
||||||
#include "explorer.h"
|
#include "explorer.h"
|
||||||
#include "editor.h"
|
#include "editor.h"
|
||||||
|
|
@ -475,6 +475,40 @@ void draw_document_icon(int x, int y, const char *label) {
|
||||||
draw_icon_label(x, y, label);
|
draw_icon_label(x, y, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw_elf_icon(int x, int y, const char *label) {
|
||||||
|
uint32_t icon_buf[48 * 48];
|
||||||
|
for (int i = 0; i < 48 * 48; i++) icon_buf[i] = 0xFFFF00FF;
|
||||||
|
graphics_set_render_target(icon_buf, 48, 48);
|
||||||
|
|
||||||
|
// Grey squircle (macOS detailed style)
|
||||||
|
draw_rounded_rect_filled(2, 2, 44, 44, 12, 0xFF353535); // Subtle shadow border
|
||||||
|
draw_rounded_rect_filled(4, 4, 40, 40, 10, 0xFF4A4A4A); // Main grey body
|
||||||
|
|
||||||
|
// Glossy top highlight
|
||||||
|
draw_rect(10, 5, 28, 1, 0xFF5A5A5A);
|
||||||
|
|
||||||
|
// Green "exec" text (fixed font 8x12)
|
||||||
|
draw_string(8, 12, "exec", 0xFF00FF00);
|
||||||
|
|
||||||
|
// Minor details to look "premium"
|
||||||
|
draw_rect(10, 28, 28, 1, 0xFF3D3D3D);
|
||||||
|
draw_rect(10, 34, 20, 1, 0xFF3D3D3D);
|
||||||
|
|
||||||
|
graphics_set_render_target(NULL, 0, 0);
|
||||||
|
|
||||||
|
int dx = x + 24, dy = y + 12;
|
||||||
|
for (int ty = 0; ty < 32; ty++) {
|
||||||
|
for (int tx = 0; tx < 32; tx++) {
|
||||||
|
int src_x = tx * 48 / 32;
|
||||||
|
int src_y = ty * 48 / 32;
|
||||||
|
uint32_t c1 = icon_buf[src_y * 48 + src_x];
|
||||||
|
if (c1 != 0xFFFF00FF) put_pixel(dx + tx, dy + ty, c1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_icon_label(x, y, label);
|
||||||
|
}
|
||||||
|
|
||||||
// === Dynamic thumbnail cache for JPG explorer icons ===
|
// === Dynamic thumbnail cache for JPG explorer icons ===
|
||||||
#define THUMB_CACHE_SIZE 8
|
#define THUMB_CACHE_SIZE 8
|
||||||
#define THUMB_PIXELS (48 * 48)
|
#define THUMB_PIXELS (48 * 48)
|
||||||
|
|
@ -967,6 +1001,12 @@ void draw_window(Window *win) {
|
||||||
draw_rounded_rect_filled(win->x, win->y + 20, win->w, win->h - 20, 8, COLOR_DARK_BG);
|
draw_rounded_rect_filled(win->x, win->y + 20, win->w, win->h - 20, 8, COLOR_DARK_BG);
|
||||||
draw_rect(win->x, win->y + 20, win->w, 8, COLOR_DARK_BG);
|
draw_rect(win->x, win->y + 20, win->w, 8, COLOR_DARK_BG);
|
||||||
|
|
||||||
|
if (win->comp_pixels) {
|
||||||
|
graphics_blit_buffer(win->comp_pixels, win->x, win->y, win->w, win->h);
|
||||||
|
} else if (win->pixels) {
|
||||||
|
graphics_blit_buffer(win->pixels, win->x, win->y, win->w, win->h);
|
||||||
|
}
|
||||||
|
|
||||||
if (win->paint) {
|
if (win->paint) {
|
||||||
win->paint(win);
|
win->paint(win);
|
||||||
}
|
}
|
||||||
|
|
@ -1101,7 +1141,8 @@ void wm_paint(void) {
|
||||||
else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label);
|
else if (str_starts_with(icon->name, "Paint")) draw_paint_icon(icon->x, icon->y, label);
|
||||||
else draw_icon(icon->x, icon->y, label);
|
else draw_icon(icon->x, icon->y, label);
|
||||||
} else {
|
} else {
|
||||||
if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
|
if (str_ends_with(icon->name, ".elf")) draw_elf_icon(icon->x, icon->y, icon->name);
|
||||||
|
else if (str_ends_with(icon->name, ".pnt")) draw_paint_icon(icon->x, icon->y, icon->name);
|
||||||
else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) {
|
else if (str_ends_with(icon->name, ".jpg") || str_ends_with(icon->name, ".JPG")) {
|
||||||
draw_image_icon(icon->x, icon->y, icon->name);
|
draw_image_icon(icon->x, icon->y, icon->name);
|
||||||
draw_icon_label(icon->x, icon->y, icon->name);
|
draw_icon_label(icon->x, icon->y, icon->name);
|
||||||
|
|
@ -1299,6 +1340,41 @@ void wm_bring_to_front(Window *win) {
|
||||||
void wm_add_window(Window *win) {
|
void wm_add_window(Window *win) {
|
||||||
if (window_count < 32) {
|
if (window_count < 32) {
|
||||||
all_windows[window_count++] = win;
|
all_windows[window_count++] = win;
|
||||||
|
wm_bring_to_front(win); // Ensure newly added windows are on top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wm_remove_window(Window *win) {
|
||||||
|
if (!win) return;
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < window_count; i++) {
|
||||||
|
if (all_windows[i] == win) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
// Shift remaining windows
|
||||||
|
for (int i = index; i < window_count - 1; i++) {
|
||||||
|
all_windows[i] = all_windows[i + 1];
|
||||||
|
}
|
||||||
|
window_count--;
|
||||||
|
|
||||||
|
// Free resources
|
||||||
|
if (win->pixels) kfree(win->pixels);
|
||||||
|
if (win->comp_pixels) kfree(win->comp_pixels);
|
||||||
|
// If the title was allocated in syscall.c, we should free it,
|
||||||
|
// but currently we don't know for sure if it was kmalloc'd or a literal.
|
||||||
|
// In syscall.c it is kmalloc'd. Let's assume we should free it if it's not a known literal.
|
||||||
|
// Safer to just free it since userland windows always have kmalloc'd titles.
|
||||||
|
if (win->title && win->handle_close) { // Heuristic: user windows have handle_close set in syscall.c
|
||||||
|
kfree(win->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(win);
|
||||||
|
force_redraw = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1499,7 +1575,12 @@ void wm_handle_click(int x, int y) {
|
||||||
|
|
||||||
// Check traffic light close button (now at top-left)
|
// Check traffic light close button (now at top-left)
|
||||||
if (rect_contains(topmost->x + 8, topmost->y + 2, 12, 12, x, y)) {
|
if (rect_contains(topmost->x + 8, topmost->y + 2, 12, 12, x, y)) {
|
||||||
topmost->visible = false;
|
if (topmost->handle_close) {
|
||||||
|
topmost->handle_close(topmost);
|
||||||
|
} else {
|
||||||
|
topmost->visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset window state on close
|
// Reset window state on close
|
||||||
if (topmost == &win_explorer) {
|
if (topmost == &win_explorer) {
|
||||||
explorer_reset();
|
explorer_reset();
|
||||||
|
|
@ -1749,7 +1830,7 @@ void wm_handle_right_click(int x, int y) {
|
||||||
} else if (str_starts_with(start_menu_pending_app, "Terminal")) {
|
} else if (str_starts_with(start_menu_pending_app, "Terminal")) {
|
||||||
cmd_reset(); wm_bring_to_front(&win_cmd);
|
cmd_reset(); wm_bring_to_front(&win_cmd);
|
||||||
} else if (str_starts_with(start_menu_pending_app, "Calculator")) {
|
} else if (str_starts_with(start_menu_pending_app, "Calculator")) {
|
||||||
wm_bring_to_front(&win_calculator);
|
process_create_elf("/bin/calculator.elf");
|
||||||
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) {
|
} else if (str_starts_with(start_menu_pending_app, "Minesweeper")) {
|
||||||
wm_bring_to_front(&win_minesweeper);
|
wm_bring_to_front(&win_minesweeper);
|
||||||
} else if (str_starts_with(start_menu_pending_app, "Settings")) {
|
} else if (str_starts_with(start_menu_pending_app, "Settings")) {
|
||||||
|
|
@ -1779,7 +1860,7 @@ void wm_handle_right_click(int x, int y) {
|
||||||
if (str_ends_with(icon->name, "Notepad.shortcut")) {
|
if (str_ends_with(icon->name, "Notepad.shortcut")) {
|
||||||
wm_bring_to_front(&win_notepad); handled = true;
|
wm_bring_to_front(&win_notepad); handled = true;
|
||||||
} else if (str_ends_with(icon->name, "Calculator.shortcut")) {
|
} else if (str_ends_with(icon->name, "Calculator.shortcut")) {
|
||||||
wm_bring_to_front(&win_calculator); handled = true;
|
process_create_elf("/bin/calculator.elf"); handled = true;
|
||||||
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) {
|
} else if (str_ends_with(icon->name, "Minesweeper.shortcut")) {
|
||||||
wm_bring_to_front(&win_minesweeper); handled = true;
|
wm_bring_to_front(&win_minesweeper); handled = true;
|
||||||
} else if (str_ends_with(icon->name, "Settings.shortcut")) {
|
} else if (str_ends_with(icon->name, "Settings.shortcut")) {
|
||||||
|
|
@ -1830,7 +1911,9 @@ void wm_handle_right_click(int x, int y) {
|
||||||
char path[128] = "/Desktop/";
|
char path[128] = "/Desktop/";
|
||||||
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
|
int p=9; int n=0; while(icon->name[n]) path[p++] = icon->name[n++]; path[p]=0;
|
||||||
|
|
||||||
if (str_ends_with(icon->name, ".pnt")) {
|
if (str_ends_with(icon->name, ".elf")) {
|
||||||
|
process_create_elf(path);
|
||||||
|
} else if (str_ends_with(icon->name, ".pnt")) {
|
||||||
paint_load(path);
|
paint_load(path);
|
||||||
wm_bring_to_front(&win_paint);
|
wm_bring_to_front(&win_paint);
|
||||||
} else if (str_ends_with(icon->name, ".md")) {
|
} else if (str_ends_with(icon->name, ".md")) {
|
||||||
|
|
@ -2173,7 +2256,6 @@ void wm_init(void) {
|
||||||
|
|
||||||
notepad_init();
|
notepad_init();
|
||||||
cmd_init();
|
cmd_init();
|
||||||
calculator_init();
|
|
||||||
explorer_init();
|
explorer_init();
|
||||||
editor_init();
|
editor_init();
|
||||||
markdown_init();
|
markdown_init();
|
||||||
|
|
@ -2189,27 +2271,25 @@ void wm_init(void) {
|
||||||
// Initialize z-indices
|
// Initialize z-indices
|
||||||
win_notepad.z_index = 0;
|
win_notepad.z_index = 0;
|
||||||
win_cmd.z_index = 1;
|
win_cmd.z_index = 1;
|
||||||
win_calculator.z_index = 2;
|
win_explorer.z_index = 2;
|
||||||
win_explorer.z_index = 3;
|
win_editor.z_index = 3;
|
||||||
win_editor.z_index = 4;
|
win_markdown.z_index = 4;
|
||||||
win_markdown.z_index = 5;
|
win_control_panel.z_index = 5;
|
||||||
win_control_panel.z_index = 6;
|
win_about.z_index = 6;
|
||||||
win_about.z_index = 7;
|
win_minesweeper.z_index = 7;
|
||||||
win_minesweeper.z_index = 8;
|
win_paint.z_index = 8;
|
||||||
win_paint.z_index = 9;
|
|
||||||
|
|
||||||
all_windows[0] = &win_notepad;
|
all_windows[0] = &win_notepad;
|
||||||
all_windows[1] = &win_cmd;
|
all_windows[1] = &win_cmd;
|
||||||
all_windows[2] = &win_calculator;
|
all_windows[2] = &win_explorer;
|
||||||
all_windows[3] = &win_explorer;
|
all_windows[3] = &win_editor;
|
||||||
all_windows[4] = &win_editor;
|
all_windows[4] = &win_markdown;
|
||||||
all_windows[5] = &win_markdown;
|
all_windows[5] = &win_control_panel;
|
||||||
all_windows[6] = &win_control_panel;
|
all_windows[6] = &win_about;
|
||||||
all_windows[7] = &win_about;
|
all_windows[7] = &win_minesweeper;
|
||||||
all_windows[8] = &win_minesweeper;
|
all_windows[8] = &win_paint;
|
||||||
all_windows[9] = &win_paint;
|
all_windows[9] = &win_viewer;
|
||||||
all_windows[10] = &win_viewer;
|
window_count = 10;
|
||||||
window_count = 11;
|
|
||||||
|
|
||||||
// Only show Explorer and Notepad on desktop (Explorer on top)
|
// Only show Explorer and Notepad on desktop (Explorer on top)
|
||||||
win_explorer.visible = false;
|
win_explorer.visible = false;
|
||||||
|
|
@ -2222,7 +2302,6 @@ void wm_init(void) {
|
||||||
|
|
||||||
// Rest are hidden initially
|
// Rest are hidden initially
|
||||||
win_cmd.visible = false;
|
win_cmd.visible = false;
|
||||||
win_calculator.visible = false;
|
|
||||||
win_editor.visible = false;
|
win_editor.visible = false;
|
||||||
win_markdown.visible = false;
|
win_markdown.visible = false;
|
||||||
win_control_panel.visible = false;
|
win_control_panel.visible = false;
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,15 @@ struct Window {
|
||||||
bool focused;
|
bool focused;
|
||||||
int z_index; // Layering depth (higher = on top)
|
int z_index; // Layering depth (higher = on top)
|
||||||
void *data; // Per-window private data
|
void *data; // Per-window private data
|
||||||
|
uint32_t *pixels; // Pointer to backing buffer for UI events (Back Buffer)
|
||||||
|
uint32_t *comp_pixels; // Pointer to composition buffer (Front Buffer)
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
void (*paint)(Window *win);
|
void (*paint)(Window *win);
|
||||||
void (*handle_key)(Window *win, char c);
|
void (*handle_key)(Window *win, char c);
|
||||||
void (*handle_click)(Window *win, int x, int y);
|
void (*handle_click)(Window *win, int x, int y);
|
||||||
void (*handle_right_click)(Window *win, int x, int y);
|
void (*handle_right_click)(Window *win, int x, int y);
|
||||||
|
void (*handle_close)(Window *win);
|
||||||
};
|
};
|
||||||
|
|
||||||
void wm_init(void);
|
void wm_init(void);
|
||||||
|
|
@ -63,6 +66,7 @@ void wm_handle_click(int x, int y);
|
||||||
void wm_handle_right_click(int x, int y);
|
void wm_handle_right_click(int x, int y);
|
||||||
void wm_process_input(void);
|
void wm_process_input(void);
|
||||||
void wm_add_window(Window *win);
|
void wm_add_window(Window *win);
|
||||||
|
void wm_remove_window(Window *win);
|
||||||
void wm_bring_to_front(Window *win);
|
void wm_bring_to_front(Window *win);
|
||||||
|
|
||||||
// Redraw system
|
// Redraw system
|
||||||
|
|
@ -87,6 +91,7 @@ void draw_traffic_light(int x, int y); // Red close button only
|
||||||
void draw_icon(int x, int y, const char *label);
|
void draw_icon(int x, int y, const char *label);
|
||||||
void draw_folder_icon(int x, int y, const char *label);
|
void draw_folder_icon(int x, int y, const char *label);
|
||||||
void draw_document_icon(int x, int y, const char *label);
|
void draw_document_icon(int x, int y, const char *label);
|
||||||
|
void draw_elf_icon(int x, int y, const char *label);
|
||||||
void draw_image_icon(int x, int y, const char *label);
|
void draw_image_icon(int x, int y, const char *label);
|
||||||
void draw_notepad_icon(int x, int y, const char *label);
|
void draw_notepad_icon(int x, int y, const char *label);
|
||||||
void draw_calculator_icon(int x, int y, const char *label);
|
void draw_calculator_icon(int x, int y, const char *label);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue