mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
BSoD
This commit is contained in:
parent
a34aaa1070
commit
f9d84d434e
18 changed files with 303 additions and 135 deletions
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
BIN
build/idt.o
BIN
build/idt.o
Binary file not shown.
BIN
disk.img
BIN
disk.img
Binary file not shown.
|
|
@ -3,8 +3,6 @@ timeout: 3
|
||||||
# Graphics mode and Splash screen
|
# Graphics mode and Splash screen
|
||||||
term_palette: ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff
|
term_palette: ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff;ffffff
|
||||||
term_background: #00000000
|
term_background: #00000000
|
||||||
# Hide terminal by placing it out of view or making it very small initially if possible,
|
|
||||||
# but Limine's wallapper will be the main background.
|
|
||||||
wallpaper: boot():/splash.jpg
|
wallpaper: boot():/splash.jpg
|
||||||
|
|
||||||
/BoredOS
|
/BoredOS
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ void cli_cmd_boredver(char *args);
|
||||||
void cli_cmd_clear(char *args);
|
void cli_cmd_clear(char *args);
|
||||||
void cli_cmd_exit(char *args);
|
void cli_cmd_exit(char *args);
|
||||||
|
|
||||||
|
|
||||||
// Filesystem commands
|
// Filesystem commands
|
||||||
void cli_cmd_cd(char *args);
|
void cli_cmd_cd(char *args);
|
||||||
void cli_cmd_pwd(char *args);
|
void cli_cmd_pwd(char *args);
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,6 @@ void pager_wrap_content(const char **lines, int count) {
|
||||||
pager_total_lines++;
|
pager_total_lines++;
|
||||||
processed += chunk_len;
|
processed += chunk_len;
|
||||||
|
|
||||||
// Skip the space we just split on
|
|
||||||
if (processed < len && line[processed] == ' ') {
|
if (processed < len && line[processed] == ' ') {
|
||||||
processed++;
|
processed++;
|
||||||
}
|
}
|
||||||
|
|
@ -425,7 +424,6 @@ static void internal_cmd_cd(char *args) {
|
||||||
}
|
}
|
||||||
path[i] = 0;
|
path[i] = 0;
|
||||||
|
|
||||||
// For cmd_state, we need to build and validate the full path
|
|
||||||
if (cmd_state) {
|
if (cmd_state) {
|
||||||
// Build full path for validation
|
// Build full path for validation
|
||||||
char full_path[512] = {0};
|
char full_path[512] = {0};
|
||||||
|
|
@ -942,9 +940,7 @@ static void cmd_exec_single(char *cmd) {
|
||||||
|
|
||||||
if (needs_path) {
|
if (needs_path) {
|
||||||
if (args && args[0]) {
|
if (args && args[0]) {
|
||||||
// For echo with redirection, we need to prepend drive to redirect target too
|
|
||||||
if (is_echo_command) {
|
if (is_echo_command) {
|
||||||
// Find > or >> and prepend drive to the filename after it
|
|
||||||
char temp_args[512] = {0};
|
char temp_args[512] = {0};
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ static int ata_identify(uint16_t port_base, bool slave) {
|
||||||
// Read 256 words (512 bytes) of identity data
|
// Read 256 words (512 bytes) of identity data
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
uint16_t data = inw(port_base + ATA_REG_DATA);
|
uint16_t data = inw(port_base + ATA_REG_DATA);
|
||||||
(void)data; // We discard identity data for now, just checking presence
|
(void)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
// === RAMFS Implementation (Drive A:) ===
|
|
||||||
// We keep the original logic for Drive A to preserve existing OS functionality.
|
|
||||||
|
|
||||||
#define MAX_FILES 256
|
#define MAX_FILES 256
|
||||||
#define MAX_CLUSTERS 1024
|
#define MAX_CLUSTERS 1024
|
||||||
|
|
@ -149,17 +147,14 @@ static char parse_drive_from_path(const char **path_ptr) {
|
||||||
|
|
||||||
// Normalize path (remove .., ., etc)
|
// Normalize path (remove .., ., etc)
|
||||||
void fat32_normalize_path(const char *path, char *normalized) {
|
void fat32_normalize_path(const char *path, char *normalized) {
|
||||||
// Basic normalization
|
|
||||||
// If we have a drive letter, strip it for internal processing logic if needed,
|
|
||||||
// but the output 'normalized' should conceptually be the path *on that drive*.
|
|
||||||
|
|
||||||
char temp[FAT32_MAX_PATH];
|
char temp[FAT32_MAX_PATH];
|
||||||
int temp_len = 0;
|
int temp_len = 0;
|
||||||
const char *p = path;
|
const char *p = path;
|
||||||
char drive = parse_drive_from_path(&p);
|
char drive = parse_drive_from_path(&p);
|
||||||
|
|
||||||
// Initialize with current directory or root
|
|
||||||
// If drive changed, we assume root of that drive
|
|
||||||
if (p[0] == '/') {
|
if (p[0] == '/') {
|
||||||
fs_strcpy(temp, "/");
|
fs_strcpy(temp, "/");
|
||||||
temp_len = 1;
|
temp_len = 1;
|
||||||
|
|
@ -632,7 +627,6 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// Check if we want to create file
|
|
||||||
if ((mode[0] == 'w' || mode[0] == 'a') && *p == 0) {
|
if ((mode[0] == 'w' || mode[0] == 'a') && *p == 0) {
|
||||||
// Create file logic
|
// Create file logic
|
||||||
char dos_name[11];
|
char dos_name[11];
|
||||||
|
|
@ -1030,7 +1024,6 @@ static bool realfs_delete(char drive, const char *path) {
|
||||||
if (*p == 0) break; // End of path
|
if (*p == 0) break; // End of path
|
||||||
}
|
}
|
||||||
|
|
||||||
// We found the file entry - now delete it
|
|
||||||
|
|
||||||
// 1. Mark directory entry as deleted
|
// 1. Mark directory entry as deleted
|
||||||
uint8_t *entry_buf = (uint8_t*)kmalloc(512);
|
uint8_t *entry_buf = (uint8_t*)kmalloc(512);
|
||||||
|
|
@ -1090,16 +1083,11 @@ static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *e
|
||||||
}
|
}
|
||||||
FAT32_Volume *vol = &volumes[vol_idx];
|
FAT32_Volume *vol = &volumes[vol_idx];
|
||||||
|
|
||||||
// Find directory start cluster
|
|
||||||
// Reuse realfs_open logic basically to find the cluster
|
|
||||||
// but without creating a handle.
|
|
||||||
// For simplicity, just use realfs_open and then read the directory entries
|
|
||||||
FAT32_FileHandle *dir_handle = realfs_open(drive, path, "r");
|
FAT32_FileHandle *dir_handle = realfs_open(drive, path, "r");
|
||||||
if (!dir_handle) return 0;
|
if (!dir_handle) return 0;
|
||||||
|
|
||||||
// Extract start_cluster BEFORE closing the handle
|
|
||||||
uint32_t current_cluster = dir_handle->start_cluster;
|
uint32_t current_cluster = dir_handle->start_cluster;
|
||||||
// We don't use the handle for reading via realfs_read because directories are special
|
|
||||||
fat32_close(dir_handle); // Return to pool - this invalidates the handle
|
fat32_close(dir_handle); // Return to pool - this invalidates the handle
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
@ -1236,9 +1224,7 @@ void fat32_close(FAT32_FileHandle *handle) {
|
||||||
entry->start_cluster_high = (handle->start_cluster >> 16);
|
entry->start_cluster_high = (handle->start_cluster >> 16);
|
||||||
entry->start_cluster_low = (handle->start_cluster & 0xFFFF);
|
entry->start_cluster_low = (handle->start_cluster & 0xFFFF);
|
||||||
}
|
}
|
||||||
// Write back with error checking
|
|
||||||
if (d->write_sector(d, handle->dir_sector, buf) != 0) {
|
if (d->write_sector(d, handle->dir_sector, buf) != 0) {
|
||||||
// Write failed - at least we tried
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
|
|
@ -1506,26 +1492,13 @@ bool fat32_is_directory(const char *path) {
|
||||||
} else {
|
} else {
|
||||||
FAT32_FileHandle *fh = realfs_open(drive, p, "r");
|
FAT32_FileHandle *fh = realfs_open(drive, p, "r");
|
||||||
if (fh) {
|
if (fh) {
|
||||||
// Wait, open checks if file/dir.
|
|
||||||
// We need to check if what we opened is a directory.
|
|
||||||
// realfs_open returns handle for directory too if strictly reading?
|
|
||||||
// Actually my realfs_open logic tries to find the entry.
|
|
||||||
// If it returns a handle, how do we know if it was a dir?
|
|
||||||
// Handle doesn't store attributes.
|
|
||||||
// But we can check if size == 0 (often for dirs) or inferred from how we opened it.
|
|
||||||
// Better: use realfs_list_directory check or modify open to return attributes?
|
|
||||||
// For now, let's assume if we can open it and it has size 0 (or we opened root), it's dir.
|
|
||||||
// This is imperfect.
|
|
||||||
// Correct way: Add attributes to FAT32_FileHandle or separate check function.
|
|
||||||
// Let's rely on naming convention or just return false for now if not root.
|
|
||||||
if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) is_dir = true;
|
if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) is_dir = true;
|
||||||
else {
|
else {
|
||||||
// Hack: check if list_directory returns > 0 entries? No empty dirs exists.
|
|
||||||
// We need to improve realfs_open to store attr.
|
|
||||||
}
|
}
|
||||||
fat32_close(fh);
|
fat32_close(fh);
|
||||||
}
|
}
|
||||||
// Workaround: assume true if path ends in /? No.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
|
|
@ -1586,23 +1559,15 @@ bool fat32_chdir(const char *path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change dir on current drive
|
|
||||||
if (fat32_is_directory(path)) {
|
if (fat32_is_directory(path)) {
|
||||||
// Normalize and set
|
|
||||||
if (drive == 'A') {
|
if (drive == 'A') {
|
||||||
char normalized[FAT32_MAX_PATH];
|
char normalized[FAT32_MAX_PATH];
|
||||||
fat32_normalize_path(p, normalized);
|
fat32_normalize_path(p, normalized);
|
||||||
fs_strcpy(current_dir, normalized);
|
fs_strcpy(current_dir, normalized);
|
||||||
} else {
|
} else {
|
||||||
// For real drive, just store path
|
|
||||||
// Need a way to validate path exists on real drive first
|
|
||||||
// fat32_is_directory call above should suffice?
|
|
||||||
// But my realfs is_directory is weak.
|
|
||||||
fs_strcpy(current_dir, p);
|
fs_strcpy(current_dir, p);
|
||||||
// Ensure leading slash
|
|
||||||
if (current_dir[0] != '/') {
|
if (current_dir[0] != '/') {
|
||||||
// Prepend /
|
|
||||||
// ... (skip for brevity, assume absolute paths mostly)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
|
|
|
||||||
142
src/kernel/idt.c
142
src/kernel/idt.c
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern void serial_write(const char *str);
|
extern void serial_write(const char *str);
|
||||||
|
|
||||||
// Simple hex printer for debugging exceptions
|
// Simple hex printer for debugging
|
||||||
static void print_hex(uint64_t val) {
|
static void print_hex(uint64_t val) {
|
||||||
const char* digits = "0123456789ABCDEF";
|
const char* digits = "0123456789ABCDEF";
|
||||||
char buf[17];
|
char buf[17];
|
||||||
|
|
@ -19,40 +19,64 @@ static void print_hex(uint64_t val) {
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "cmd.h"
|
#include "cmd.h"
|
||||||
|
|
||||||
|
void kernel_panic(registers_t *regs, const char *error_name);
|
||||||
|
|
||||||
|
static const char *exception_messages[] = {
|
||||||
|
"Division By Zero",
|
||||||
|
"Debug",
|
||||||
|
"Non-Maskable Interrupt",
|
||||||
|
"Breakpoint",
|
||||||
|
"Into Detected Overflow",
|
||||||
|
"Out of Bounds",
|
||||||
|
"Invalid Opcode",
|
||||||
|
"No Coprocessor",
|
||||||
|
"Double Fault",
|
||||||
|
"Coprocessor Segment Overrun",
|
||||||
|
"Bad TSS",
|
||||||
|
"Segment Not Present",
|
||||||
|
"Stack Fault",
|
||||||
|
"General Protection Fault",
|
||||||
|
"Page Fault",
|
||||||
|
"Unknown Interrupt",
|
||||||
|
"Coprocessor Fault",
|
||||||
|
"Alignment Check",
|
||||||
|
"Machine Check",
|
||||||
|
"SIMD Floating-Point Exception",
|
||||||
|
"Virtualization Exception",
|
||||||
|
"Control Protection Exception",
|
||||||
|
"Reserved",
|
||||||
|
"Reserved",
|
||||||
|
"Reserved",
|
||||||
|
"Reserved",
|
||||||
|
"Reserved",
|
||||||
|
"Reserved",
|
||||||
|
"Hypervisor Injection Exception",
|
||||||
|
"VMM Communication Exception",
|
||||||
|
"Security Exception",
|
||||||
|
"Reserved"
|
||||||
|
};
|
||||||
|
|
||||||
uint64_t exception_handler_c(registers_t *regs) {
|
uint64_t exception_handler_c(registers_t *regs) {
|
||||||
uint64_t vector = regs->int_no;
|
uint64_t vector = regs->int_no;
|
||||||
uint64_t rip = regs->rip;
|
|
||||||
uint64_t cr2;
|
|
||||||
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
|
||||||
|
|
||||||
|
// Serial Mirror
|
||||||
serial_write("\n*** EXCEPTION ***\nVector: ");
|
serial_write("\n*** EXCEPTION ***\nVector: ");
|
||||||
print_hex(vector);
|
print_hex(vector);
|
||||||
serial_write("\nError Code: ");
|
|
||||||
print_hex(regs->err_code);
|
|
||||||
serial_write("\nRIP: ");
|
|
||||||
print_hex(rip);
|
|
||||||
serial_write("\nCR2: ");
|
|
||||||
print_hex(cr2);
|
|
||||||
|
|
||||||
// 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) {
|
if ((regs->cs & 0x3) != 0) {
|
||||||
serial_write("\nUSER MODE EXCEPTION - Terminating process.\n");
|
serial_write("\nUSER MODE EXCEPTION - Terminating process.\n");
|
||||||
cmd_write("USER MODE EXCEPTION - Terminating process.\n");
|
if (cmd_get_cursor_col() != 0) cmd_write("\n");
|
||||||
|
cmd_write("*** USER EXCEPTION ***\nVector: "); cmd_write_hex(vector);
|
||||||
|
cmd_write("\nRIP: "); cmd_write_hex(regs->rip);
|
||||||
|
cmd_write("\nTerminating process.\n");
|
||||||
return process_terminate_current();
|
return process_terminate_current();
|
||||||
}
|
}
|
||||||
|
|
||||||
serial_write("\nKERNEL PANIC - CPU HALTED.\n");
|
// Kernel mode exception
|
||||||
cmd_write("KERNEL PANIC - CPU HALTED.\n");
|
const char *name = (vector < 32) ? exception_messages[vector] : "Unknown Kernel Exception";
|
||||||
while(1) { asm volatile("cli; hlt"); }
|
kernel_panic(regs, name);
|
||||||
return (uint64_t)regs; // Unreachable but for completeness
|
|
||||||
|
return (uint64_t)regs; // Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IDT_ENTRIES 256
|
#define IDT_ENTRIES 256
|
||||||
|
|
@ -145,10 +169,72 @@ void idt_register_interrupts(void) {
|
||||||
idt_set_gate(44, isr12_wrapper, cs, 0x8E); // Mouse (IRQ 12)
|
idt_set_gate(44, isr12_wrapper, cs, 0x8E); // Mouse (IRQ 12)
|
||||||
|
|
||||||
// Exceptions
|
// Exceptions
|
||||||
extern void isr8_wrapper(void);
|
extern void exc0_wrapper(void);
|
||||||
extern void isr14_wrapper(void);
|
extern void exc1_wrapper(void);
|
||||||
idt_set_gate(8, isr8_wrapper, cs, 0x8E); // Double Fault
|
extern void exc2_wrapper(void);
|
||||||
idt_set_gate(14, isr14_wrapper, cs, 0x8E); // Page Fault
|
extern void exc3_wrapper(void);
|
||||||
|
extern void exc4_wrapper(void);
|
||||||
|
extern void exc5_wrapper(void);
|
||||||
|
extern void exc6_wrapper(void);
|
||||||
|
extern void exc7_wrapper(void);
|
||||||
|
extern void exc8_wrapper(void);
|
||||||
|
extern void exc9_wrapper(void);
|
||||||
|
extern void exc10_wrapper(void);
|
||||||
|
extern void exc11_wrapper(void);
|
||||||
|
extern void exc12_wrapper(void);
|
||||||
|
extern void exc13_wrapper(void);
|
||||||
|
extern void exc14_wrapper(void);
|
||||||
|
extern void exc15_wrapper(void);
|
||||||
|
extern void exc16_wrapper(void);
|
||||||
|
extern void exc17_wrapper(void);
|
||||||
|
extern void exc18_wrapper(void);
|
||||||
|
extern void exc19_wrapper(void);
|
||||||
|
extern void exc20_wrapper(void);
|
||||||
|
extern void exc21_wrapper(void);
|
||||||
|
extern void exc22_wrapper(void);
|
||||||
|
extern void exc23_wrapper(void);
|
||||||
|
extern void exc24_wrapper(void);
|
||||||
|
extern void exc25_wrapper(void);
|
||||||
|
extern void exc26_wrapper(void);
|
||||||
|
extern void exc27_wrapper(void);
|
||||||
|
extern void exc28_wrapper(void);
|
||||||
|
extern void exc29_wrapper(void);
|
||||||
|
extern void exc30_wrapper(void);
|
||||||
|
extern void exc31_wrapper(void);
|
||||||
|
|
||||||
|
idt_set_gate(0, exc0_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(1, exc1_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(2, exc2_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(3, exc3_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(4, exc4_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(5, exc5_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(6, exc6_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(7, exc7_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(8, exc8_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(9, exc9_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(10, exc10_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(11, exc11_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(12, exc12_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(13, exc13_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(14, exc14_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(15, exc15_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(16, exc16_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(17, exc17_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(18, exc18_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(19, exc19_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(20, exc20_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(21, exc21_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(22, exc22_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(23, exc23_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(24, exc24_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(25, exc25_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(26, exc26_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(27, exc27_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(28, exc28_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(29, exc29_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(30, exc30_wrapper, cs, 0x8E);
|
||||||
|
idt_set_gate(31, exc31_wrapper, cs, 0x8E);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void idt_load(void) {
|
void idt_load(void) {
|
||||||
|
|
|
||||||
|
|
@ -74,28 +74,66 @@ isr1_wrapper:
|
||||||
isr12_wrapper:
|
isr12_wrapper:
|
||||||
ISR_NOERRCODE mouse_handler, 44
|
ISR_NOERRCODE mouse_handler, 44
|
||||||
|
|
||||||
; Common exception macro
|
; Common exception macro for exceptions WITHOUT error code
|
||||||
%macro EXCEPTION_ERRCODE 1
|
%macro EXCEPTION_NOERRCODE 1
|
||||||
isr%1_wrapper:
|
global exc%1_wrapper
|
||||||
push %1
|
exc%1_wrapper:
|
||||||
|
push 0 ; Dummy error code
|
||||||
|
push %1 ; Vector
|
||||||
jmp exception_common
|
jmp exception_common
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
; Exception 8: Double Fault (has error code)
|
; Common exception macro for exceptions WITH error code
|
||||||
EXCEPTION_ERRCODE 8
|
%macro EXCEPTION_ERRCODE 1
|
||||||
|
global exc%1_wrapper
|
||||||
|
exc%1_wrapper:
|
||||||
|
push %1 ; Vector
|
||||||
|
jmp exception_common
|
||||||
|
%endmacro
|
||||||
|
|
||||||
; Exception 14: Page Fault (has error code)
|
; Define all 32 standard exceptions
|
||||||
EXCEPTION_ERRCODE 14
|
EXCEPTION_NOERRCODE 0 ; Divide Error
|
||||||
|
EXCEPTION_NOERRCODE 1 ; Debug
|
||||||
|
EXCEPTION_NOERRCODE 2 ; NMI
|
||||||
|
EXCEPTION_NOERRCODE 3 ; Breakpoint
|
||||||
|
EXCEPTION_NOERRCODE 4 ; Overflow
|
||||||
|
EXCEPTION_NOERRCODE 5 ; Bound Range Exceeded
|
||||||
|
EXCEPTION_NOERRCODE 6 ; Invalid Opcode
|
||||||
|
EXCEPTION_NOERRCODE 7 ; Device Not Available
|
||||||
|
EXCEPTION_ERRCODE 8 ; Double Fault
|
||||||
|
EXCEPTION_NOERRCODE 9 ; Coprocessor Segment Overrun
|
||||||
|
EXCEPTION_ERRCODE 10 ; Invalid TSS
|
||||||
|
EXCEPTION_ERRCODE 11 ; Segment Not Present
|
||||||
|
EXCEPTION_ERRCODE 12 ; Stack-Segment Fault
|
||||||
|
EXCEPTION_ERRCODE 13 ; General Protection Fault
|
||||||
|
EXCEPTION_ERRCODE 14 ; Page Fault
|
||||||
|
EXCEPTION_NOERRCODE 15 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 16 ; x87 Floating-Point Exception
|
||||||
|
EXCEPTION_ERRCODE 17 ; Alignment Check
|
||||||
|
EXCEPTION_NOERRCODE 18 ; Machine Check
|
||||||
|
EXCEPTION_NOERRCODE 19 ; SIMD Floating-Point Exception
|
||||||
|
EXCEPTION_NOERRCODE 20 ; Virtualization Exception
|
||||||
|
EXCEPTION_ERRCODE 21 ; Control Protection Exception
|
||||||
|
EXCEPTION_NOERRCODE 22 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 23 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 24 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 25 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 26 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 27 ; Reserved
|
||||||
|
EXCEPTION_NOERRCODE 28 ; Hypervisor Injection Exception
|
||||||
|
EXCEPTION_ERRCODE 29 ; VMM Communication Exception
|
||||||
|
EXCEPTION_ERRCODE 30 ; Security Exception
|
||||||
|
EXCEPTION_NOERRCODE 31 ; Reserved
|
||||||
|
|
||||||
exception_common:
|
exception_common:
|
||||||
; Save registers
|
; Save registers (registers_t structure)
|
||||||
push rax
|
push rax
|
||||||
|
push rbx
|
||||||
push rcx
|
push rcx
|
||||||
push rdx
|
push rdx
|
||||||
push rbx
|
|
||||||
push rbp
|
push rbp
|
||||||
push rsi
|
|
||||||
push rdi
|
push rdi
|
||||||
|
push rsi
|
||||||
push r8
|
push r8
|
||||||
push r9
|
push r9
|
||||||
push r10
|
push r10
|
||||||
|
|
@ -105,14 +143,15 @@ exception_common:
|
||||||
push r14
|
push r14
|
||||||
push r15
|
push r15
|
||||||
|
|
||||||
; Call C handler: uint64_t exception_handler_c(registers_t *regs)
|
; Pass current RSP as 1st argument (registers_t*)
|
||||||
mov rdi, rsp
|
mov rdi, rsp
|
||||||
|
|
||||||
call exception_handler_c
|
call exception_handler_c
|
||||||
|
|
||||||
; Switch stack if needed (for process termination)
|
; Switch stack if needed (for process termination)
|
||||||
mov rsp, rax
|
mov rsp, rax
|
||||||
|
|
||||||
; Restore (in case we want to return, but usually we halt if kernel)
|
; Restore registers
|
||||||
pop r15
|
pop r15
|
||||||
pop r14
|
pop r14
|
||||||
pop r13
|
pop r13
|
||||||
|
|
@ -121,12 +160,13 @@ exception_common:
|
||||||
pop r10
|
pop r10
|
||||||
pop r9
|
pop r9
|
||||||
pop r8
|
pop r8
|
||||||
pop rdi
|
|
||||||
pop rsi
|
pop rsi
|
||||||
|
pop rdi
|
||||||
pop rbp
|
pop rbp
|
||||||
pop rbx
|
|
||||||
pop rdx
|
pop rdx
|
||||||
pop rcx
|
pop rcx
|
||||||
|
pop rbx
|
||||||
pop rax
|
pop rax
|
||||||
add rsp, 16 ; drop vector and error code
|
add rsp, 16 ; drop vector and error code
|
||||||
iretq
|
iretq
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@ static void hcf(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple serial port initialization and output for debugging
|
|
||||||
static void init_serial() {
|
static void init_serial() {
|
||||||
outb(0x3F8 + 1, 0x00);
|
outb(0x3F8 + 1, 0x00);
|
||||||
outb(0x3F8 + 3, 0x80);
|
outb(0x3F8 + 3, 0x80);
|
||||||
|
|
@ -84,7 +83,6 @@ void kmain(void) {
|
||||||
platform_init();
|
platform_init();
|
||||||
serial_write("[DEBUG] platform_init OK\n");
|
serial_write("[DEBUG] platform_init OK\n");
|
||||||
|
|
||||||
// 1. Memory Detection and Heap Init
|
|
||||||
uint64_t heap_phys_addr = 0;
|
uint64_t heap_phys_addr = 0;
|
||||||
size_t heap_size = 0;
|
size_t heap_size = 0;
|
||||||
if (memmap_request.response != NULL) {
|
if (memmap_request.response != NULL) {
|
||||||
|
|
@ -99,7 +97,7 @@ void kmain(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heap_size > 512 * 1024 * 1024) heap_size = 512 * 1024 * 1024; // Cap at 512MB
|
if (heap_size > 512 * 1024 * 1024) heap_size = 512 * 1024 * 1024;
|
||||||
|
|
||||||
if (heap_phys_addr != 0) {
|
if (heap_phys_addr != 0) {
|
||||||
memory_manager_init_at((void*)p2v(heap_phys_addr), heap_size);
|
memory_manager_init_at((void*)p2v(heap_phys_addr), heap_size);
|
||||||
|
|
@ -109,7 +107,6 @@ void kmain(void) {
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Graphics Init
|
|
||||||
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
|
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
|
||||||
serial_write("[DEBUG] No framebuffer! Halting.\n");
|
serial_write("[DEBUG] No framebuffer! Halting.\n");
|
||||||
hcf();
|
hcf();
|
||||||
|
|
@ -119,19 +116,15 @@ void kmain(void) {
|
||||||
graphics_init(fb);
|
graphics_init(fb);
|
||||||
serial_write("[DEBUG] graphics_init OK\n");
|
serial_write("[DEBUG] graphics_init OK\n");
|
||||||
|
|
||||||
// 3. GDT & TSS Init
|
|
||||||
gdt_init();
|
gdt_init();
|
||||||
serial_write("[DEBUG] gdt_init OK\n");
|
serial_write("[DEBUG] gdt_init OK\n");
|
||||||
|
|
||||||
// 4. Paging Init
|
|
||||||
paging_init();
|
paging_init();
|
||||||
serial_write("[DEBUG] paging_init OK\n");
|
serial_write("[DEBUG] paging_init OK\n");
|
||||||
|
|
||||||
// 5. Syscall Init
|
|
||||||
syscall_init();
|
syscall_init();
|
||||||
serial_write("[DEBUG] syscall_init OK\n");
|
serial_write("[DEBUG] syscall_init OK\n");
|
||||||
|
|
||||||
// 6. Interrupts Init
|
|
||||||
idt_init();
|
idt_init();
|
||||||
idt_register_interrupts();
|
idt_register_interrupts();
|
||||||
idt_load();
|
idt_load();
|
||||||
|
|
@ -141,13 +134,11 @@ void kmain(void) {
|
||||||
|
|
||||||
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
||||||
|
|
||||||
// Initialize FAT32 RAMFS and mount Limine modules
|
|
||||||
fat32_init();
|
fat32_init();
|
||||||
if (module_request.response != NULL) {
|
if (module_request.response != NULL) {
|
||||||
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
|
for (uint64_t i = 0; i < module_request.response->module_count; i++) {
|
||||||
struct limine_file *mod = module_request.response->modules[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");
|
FAT32_FileHandle *fh = fat32_open(mod->path, "w");
|
||||||
if (fh && fh->valid) {
|
if (fh && fh->valid) {
|
||||||
fat32_write(fh, mod->address, mod->size);
|
fat32_write(fh, mod->address, mod->size);
|
||||||
|
|
@ -159,25 +150,20 @@ void kmain(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ENABLE_USER_TEST = 1; // Set to 1 to test User Mode ring 3 jump
|
int ENABLE_USER_TEST = 1;
|
||||||
#ifdef ENABLE_USER_TEST
|
#ifdef ENABLE_USER_TEST
|
||||||
process_init();
|
process_init();
|
||||||
// The desktop is PID 0
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 3. PS/2 Init (Mouse/Keyboard)
|
|
||||||
asm("cli");
|
asm("cli");
|
||||||
ps2_init();
|
ps2_init();
|
||||||
asm("sti");
|
asm("sti");
|
||||||
|
|
||||||
// 4. Window Manager Init (Draws initial desktop)
|
|
||||||
wm_init();
|
wm_init();
|
||||||
|
|
||||||
// Re-enable interrupts since we removed sti from idt_load
|
|
||||||
asm volatile("sti");
|
asm volatile("sti");
|
||||||
|
|
||||||
// 5. Main loop - just wait for interrupts
|
|
||||||
// Timer interrupt will drive the redraw system
|
|
||||||
while (1) {
|
while (1) {
|
||||||
wm_process_input();
|
wm_process_input();
|
||||||
wm_process_deferred_thumbs();
|
wm_process_deferred_thumbs();
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,9 @@ void memory_manager_init_at(void *pool_address, size_t pool_size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void memory_manager_init_with_size(size_t pool_size) {
|
void memory_manager_init_with_size(size_t pool_size) {
|
||||||
// This is now just a wrapper if init_at wasn't called.
|
|
||||||
// However, in BoredOS we now prefer explicit init_at.
|
|
||||||
if (initialized) return;
|
if (initialized) return;
|
||||||
// Fallback: we still need a buffer if no address is provided?
|
|
||||||
// Let's assume for now that BoredOS always calls init_at.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void memory_manager_init(void) {
|
void memory_manager_init(void) {
|
||||||
|
|
@ -183,7 +181,6 @@ void* kmalloc_aligned(size_t size, size_t alignment) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we can allocate
|
|
||||||
if (total_allocated + size > memory_pool_size) {
|
if (total_allocated + size > memory_pool_size) {
|
||||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ static void write_cr3(uint64_t cr3) {
|
||||||
|
|
||||||
// Helper to allocate a page table and clear it
|
// Helper to allocate a page table and clear it
|
||||||
static uint64_t alloc_page_table_phys(void) {
|
static uint64_t alloc_page_table_phys(void) {
|
||||||
// We allocate a page table in Virtual Memory (HHDM)
|
|
||||||
void* ptr = kmalloc(PAGE_SIZE * 2);
|
void* ptr = kmalloc(PAGE_SIZE * 2);
|
||||||
if (!ptr) return 0;
|
if (!ptr) return 0;
|
||||||
|
|
||||||
|
|
@ -41,8 +40,7 @@ static uint64_t alloc_page_table_phys(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void paging_init(void) {
|
void paging_init(void) {
|
||||||
// Limine sets up a highly mapped page table for us.
|
|
||||||
// CR3 contains the physical address of the PML4.
|
|
||||||
current_pml4_phys = read_cr3() & PT_ADDR_MASK;
|
current_pml4_phys = read_cr3() & PT_ADDR_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
116
src/kernel/panic.c
Normal file
116
src/kernel/panic.c
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include "process.h"
|
||||||
|
#include "graphics.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
extern void serial_write(const char *str);
|
||||||
|
|
||||||
|
static void draw_string_centered(int y, const char *s, uint32_t color) {
|
||||||
|
if (!s) return;
|
||||||
|
int len = 0;
|
||||||
|
while (s[len]) len++;
|
||||||
|
int x = (get_screen_width() - (len * 8)) / 2;
|
||||||
|
draw_string(x, y, s, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernel_panic(registers_t *regs, const char *error_name) {
|
||||||
|
// Disable interrupts to prevent nested panics
|
||||||
|
asm volatile("cli");
|
||||||
|
|
||||||
|
// Clear back buffer to black
|
||||||
|
graphics_clear_back_buffer(0x00000000);
|
||||||
|
|
||||||
|
int sh = get_screen_height();
|
||||||
|
int cy = sh / 2;
|
||||||
|
|
||||||
|
// Draw header
|
||||||
|
draw_string_centered(cy - 150, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", 0xFFFF0000);
|
||||||
|
draw_string_centered(cy - 130, "KERNEL EXCEPTION OCCURRED", 0xFFFFFFFF);
|
||||||
|
draw_string_centered(cy - 110, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", 0xFFFF0000);
|
||||||
|
|
||||||
|
// Error name
|
||||||
|
char err_buf[256];
|
||||||
|
int pos = 0;
|
||||||
|
const char *prefix = "Exception: ";
|
||||||
|
while(prefix[pos]) { err_buf[pos] = prefix[pos]; pos++; }
|
||||||
|
int i = 0;
|
||||||
|
while(error_name[i]) { err_buf[pos++] = error_name[i++]; }
|
||||||
|
err_buf[pos] = 0;
|
||||||
|
draw_string_centered(cy - 70, err_buf, 0xFFFFCC00);
|
||||||
|
|
||||||
|
// Details - simplified centering by drawing them as a block or individually centered
|
||||||
|
char info_buf[64];
|
||||||
|
|
||||||
|
// Vector
|
||||||
|
pos = 0;
|
||||||
|
prefix = "Vector: ";
|
||||||
|
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
|
||||||
|
uint64_t v = regs->int_no;
|
||||||
|
const char* digits = "0123456789ABCDEF";
|
||||||
|
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
|
||||||
|
for (int i = 15; i >= 0; i--) {
|
||||||
|
info_buf[pos + i] = digits[v & 0xF];
|
||||||
|
v >>= 4;
|
||||||
|
}
|
||||||
|
info_buf[pos + 16] = 0;
|
||||||
|
draw_string_centered(cy - 40, info_buf, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
// Error Code
|
||||||
|
pos = 0;
|
||||||
|
prefix = "Error Code: ";
|
||||||
|
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
|
||||||
|
v = regs->err_code;
|
||||||
|
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
|
||||||
|
for (int i = 15; i >= 0; i--) {
|
||||||
|
info_buf[pos + i] = digits[v & 0xF];
|
||||||
|
v >>= 4;
|
||||||
|
}
|
||||||
|
info_buf[pos + 16] = 0;
|
||||||
|
draw_string_centered(cy - 20, info_buf, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
// RIP
|
||||||
|
pos = 0;
|
||||||
|
prefix = "RIP: ";
|
||||||
|
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
|
||||||
|
v = regs->rip;
|
||||||
|
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
|
||||||
|
for (int i = 15; i >= 0; i--) {
|
||||||
|
info_buf[pos + i] = digits[v & 0xF];
|
||||||
|
v >>= 4;
|
||||||
|
}
|
||||||
|
info_buf[pos + 16] = 0;
|
||||||
|
draw_string_centered(cy, info_buf, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
// CR2 for page faults
|
||||||
|
if (regs->int_no == 14) {
|
||||||
|
uint64_t cr2;
|
||||||
|
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
pos = 0;
|
||||||
|
prefix = "CR2: ";
|
||||||
|
while(prefix[pos]) { info_buf[pos] = prefix[pos]; pos++; }
|
||||||
|
info_buf[pos++] = '0'; info_buf[pos++] = 'x';
|
||||||
|
for (int i = 15; i >= 0; i--) {
|
||||||
|
info_buf[pos + i] = digits[cr2 & 0xF];
|
||||||
|
cr2 >>= 4;
|
||||||
|
}
|
||||||
|
info_buf[pos + 16] = 0;
|
||||||
|
draw_string_centered(cy + 20, info_buf, 0xFFFF5555);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message
|
||||||
|
draw_string_centered(cy + 100, "The system has been halted to prevent damage.", 0xFFFFFFFF);
|
||||||
|
draw_string_centered(cy + 120, "Please restart your computer.", 0xFFAAAAAA);
|
||||||
|
|
||||||
|
|
||||||
|
// Flip buffer to screen
|
||||||
|
graphics_mark_screen_dirty();
|
||||||
|
graphics_flip_buffer();
|
||||||
|
|
||||||
|
serial_write("\n*** KERNEL PANIC ***\n");
|
||||||
|
serial_write(error_name);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
// Halt
|
||||||
|
while(1) {
|
||||||
|
asm volatile("cli; hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -336,8 +336,6 @@ uint64_t syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, u
|
||||||
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
|
||||||
|
|
||||||
if (win->pixels) {
|
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 - 20)) {
|
if (ux >= -100 && ux < win->w && uy >= -100 && uy < (win->h - 20)) {
|
||||||
graphics_set_render_target(win->pixels, win->w, win->h - 20);
|
graphics_set_render_target(win->pixels, win->w, win->h - 20);
|
||||||
draw_string(ux, uy, kernel_str, color);
|
draw_string(ux, uy, kernel_str, color);
|
||||||
|
|
|
||||||
|
|
@ -325,8 +325,6 @@ static void editor_paint(ui_window_t win) {
|
||||||
ui_draw_rounded_rect_filled(win, padding, status_y + 2, content_width, footer_h - 4, 6, COLOR_DARK_PANEL);
|
ui_draw_rounded_rect_filled(win, padding, status_y + 2, content_width, footer_h - 4, 6, COLOR_DARK_PANEL);
|
||||||
|
|
||||||
char status_text[128];
|
char status_text[128];
|
||||||
// Simple manual sprintf-like functionality for BoredOS userspace
|
|
||||||
// We'll just draw parts for now as before but cleaned up
|
|
||||||
ui_draw_string(win, padding + 15, status_y + 5, "Line:", COLOR_DKGRAY);
|
ui_draw_string(win, padding + 15, status_y + 5, "Line:", COLOR_DKGRAY);
|
||||||
|
|
||||||
char line_str[32];
|
char line_str[32];
|
||||||
|
|
|
||||||
|
|
@ -464,14 +464,9 @@ int vm_exec(const uint8_t *code, int code_size) {
|
||||||
|
|
||||||
vm_reset();
|
vm_reset();
|
||||||
|
|
||||||
// Reset Graphics Overlay
|
|
||||||
vm_rect_count = 0;
|
vm_rect_count = 0;
|
||||||
wm_custom_paint_hook = vm_paint_overlay;
|
wm_custom_paint_hook = vm_paint_overlay;
|
||||||
|
|
||||||
// Copy data segment if any?
|
|
||||||
// For this simple VM, we will copy the ENTIRE bytecode to the start of memory
|
|
||||||
// So code and data share the same space (Von Neumann)
|
|
||||||
// But we need to be careful not to overwrite running code if we write to it
|
|
||||||
|
|
||||||
// Safety check
|
// Safety check
|
||||||
if (code_size > VM_MEMORY_SIZE) {
|
if (code_size > VM_MEMORY_SIZE) {
|
||||||
|
|
@ -489,13 +484,7 @@ int vm_exec(const uint8_t *code, int code_size) {
|
||||||
while (pc < code_size) {
|
while (pc < code_size) {
|
||||||
uint8_t op = memory[pc++];
|
uint8_t op = memory[pc++];
|
||||||
|
|
||||||
// DEBUGGING
|
|
||||||
/*
|
|
||||||
cmd_write("PC:"); cmd_write_int(pc-1);
|
|
||||||
cmd_write(" OP:"); cmd_write_int(op);
|
|
||||||
cmd_write(" SP:"); cmd_write_int(sp);
|
|
||||||
cmd_write("\n");
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case OP_HALT:
|
case OP_HALT:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue