add /IGNORE:4216 to HAL link step, add writing and directory listing to the FAT32 driver, add the concept of current working directory to the VFS, add writing to the VFS, more operations in the shell, rename KeCurrentThread to KeGetCurrentThread and add KeGetCurrentProcess, add STATUS_NO_MORE_FILES, change KeFault* to KePanic, add syscalls for writing to files, add a proper syscall to writing to the console, move Screen.Clear to after memory status debug print, add ObjectTypeDir to OBJECT_TYPE, extend kernel32 by file operations and upgrade user32 (and add gdi32) to match windows a bit more closely, change CreateProcessA to accept the first argument as NULL.

This commit is contained in:
maksw committed 2026-05-04 16:27:49 +02:00
1 parent a5f0acfac8
commit 8c7564d5e1
33 files changed
+2337 -423

No files matched your search

Binary file not shown.
+1 -1
View File
@@ -54,7 +54,7 @@ for %%f in (%SRC%\hal\*.c) do (
:: ---- Link hal.dll ----
echo Linking hal.dll...
link %LFLAGS% /DLL /OUT:"bin\hal.dll" /ENTRY:HalInitialize obj\printf.obj obj\libc.obj obj\hal\*.obj || exit /b 1
link %LFLAGS% /DLL /OUT:"bin\hal.dll" /ENTRY:HalInitialize /IGNORE:4216 obj\printf.obj obj\libc.obj obj\hal\*.obj || exit /b 1
:: ---- Compile kernel ----
echo Compiling kernel...
+810 -169
View File
File diff suppressed because it is too large. Load diff
+283 -16
View File
@@ -1,6 +1,7 @@
// io_file.c - VFS dispatch layer
#include <string.h>
#include <ke_list.h>
#include <ke_process.h>
#include <io_file.h>
#include <mm_virt.h>
#include <ob_object.h>
@@ -14,6 +15,142 @@ typedef struct {
static VOLUME_ENTRY gVolumes[MAX_DRIVES];
// ---------------------------------------------------------------------------
// Current Working Directory (drive 0 relative, no trailing backslash)
// e.g. "" = root, "apps" = root\apps, "apps\\data" = nested
// ---------------------------------------------------------------------------
#define CWD_MAX 512
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
if (!buf || size == 0) return STATUS_INVALID_PARAMETER;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
size_t cwdLen = strlen(proc->CurrentDirectory);
if (cwdLen + 1 > size) {
KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_BUFFER_TOO_SMALL;
}
memcpy(buf, proc->CurrentDirectory, cwdLen + 1);
KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_SUCCESS;
}
// Resolve a possibly-relative path component against the CWD.
// Writes the fully-resolved, drive-stripped relative path into 'resolved'
// (caller supplies buf of at least CWD_MAX bytes).
// Returns the drive index.
static uint8_t ResolvePath(const char* path, char* resolved, size_t resolvedSize) {
// Strip explicit drive prefix "N:\"
uint8_t drive = 0;
if (path[0] >= '0' && path[0] <= '7' &&
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
drive = (uint8_t)(path[0] - '0');
path = path + 3;
}
char work[CWD_MAX * 2];
work[0] = '\0';
// Initialize our working base
if (path[0] == '\\' || path[0] == '/') {
// Absolute path: start from the root (empty work buffer)
// The segment loop below will automatically skip the leading separators.
} else {
// Relative path: start from the current process's CWD
PROCESS* proc = KeGetCurrentProcess();
if (proc) {
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
size_t wlen = strlen(proc->CurrentDirectory);
if (wlen >= sizeof(work)) wlen = sizeof(work) - 1;
memcpy(work, proc->CurrentDirectory, wlen);
work[wlen] = '\0';
KeReleaseSpinlock(&proc->CwdLock, flags);
}
}
// Segment-by-segment canonicalization
const char* p = path;
while (*p) {
// Skip separators
while (*p == '\\' || *p == '/') p++;
if (!*p) break;
// Read next component
char comp[256];
int clen = 0;
while (*p && *p != '\\' && *p != '/' && clen < 255)
comp[clen++] = *p++;
comp[clen] = '\0';
if (clen == 0) continue;
if (strcmp(comp, ".") == 0) {
// Stay in place
} else if (strcmp(comp, "..") == 0) {
// Remove last component from work
int i = (int)strlen(work) - 1;
while (i >= 0 && work[i] != '\\' && work[i] != '/') i--;
if (i < 0) i = 0;
work[i] = '\0';
} else {
// Append separator + component
size_t wl = strlen(work);
if (wl > 0 && work[wl-1] != '\\' && work[wl-1] != '/') {
if (wl + 1 < sizeof(work)) { work[wl] = '\\'; work[wl+1] = '\0'; }
}
size_t wl2 = strlen(work);
if (wl2 + clen + 1 < sizeof(work)) {
memcpy(work + wl2, comp, clen);
work[wl2 + clen] = '\0';
}
}
}
// Output the final canonicalized, drive-stripped path
size_t finalLen = strlen(work);
if (finalLen + 1 > resolvedSize) finalLen = resolvedSize - 1;
memcpy(resolved, work, finalLen);
resolved[finalLen] = '\0';
return drive;
}
__declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED;
// Resolve the new path (this also validates ".." navigation).
// Note: ResolvePath already uses proc->CurrentDirectory internally!
char resolved[CWD_MAX];
uint8_t drive = ResolvePath(path, resolved, sizeof(resolved));
// Validate: try to open the directory to confirm it actually exists on disk
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
void* dirCtx = NULL;
KSTATUS s = fs->OpenDir(fs, resolved, &dirCtx);
if (!KSUCCESS(s)) return s;
fs->CloseDir(fs, dirCtx);
// Commit the new CWD to the process block
size_t len = strlen(resolved);
if (len + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
memcpy(proc->CurrentDirectory, resolved, len + 1);
KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// Registration
// ---------------------------------------------------------------------------
@@ -29,20 +166,6 @@ __declspec(dllexport) KSTATUS IoRegisterFilesystem(uint8_t volumeIndex,
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// Path parsing - "0:\apps\shell.exe" or just "apps\shell.exe" (drive 0)
// ---------------------------------------------------------------------------
static uint8_t ParseDrive(const char* path, const char **pathOut) {
// Accept "N:\..." prefix, otherwise default to drive 0.
if (path[0] >= '0' && path[0] <= '7' &&
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
*pathOut = path + 3;
return (uint8_t)(path[0] - '0');
}
*pathOut = path;
return 0;
}
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
@@ -57,8 +180,8 @@ static void FileDestroy(OBJECT* obj) {
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
if (!path || !out) return STATUS_INVALID_PARAMETER;
const char* relPath;
uint8_t drive = ParseDrive(path, &relPath);
char relPath[CWD_MAX];
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
@@ -97,6 +220,90 @@ __declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf, size_t si
return s;
}
// ---------------------------------------------------------------------------
// IoWriteFile - sequential write at current offset
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoWriteFile(FILE_OBJECT* file, const void* buf,
size_t size, size_t* bytesWritten) {
if (!file || !buf || !bytesWritten) return STATUS_INVALID_PARAMETER;
if (!file->Fs->Write) return STATUS_NOT_SUPPORTED;
size_t written = 0;
KSTATUS s = file->Fs->Write(file->Fs, file->FileCtx,
file->Offset, buf, size, &written);
if (KSUCCESS(s)) {
file->Offset += written;
// Keep the cached size in sync if the file grew.
if (file->Offset > file->Size)
file->Size = file->Offset;
}
*bytesWritten = written;
return s;
}
// ---------------------------------------------------------------------------
// IoCreateFile - create a new file and return it open for writing
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out) {
if (!path || !out) return STATUS_INVALID_PARAMETER;
char relPath[CWD_MAX];
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Create) return STATUS_NOT_SUPPORTED;
void* fileCtx;
KSTATUS s = fs->Create(fs, relPath, &fileCtx);
if (!KSUCCESS(s)) return s;
FILE_OBJECT* f = (FILE_OBJECT*)ObAllocateObject(ObjectTypeFile,
sizeof(FILE_OBJECT));
if (!f) { fs->Close(fs, fileCtx); return STATUS_NO_MEMORY; }
f->Header.Destroy = FileDestroy;
f->Fs = fs;
f->FileCtx = fileCtx;
f->Size = 0;
f->Offset = 0;
*out = f;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// IoDeleteFile - delete a file by path (does not require an open handle)
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoDeleteFile(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
char relPath[CWD_MAX];
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Delete) return STATUS_NOT_SUPPORTED;
return fs->Delete(fs, relPath);
}
// ---------------------------------------------------------------------------
// IoTruncateFile - resize the file; updates the cached Size on success
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize) {
if (!file) return STATUS_INVALID_PARAMETER;
if (!file->Fs->Truncate) return STATUS_NOT_SUPPORTED;
KSTATUS s = file->Fs->Truncate(file->Fs, file->FileCtx, newSize);
if (KSUCCESS(s)) {
file->Size = newSize;
// Pull the file pointer back if it now sits past EOF.
if (file->Offset > file->Size)
file->Offset = file->Size;
}
return s;
}
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset) {
if (!file) return STATUS_INVALID_PARAMETER;
@@ -111,3 +318,63 @@ __declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file) {
__declspec(dllexport) void IoCloseFile(FILE_OBJECT* file) {
if (file) ObDereferenceObject(&file->Header);
}
// ---------------------------------------------------------------------------
// Directory enumeration
// ---------------------------------------------------------------------------
static void DirDestroy(OBJECT* obj) {
DIR_OBJECT* d = (DIR_OBJECT*)obj; // Header is first member
if (d->Fs && d->DirCtx) d->Fs->CloseDir(d->Fs, d->DirCtx);
}
__declspec(dllexport) KSTATUS IoOpenDir(const char* dirPath, DIR_OBJECT** out) {
if (!out) return STATUS_INVALID_PARAMETER;
// NULL or empty path means the CWD itself
char relPath[CWD_MAX];
uint8_t drive;
if (!dirPath || dirPath[0] == '\0') {
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
memcpy(relPath, proc->CurrentDirectory, strlen(proc->CurrentDirectory) + 1);
KeReleaseSpinlock(&proc->CwdLock, flags);
// Note: For now, this assumes drive 0. If you add multi-drive support
// later, you should add a CurrentDrive field to the PROCESS struct.
drive = 0;
} else {
drive = ResolvePath(dirPath, relPath, sizeof(relPath));
}
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
void* dirCtx = NULL;
KSTATUS s = fs->OpenDir(fs, relPath, &dirCtx);
if (!KSUCCESS(s)) return s;
DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT));
if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; }
d->Header.Destroy = DirDestroy;
d->Fs = fs;
d->DirCtx = dirCtx;
*out = d;
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS IoReadDir(DIR_OBJECT* dir, KDIR_ENTRY* entryOut) {
if (!dir || !entryOut) return STATUS_INVALID_PARAMETER;
if (!dir->Fs->ReadDir) return STATUS_NOT_SUPPORTED;
return dir->Fs->ReadDir(dir->Fs, dir->DirCtx, entryOut);
}
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir) {
if (dir) ObDereferenceObject(&dir->Header);
}
+72
View File
@@ -7,6 +7,18 @@
#include <ke_status.h>
#include <ob_object.h>
// ---------------------------------------------------------------------------
// KDIR_ENTRY - one directory entry returned by ReadDir / SYS_READ_DIR
// Attribute flags match FAT32/Win32: ATTR_DIRECTORY=0x10, ATTR_ARCHIVE=0x20
// ---------------------------------------------------------------------------
#define KDIR_NAME_MAX 260
typedef struct KDIR_ENTRY {
char Name[KDIR_NAME_MAX]; // null-terminated filename
uint8_t Attr; // FAT attribute byte (0x10=dir, 0x20=file)
uint32_t FileSize; // 0 for directories
} KDIR_ENTRY;
// ---------------------------------------------------------------------------
// Filesystem driver vtable - filled in by fat32.sys (or any future fs driver)
// ---------------------------------------------------------------------------
@@ -22,6 +34,21 @@ typedef struct IO_FILESYSTEM {
uint64_t offset, void* buf,
size_t size, size_t* bytesRead);
KSTATUS (*Write)(struct IO_FILESYSTEM* fs, void* fileCtx,
uint64_t offset, const void* buf,
size_t size, size_t* bytesWritten);
// Create a new file at 'path'; returns an open fileCtx on success.
KSTATUS (*Create)(struct IO_FILESYSTEM* fs, const char* path,
void** fileCtx);
// Delete the file at 'path'.
KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, const char* path);
// Truncate or extend the file to exactly 'newSize' bytes.
KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx,
uint64_t newSize);
// Return the file size in bytes.
uint64_t (*GetSize)(struct IO_FILESYSTEM* fs, void* fileCtx);
@@ -30,6 +57,24 @@ typedef struct IO_FILESYSTEM {
// Optional driver-private data.
void* DriverCtx;
// ---------------------------------------------------------------------------
// Directory enumeration (optional - may be NULL if not supported)
// ---------------------------------------------------------------------------
// Open a directory iterator for 'dirPath' (relative or drive-prefixed).
// Returns an opaque per-iteration driver cookie in *dirCtx.
KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs,
const char* dirPath, void** dirCtx);
// Advance to the next entry.
// Fills 'out' with name, attributes, and size.
// Returns STATUS_NO_MORE_FILES when the directory is exhausted.
KSTATUS (*ReadDir)(struct IO_FILESYSTEM* fs, void* dirCtx,
struct KDIR_ENTRY* out);
// Release resources allocated by OpenDir.
void (*CloseDir)(struct IO_FILESYSTEM* fs, void* dirCtx);
} IO_FILESYSTEM;
// ---------------------------------------------------------------------------
@@ -43,6 +88,15 @@ typedef struct FILE_OBJECT {
uint64_t Offset; // current position for sequential reads
} FILE_OBJECT;
// ---------------------------------------------------------------------------
// DIR_OBJECT - kernel object for an open directory iterator
// ---------------------------------------------------------------------------
typedef struct DIR_OBJECT {
OBJECT Header; // must be first
IO_FILESYSTEM* Fs;
void* DirCtx; // driver cookie (opaque iteration state)
} DIR_OBJECT;
typedef struct IO_VOLUME_PARAMS {
uint8_t DiskIndex; // which disk in HAL_DISK table
uint64_t PartitionLba; // starting LBA of this partition
@@ -61,6 +115,24 @@ __declspec(dllexport) KSTATUS IoRegisterFilesystem(uint8_t driveIndex, IO_FILESY
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out);
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf,
size_t size, size_t* bytesRead);
__declspec(dllexport) KSTATUS IoWriteFile (FILE_OBJECT* file, const void* buf,
size_t size, size_t* bytesWritten);
__declspec(dllexport) KSTATUS IoCreateFile (const char* path, FILE_OBJECT** out);
__declspec(dllexport) KSTATUS IoDeleteFile (const char* path);
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
__declspec(dllexport) void IoCloseFile(FILE_OBJECT* file);
// ---------------------------------------------------------------------------
// Directory enumeration API
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoOpenDir (const char* dirPath, DIR_OBJECT** out);
__declspec(dllexport) KSTATUS IoReadDir (DIR_OBJECT* dir, KDIR_ENTRY* entryOut);
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir);
// ---------------------------------------------------------------------------
// Current Working Directory
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoSetCwd(const char* path);
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size);
+9
View File
@@ -53,3 +53,12 @@ __declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
// Halt it!
while (true) __halt();
}
// KePrintConsole - write user-visible console text in white-on-black.
// Used by SYS_WRITE_CONSOLE so user programs get a distinct colour from
// kernel debug output (green) and panic output (red).
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len) {
if (!buf || !PrintChar) return;
for (size_t i = 0; i < len; i++)
PrintChar(buf[i], 0xFFFFFF, 0x000000);
}
+1
View File
@@ -5,6 +5,7 @@
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
__declspec(dllexport) void KePanic(const char *fmt, ...);
#endif // KE_DEBUG_H
+1 -1
View File
@@ -55,7 +55,7 @@ __declspec(dllexport) void KeClearEvent(EVENT* event) {
__declspec(dllexport) KSTATUS KeWaitForEvent(EVENT* event) {
if (!event) return STATUS_INVALID_PARAMETER;
THREAD* self = KeCurrentThread();
THREAD* self = KeGetCurrentThread();
while (true) {
uintptr_t flags = KeAcquireSpinlock(&event->Lock);
+2 -2
View File
@@ -22,7 +22,7 @@ KSTATUS MutexCreate(MUTEX **out) {
KSTATUS KeAcquireMutex(MUTEX* mutex) {
if (!mutex) return STATUS_INVALID_PARAMETER;
THREAD* self = KeCurrentThread();
THREAD* self = KeGetCurrentThread();
while (true) {
uintptr_t flags = KeAcquireSpinlock(&mutex->Lock);
@@ -39,7 +39,7 @@ KSTATUS KeAcquireMutex(MUTEX* mutex) {
KSTATUS KeTryAcquireMutex(MUTEX* mutex) {
if (!mutex) return STATUS_INVALID_PARAMETER;
THREAD* self = KeCurrentThread();
THREAD* self = KeGetCurrentThread();
uintptr_t flags = KeAcquireSpinlock(&mutex->Lock);
if (!mutex->Owner) {
mutex->Owner = self;
+9
View File
@@ -3,6 +3,7 @@
#include <intrin.h>
#include <ke_process.h>
#include <ke_thread.h>
#include <ke_sched.h>
#include <ke_event.h>
#include <mm_virt.h>
#include <ob_object.h>
@@ -27,6 +28,14 @@ LIST_ENTRY* KeGetProcessList(void) {
return &gProcessList;
}
PROCESS* KeGetCurrentProcess(void) {
THREAD* currentThread = KeGetCurrentThread();
if (currentThread) {
return currentThread->Owner;
}
return NULL;
}
// ---------------------------------------------------------------------------
// Destructor (called by ObDereferenceObject when RefCount hits zero)
// ---------------------------------------------------------------------------
+5 -1
View File
@@ -16,6 +16,9 @@ typedef struct PROCESS {
uint32_t ProcessId;
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
char CurrentDirectory[512];
SPINLOCK CwdLock;
/* Thread list. */
LIST_ENTRY ThreadList; /* links THREAD.ProcessLink */
uint32_t ThreadCount;
@@ -23,7 +26,7 @@ typedef struct PROCESS {
/* Handle table.
Index 0 is always NULL (the invalid handle).
Entries are OBJECT pointers, each holding a reference. */
OBJECT* Handles[HANDLE_TABLE_SIZE];
OBJECT* Handles[HANDLE_TABLE_SIZE];
SPINLOCK HandleLock;
int ExitCode;
@@ -46,3 +49,4 @@ OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
/* Return the global process list head (kernel-internal). */
LIST_ENTRY* KeGetProcessList(void);
PROCESS* KeGetCurrentProcess(void);
+2 -2
View File
@@ -22,7 +22,7 @@ KSTATUS QueueCreate(QUEUE **out) {
KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, bool blocking) {
if (!queue || !msg) return STATUS_INVALID_PARAMETER;
THREAD* self = KeCurrentThread();
THREAD* self = KeGetCurrentThread();
while (true) {
uintptr_t flags = KeAcquireSpinlock(&queue->Lock);
@@ -54,7 +54,7 @@ KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, bool blocking) {
KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, bool blocking) {
if (!queue || !msg) return STATUS_INVALID_PARAMETER;
THREAD* self = KeCurrentThread();
THREAD* self = KeGetCurrentThread();
while (true) {
uintptr_t flags = KeAcquireSpinlock(&queue->Lock);
+3 -3
View File
@@ -17,8 +17,8 @@ static THREAD* gIdleThread;
static THREAD* gOutgoingThread;
static uint32_t gAgingCounter = 0;
THREAD* KeCurrentThread(void) { return gCurrentThread; }
void KeSetCurrentThread(THREAD* t){ gCurrentThread = t; }
THREAD* KeGetCurrentThread(void) { return gCurrentThread; }
void KeSetCurrentThread(THREAD* t) { gCurrentThread = t; }
static void IdleLoop(void) {
while (true) { __halt(); }
@@ -60,7 +60,7 @@ void KeInitializeScheduler(void) {
// Safely release the lock and clean up any dead threads natively post-switch
void KeSchedulerPostSwitch(void) {
THREAD* cur = KeCurrentThread();
THREAD* cur = KeGetCurrentThread();
KeReleaseSpinlock(&gSchedLock, cur->SchedLockFlags);
if (gOutgoingThread && gOutgoingThread->State == ThreadDead) {
+1 -1
View File
@@ -19,7 +19,7 @@ void KeSchedulerTick(void);
__declspec(dllexport) void KeYield(void);
// Returns the thread currently running on this CPU (reads GS:[0]).
THREAD* KeCurrentThread(void);
THREAD* KeGetCurrentThread(void);
// Set the current-thread GS slot. Called once per CPU at boot.
void KeSetCurrentThread(THREAD* thread);
+1 -1
View File
@@ -23,7 +23,7 @@ __declspec(dllexport) KSTATUS KeSemaphoreCreate(int32_t initial, int32_t max, SE
__declspec(dllexport) KSTATUS KeWaitSemaphore(SEMAPHORE* sem) {
if (!sem) return STATUS_INVALID_PARAMETER;
THREAD* self = KeCurrentThread();
THREAD* self = KeGetCurrentThread();
while (true) {
uintptr_t flags = KeAcquireSpinlock(&sem->Lock);
+3
View File
@@ -24,6 +24,7 @@ typedef uint32_t KSTATUS;
#define STATUS_ACCESS_DENIED ((KSTATUS)0x0021)
#define STATUS_NOT_FOUND ((KSTATUS)0x0022)
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x0023)
#define STATUS_OBJECT_NAME_COLLISION ((KSTATUS)0x0024)
// --- State & Lifecycle (0x0030) ---
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0030)
@@ -33,4 +34,6 @@ typedef uint32_t KSTATUS;
// --- Hardware & External (0x0040) ---
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0040)
#define STATUS_NO_MORE_FILES ((KSTATUS)0x0050)
#define KSUCCESS(s) ((s) == STATUS_SUCCESS)
+2 -2
View File
@@ -76,7 +76,7 @@ KeSwitchContext ENDP
; We load RCX from the stack (first arg per MS x64), then IRETQ.
; -------------------------------------------------------------------------
EXTERN KeSchedulerPostSwitch : PROC
EXTERN KeCurrentThread : PROC
EXTERN KeGetCurrentThread : PROC
EXTERN KeExitThread : PROC
KeThreadEntry PROC
@@ -115,7 +115,7 @@ KeKernelThreadEntry PROC
; Thread function returned -- exit cleanly.
sub rsp, 32
call KeCurrentThread ; rax = current THREAD*
call KeGetCurrentThread ; rax = current THREAD*
add rsp, 32
mov rcx, rax ; arg1 = thread*
xor edx, edx ; arg2 = exitCode 0
+4 -13
View File
@@ -39,7 +39,7 @@ KeSyscallEntry ENDP
; ---------------------------------------------------------------------------
; Fault stubs - print a message and halt. A real kernel would do a bugcheck.
; ---------------------------------------------------------------------------
EXTERN DbgPrint:PROC
EXTERN KePanic:PROC
FaultMsg DB "KERNEL FAULT: vector %d at RIP %016llx", 0Ah, 0
@@ -48,10 +48,7 @@ KeFaultDE PROC
lea rcx, FaultMsg
mov rdx, 0
pop r8 ; RIP from the fault frame
call DbgPrint
add rsp, 28h
@@: hlt
jmp @B
call KePanic
KeFaultDE ENDP
KeFaultGP PROC
@@ -60,10 +57,7 @@ KeFaultGP PROC
mov rdx, 13
pop rax ; error code
pop r8 ; RIP
call DbgPrint
add rsp, 28h
@@: hlt
jmp @B
call KePanic
KeFaultGP ENDP
KeFaultPF PROC
@@ -72,10 +66,7 @@ KeFaultPF PROC
mov rdx, 14
pop rax ; error code
pop r8 ; RIP
call DbgPrint
add rsp, 28h
@@: hlt
jmp @B
call KePanic
KeFaultPF ENDP
EXTERN KeIrqDispatch:PROC
+185 -21
View File
@@ -58,7 +58,7 @@ void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; }
// Helper: get the current process, or NULL if unavailable.
// ---------------------------------------------------------------------------
static inline PROCESS* CurrentProcess(void) {
THREAD* t = KeCurrentThread();
THREAD* t = KeGetCurrentThread();
return t ? t->Owner : NULL;
}
@@ -102,7 +102,7 @@ static KSTATUS AllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) {
static uint64_t SyscallExitProcess(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
THREAD* t = KeCurrentThread();
THREAD* t = KeGetCurrentThread();
PROCESS* proc = t ? t->Owner : NULL;
if (proc) KeExitProcess(proc, (int)a0);
if (t) KeExitThread(t, (int)a0);
@@ -115,7 +115,7 @@ static uint64_t SyscallExitProcess(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallExitThread(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
THREAD* t = KeCurrentThread();
THREAD* t = KeGetCurrentThread();
if (t) KeExitThread(t, (int)a0);
while (true) ;
}
@@ -144,7 +144,7 @@ static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallGetCurrentTid(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
THREAD* t = KeCurrentThread();
THREAD* t = KeGetCurrentThread();
return t ? t->ThreadId : 0;
}
@@ -188,7 +188,7 @@ static uint64_t SyscallSetThreadPriority(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (a0 >= PriorityCount) return STATUS_INVALID_PARAMETER;
THREAD* t = KeCurrentThread();
THREAD* t = KeGetCurrentThread();
if (!t) return STATUS_FAILURE;
t->Priority = (THREAD_PRIORITY)a0;
return STATUS_SUCCESS;
@@ -492,6 +492,78 @@ static uint64_t SyscallReadFile(uint64_t a0, uint64_t a1, uint64_t a2,
return s;
}
// ---------------------------------------------------------------------------
// SYS_WRITE_FILE (43) a0=handle a1=buf a2=size a3=*bytesWrittenOut
// ---------------------------------------------------------------------------
static uint64_t SyscallWriteFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a4; (void)a5;
if (!a1) return STATUS_INVALID_PARAMETER;
FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile);
if (!file) return STATUS_NOT_FOUND;
size_t written = 0;
KSTATUS s = IoWriteFile(file, (const void*)(uintptr_t)a1, (size_t)a2, &written);
if (a3) *(size_t*)(uintptr_t)a3 = written;
ObDereferenceObject(&file->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_CREATE_FILE (44) a0=*path a1=*handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallCreateFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
if (!a0 || !a1) return STATUS_INVALID_PARAMETER;
FILE_OBJECT* file = NULL;
KSTATUS s = IoCreateFile((const char*)(uintptr_t)a0, &file);
if (!KSUCCESS(s)) return s;
uint32_t h = 0;
s = AllocHandleForCurrent(&file->Header, &h);
if (!KSUCCESS(s)) {
ObDereferenceObject(&file->Header);
return s;
}
*(uint32_t*)(uintptr_t)a1 = h;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_DELETE_FILE (45) a0=*path
// ---------------------------------------------------------------------------
static uint64_t SyscallDeleteFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
if (!a0) return STATUS_INVALID_PARAMETER;
return IoDeleteFile((const char*)(uintptr_t)a0);
}
// ---------------------------------------------------------------------------
// SYS_TRUNCATE_FILE (46) a0=handle a1=newSize
// ---------------------------------------------------------------------------
static uint64_t SyscallTruncateFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile);
if (!file) return STATUS_NOT_FOUND;
KSTATUS s = IoTruncateFile(file, (uint64_t)a1);
ObDereferenceObject(&file->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_GET_FILE_POS (47) a0=handle -> current seek offset
// ---------------------------------------------------------------------------
static uint64_t SyscallGetFilePos(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
FILE_OBJECT* file = (FILE_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeFile);
if (!file) return (uint64_t)-1;
uint64_t pos = file->Offset;
ObDereferenceObject(&file->Header);
return pos;
}
// ---------------------------------------------------------------------------
// SYS_SEEK_FILE (29) a0=handle a1=offset
// ---------------------------------------------------------------------------
@@ -704,6 +776,88 @@ static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2,
return s;
}
// ---------------------------------------------------------------------------
// SYS_WRITE_CONSOLE (48) a0=*buf a1=len
// Writes user console text in white; distinct from green SYS_DEBUG_PRINT.
// ---------------------------------------------------------------------------
static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
const char* buf = (const char*)(uintptr_t)a0;
size_t len = (size_t)a1;
if (buf && len) KePrintConsole(buf, len);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_OPEN_DIR (49) a0=*dirPath a1=*handleOut
// ---------------------------------------------------------------------------
static uint64_t SyscallOpenDir(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
const char* dirPath = (const char*)(uintptr_t)a0;
uint32_t* handleOut = (uint32_t*)(uintptr_t)a1;
if (!handleOut) return STATUS_INVALID_PARAMETER;
DIR_OBJECT* dir = NULL;
KSTATUS s = IoOpenDir(dirPath, &dir);
if (!KSUCCESS(s)) return s;
// Put the object in the handle table and get a valid index (1-255)
s = KeAllocateHandle(KeGetCurrentProcess(), &dir->Header, handleOut);
// Drop the reference KeAllocateHandle took, so that when user space
// calls CloseHandle, the refcount drops to 0 and the object is destroyed.
ObDereferenceObject(&dir->Header);
return s;
}
// ---------------------------------------------------------------------------
// SYS_READ_DIR (50) a0=handle a1=*KDIR_ENTRY
// Returns STATUS_NO_MORE_FILES when the directory is exhausted.
// ---------------------------------------------------------------------------
static uint64_t SyscallReadDir(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
// a0 = handle from user mode
// a1 = KDIR_ENTRY* buffer
// Look up a DIR_OBJECT using ObjectTypeDir (not ObjectTypeFile)
DIR_OBJECT* dir = (DIR_OBJECT*)HandleToObject((uint32_t)a0, ObjectTypeDir);
if (!dir) return STATUS_INVALID_PARAMETER;
KSTATUS s = IoReadDir(dir, (void*)(uintptr_t)a1);
ObDereferenceObject(&dir->Header); // Drop the ref taken by HandleToObject
return s;
}
// ---------------------------------------------------------------------------
// SYS_SET_CWD (51) a0=*path
// ---------------------------------------------------------------------------
static uint64_t SyscallSetCwd(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
const char* path = (const char*)(uintptr_t)a0;
if (!path) return STATUS_INVALID_PARAMETER;
return IoSetCwd(path);
}
// ---------------------------------------------------------------------------
// SYS_GET_CWD (52) a0=*buf a1=size
// ---------------------------------------------------------------------------
static uint64_t SyscallGetCwd(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
char* buf = (char*)(uintptr_t)a0;
size_t size = (size_t)a1;
if (!buf || size == 0) return STATUS_INVALID_PARAMETER;
return IoGetCwd(buf, size);
}
// ===========================================================================
// Dispatch table - index must match SYS_* in syscall_nr.h exactly.
// ===========================================================================
@@ -738,22 +892,32 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
/* 24 SYS_QUEUE_SEND */ SyscallQueueSend,
/* 25 SYS_QUEUE_RECV */ SyscallQueueRecv,
/* 26 SYS_CLOSE_HANDLE */ SyscallCloseHandle,
/* 27 SYS_OPEN_FILE */ SyscallOpenFile,
/* 28 SYS_READ_FILE */ SyscallReadFile,
/* 29 SYS_SEEK_FILE */ SyscallSeekFile,
/* 30 SYS_GET_FILE_SIZE */ SyscallGetFileSize,
/* 31 SYS_SCREEN_BLIT */ SyscallScreenBlit,
/* 32 SYS_SCREEN_CLEAR */ SyscallScreenClear,
/* 33 SYS_SCREEN_GET_SIZE */ SyscallScreenGetSize,
/* 34 SYS_SCREEN_PUT_CHAR */ SyscallScreenPutChar,
/* 35 SYS_KB_POLL */ SyscallKbPoll,
/* 36 SYS_KB_READ_CHAR */ SyscallKbReadChar,
/* 37 SYS_KB_READ_SCANCODE */ SyscallKbReadScancode,
/* 38 SYS_GET_TICK */ SyscallGetTick,
/* 39 SYS_DEBUG_PRINT */ SyscallDebugPrint,
/* 40 SYS_CREATE_PROCESS */ SyscallCreateProcess,
/* 41 SYS_WAIT_FOR_PROCESS */ SyscallWaitForProcess,
/* 42 SYS_GET_PROCESS_EXIT_CODE */ SyscallGetProcessExitCode,
/* 27 SYS_OPEN_FILE */ SyscallOpenFile,
/* 28 SYS_READ_FILE */ SyscallReadFile,
/* 29 SYS_SEEK_FILE */ SyscallSeekFile,
/* 30 SYS_GET_FILE_SIZE */ SyscallGetFileSize,
/* 31 SYS_SCREEN_BLIT */ SyscallScreenBlit,
/* 32 SYS_SCREEN_CLEAR */ SyscallScreenClear,
/* 33 SYS_SCREEN_GET_SIZE */ SyscallScreenGetSize,
/* 34 SYS_SCREEN_PUT_CHAR */ SyscallScreenPutChar,
/* 35 SYS_KB_POLL */ SyscallKbPoll,
/* 36 SYS_KB_READ_CHAR */ SyscallKbReadChar,
/* 37 SYS_KB_READ_SCANCODE */ SyscallKbReadScancode,
/* 38 SYS_GET_TICK */ SyscallGetTick,
/* 39 SYS_DEBUG_PRINT */ SyscallDebugPrint,
/* 40 SYS_CREATE_PROCESS */ SyscallCreateProcess,
/* 41 SYS_WAIT_FOR_PROCESS */ SyscallWaitForProcess,
/* 42 SYS_GET_PROCESS_EXIT_CODE */ SyscallGetProcessExitCode,
/* 43 SYS_WRITE_FILE */ SyscallWriteFile,
/* 44 SYS_CREATE_FILE */ SyscallCreateFile,
/* 45 SYS_DELETE_FILE */ SyscallDeleteFile,
/* 46 SYS_TRUNCATE_FILE */ SyscallTruncateFile,
/* 47 SYS_GET_FILE_POS */ SyscallGetFilePos,
/* 48 SYS_WRITE_CONSOLE */ SyscallWriteConsole,
/* 49 SYS_OPEN_DIR */ SyscallOpenDir,
/* 50 SYS_READ_DIR */ SyscallReadDir,
/* 51 SYS_SET_CWD */ SyscallSetCwd,
/* 52 SYS_GET_CWD */ SyscallGetCwd,
};
// ---------------------------------------------------------------------------
+1 -1
View File
@@ -137,7 +137,7 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out) {
// Kernel threads are owned by the idle process. We need a PROCESS* for
// the owner field; grab it from the idle/init thread that already exists.
THREAD* idleThread = KeCurrentThread(); // safe to call before scheduler starts
THREAD* idleThread = KeGetCurrentThread(); // safe to call before scheduler starts
PROCESS* owner = idleThread ? idleThread->Owner : (PROCESS*)0;
if (!owner) return STATUS_INVALID_PARAMETER;
if (priority >= PriorityCount) return STATUS_INVALID_PARAMETER;
+1 -1
View File
@@ -41,7 +41,7 @@ typedef struct THREAD {
// Quanta consumed without blocking - used by the MLFQ for demotion.
uint32_t QuantaConsumed;
struct PROCESS* Owner;
PROCESS* Owner;
LIST_ENTRY ProcessLink; /* node in Owner->ThreadList */
LIST_ENTRY SchedulerLink; /* node in run queue or wait list */
+2 -2
View File
@@ -310,14 +310,14 @@ void SystemInitThread(void* arg) {
// (e.g., win32.dll resolving its imports against syscall.dll)
ResolveSystemImports(args->bootInfo);
args->hal->Screen.Clear(0x000000);
// Print memory status now that the system is fully alive
DbgPrint("Memory Status:\n");
DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f));
DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f));
DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
args->hal->Screen.Clear(0x000000);
// Launch User-Mode Shell (this will trigger AHCI & FAT32 which CAN safely block now)
PROCESS* shellProc = LaunchUserProcess(args->bootInfo->CommandLine);
+1
View File
@@ -14,6 +14,7 @@ typedef enum OBJECT_TYPE {
ObjectTypeEvent = 5,
ObjectTypeQueue = 6,
ObjectTypeFile = 7,
ObjectTypeDir = 8,
} OBJECT_TYPE;
typedef struct OBJECT {
+24 -1
View File
@@ -74,6 +74,11 @@
#define SYS_READ_FILE 28 // (uint32_t handle, void* buf, size_t size, size_t* bytesReadOut) -> KSTATUS
#define SYS_SEEK_FILE 29 // (uint32_t handle, uint64_t offset) -> KSTATUS
#define SYS_GET_FILE_SIZE 30 // (uint32_t handle) -> uint64_t
#define SYS_WRITE_FILE 43
#define SYS_CREATE_FILE 44
#define SYS_DELETE_FILE 45
#define SYS_TRUNCATE_FILE 46
#define SYS_GET_FILE_POS 47
// ---------------------------------------------------------------------------
// Screen / Graphics
@@ -107,7 +112,25 @@
#define SYS_WAIT_FOR_PROCESS 41 // (uint32_t handle) -> KSTATUS
#define SYS_GET_PROCESS_EXIT_CODE 42 // (uint32_t handle, int* exitCodeOut) -> KSTATUS
// ---------------------------------------------------------------------------
// Console output (white text, distinct from green SYS_DEBUG_PRINT)
// ---------------------------------------------------------------------------
#define SYS_WRITE_CONSOLE 48 // (const char* buf, size_t len) -> void
// ---------------------------------------------------------------------------
// Directory enumeration
// (Close the handle via SYS_CLOSE_HANDLE like any other object.)
// ---------------------------------------------------------------------------
#define SYS_OPEN_DIR 49 // (const char* dirPath, uint32_t* handleOut) -> KSTATUS
#define SYS_READ_DIR 50 // (uint32_t handle, KDIR_ENTRY* entryOut) -> KSTATUS
// ---------------------------------------------------------------------------
// Current Working Directory
// ---------------------------------------------------------------------------
#define SYS_SET_CWD 51 // (const char* path) -> KSTATUS
#define SYS_GET_CWD 52 // (char* buf, size_t size) -> KSTATUS
// ---------------------------------------------------------------------------
// Sentinel
// ---------------------------------------------------------------------------
#define SYS_MAX 43
#define SYS_MAX 53
+9
View File
@@ -50,6 +50,15 @@ int strcmp(const char* s1, const char* s2) {
return (unsigned char)*s1 - (unsigned char)*s2;
}
int strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
s1++;
s2++;
n--;
}
return n == 0 ? 0 : *(unsigned char *)s1 - *(unsigned char *)s2;
}
int _stricmp(const char* s1, const char* s2) {
// 0x20 (32) is the bit that differs between 'A' and 'a'
while (*s1 && ((*s1 | 0x20) == (*s2 | 0x20))) {
+162 -23
View File
@@ -4,28 +4,43 @@
#include <string.h>
// ---------------------------------------------------------------------------
// Win32 imports
// Win32 imports & definitions
// ---------------------------------------------------------------------------
_Noreturn void ExitProcess(unsigned int code);
void OutputDebugStringA(const char* s);
typedef void* HANDLE;
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef int BOOL;
#define INVALID_HANDLE_VALUE ((HANDLE)0)
#define INFINITE 0xFFFFFFFFUL
#define STD_INPUT_HANDLE ((DWORD)-10)
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
#define INFINITE 0xFFFFFFFFUL
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define MAX_PATH 260
// File I/O constants
#define GENERIC_READ 0x80000000L
#define OPEN_EXISTING 3
#define FILE_SHARE_READ 0x00000001
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
HANDLE GetStdHandle(DWORD nStdHandle);
BOOL ReadConsoleA(HANDLE hConsoleInput, void* lpBuffer,
DWORD nNumberOfCharsToRead, DWORD* lpNumberOfCharsRead,
void* pInputControl);
DWORD GetLastError();
BOOL ReadConsoleA(HANDLE hConsoleInput, void* lpBuffer,
DWORD nNumberOfCharsToRead, DWORD* lpNumberOfCharsRead,
void* pInputControl);
BOOL WriteConsoleA(HANDLE hConsoleOutput, const void* lpBuffer,
DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten,
void* lpReserved);
typedef struct {
DWORD cb;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
DWORD dwX, dwY, dwXSize, dwYSize;
DWORD dwXCountChars, dwYCountChars;
DWORD dwFillAttribute, dwFlags;
@@ -41,6 +56,24 @@ typedef struct {
DWORD dwThreadId;
} PROCESS_INFORMATION;
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
typedef struct _WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
char cFileName[MAX_PATH];
char cAlternateFileName[14];
} WIN32_FIND_DATAA;
BOOL CreateProcessA(const char* lpApp, char* lpCmdLine,
void* lpPA, void* lpTA, BOOL bInherit,
DWORD dwFlags, void* lpEnv, const char* lpDir,
@@ -49,11 +82,35 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMs);
BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode);
BOOL CloseHandle(HANDLE hObject);
// File operation imports
HANDLE FindFirstFileA(const char* lpFileName, WIN32_FIND_DATAA* lpFindFileData);
BOOL FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATAA* lpFindFileData);
BOOL FindClose(HANDLE hFindFile);
BOOL SetCurrentDirectoryA(const char* lpPathName);
BOOL DeleteFileA(const char* lpFileName);
HANDLE CreateFileA(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
void* lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
BOOL ReadFile(HANDLE hFile, void* lpBuffer, DWORD nNumberOfBytesToRead,
DWORD* lpNumberOfBytesRead, void* lpOverlapped);
// ---------------------------------------------------------------------------
// Global handles
// ---------------------------------------------------------------------------
static HANDLE hIn = INVALID_HANDLE_VALUE;
static HANDLE hOut = INVALID_HANDLE_VALUE;
// ---------------------------------------------------------------------------
// Output helpers
// ---------------------------------------------------------------------------
static void print(const char* s) {
OutputDebugStringA(s);
DWORD written;
// Write to the actual console window
if (hOut != INVALID_HANDLE_VALUE) {
WriteConsoleA(hOut, s, (DWORD)strlen(s), &written, 0);
} else {
OutputDebugStringA(s);
}
}
static void printUInt(uint32_t n) {
@@ -70,12 +127,10 @@ static void printUInt(uint32_t n) {
// ---------------------------------------------------------------------------
// Line editor
// Reads one character at a time via ReadConsoleA, echoes it, handles
// backspace, returns on Enter.
// ---------------------------------------------------------------------------
#define MAX_CMD 256
static void readLine(HANDLE hIn, char* buf, int len) {
static void readLine(char* buf, int len) {
int pos = 0;
while (true) {
char c;
@@ -106,9 +161,6 @@ static void readLine(HANDLE hIn, char* buf, int len) {
buf[pos] = '\0';
}
// ---------------------------------------------------------------------------
// Trim leading/trailing spaces in-place; returns pointer into buf.
// ---------------------------------------------------------------------------
static char* trim(char* s) {
while (*s == ' ') s++;
int len = (int)strlen(s);
@@ -117,6 +169,51 @@ static char* trim(char* s) {
return s;
}
// ---------------------------------------------------------------------------
// Built-in commands
// ---------------------------------------------------------------------------
static void cmd_dir() {
WIN32_FIND_DATAA fd;
HANDLE hFind = FindFirstFileA("*", &fd);
if (hFind == INVALID_HANDLE_VALUE) {
print("dir: failed to read directory\n");
return;
}
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
print("<DIR> ");
} else {
print(" ");
}
print(fd.cFileName);
print("\n");
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
}
static void cmd_type(const char* filename) {
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) {
print("type: could not open '");
print(filename);
print("'\n");
return;
}
char buffer[512];
DWORD bytesRead;
while (ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, 0) && bytesRead > 0) {
buffer[bytesRead] = '\0';
print(buffer);
}
CloseHandle(hFile);
print("\n");
}
// ---------------------------------------------------------------------------
// Run one command. Returns 1 if the shell should exit.
// ---------------------------------------------------------------------------
@@ -124,19 +221,59 @@ static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0')
return 0;
if (strcmp(cmd, "exit") == 0)
// --- Built-in commands ---
if (strcmp(cmd, "exit") == 0) {
return 1;
}
if (strcmp(cmd, "dir") == 0) {
cmd_dir();
return 0;
}
if (strncmp(cmd, "cd ", 3) == 0) {
const char* path = cmd + 3;
if (!SetCurrentDirectoryA(path)) {
print("cd: failed to change directory to '");
print(path);
print("'\n");
}
return 0;
}
if (strncmp(cmd, "del ", 4) == 0) {
const char* file = cmd + 4;
if (!DeleteFileA(file)) {
print("del: failed to delete '");
print(file);
print("'\n");
}
return 0;
}
if (strncmp(cmd, "type ", 5) == 0) {
cmd_type(cmd + 5);
return 0;
}
// --- External Process Execution ---
STARTUPINFOA si;
PROCESS_INFORMATION pi;
unsigned char* p = (unsigned char*)&si;
for (size_t i = 0; i < sizeof(si); i++) p[i] = 0;
si.cb = (DWORD)sizeof(si);
if (!CreateProcessA(cmd, 0, 0, 0, 0, 0, 0, 0, &si, &pi)) {
// CreateProcessA requires a mutable buffer for the command line argument
char cmdBuf[MAX_CMD];
strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
if (!CreateProcessA(0, cmdBuf, 0, 0, 0, 0, 0, 0, &si, &pi)) {
print("shell: could not launch '");
print(cmd);
print("'\n");
print("', error code: ");
printUInt(GetLastError());
print("\n");
return 0;
}
@@ -145,6 +282,7 @@ static int runCommand(const char* cmd) {
DWORD exitCode = 0;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (exitCode != 0) {
print("shell: '");
@@ -161,14 +299,15 @@ static int runCommand(const char* cmd) {
// Entry point
// ---------------------------------------------------------------------------
int main() {
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
hIn = GetStdHandle(STD_INPUT_HANDLE);
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
char lineBuf[MAX_CMD];
print("shell: ready\n");
while (true) {
print("> ");
readLine(hIn, lineBuf, MAX_CMD);
readLine(lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf)))
break;
}
+17 -1
View File
@@ -47,6 +47,11 @@ SYS_OPEN_FILE EQU 27
SYS_READ_FILE EQU 28
SYS_SEEK_FILE EQU 29
SYS_GET_FILE_SIZE EQU 30
SYS_WRITE_FILE EQU 43 ; a0=handle a1=buf a2=size a3=*bytesWritten
SYS_CREATE_FILE EQU 44 ; a0=*path a1=*handleOut
SYS_DELETE_FILE EQU 45 ; a0=*path
SYS_TRUNCATE_FILE EQU 46 ; a0=handle a1=newSize
SYS_GET_FILE_POS EQU 47 ; a0=handle -> current offset
; Screen
SYS_SCREEN_BLIT EQU 31
@@ -70,5 +75,16 @@ SYS_CREATE_PROCESS EQU 40
SYS_WAIT_FOR_PROCESS EQU 41
SYS_GET_PROCESS_EXIT_CODE EQU 42
; Console output
SYS_WRITE_CONSOLE EQU 48
; Directory enumeration
SYS_OPEN_DIR EQU 49
SYS_READ_DIR EQU 50
; Current Working Directory
SYS_SET_CWD EQU 51
SYS_GET_CWD EQU 52
; Sentinel
SYS_MAX EQU 43
SYS_MAX EQU 53
+22
View File
@@ -109,6 +109,11 @@ SYSCALL_STUB2 OpenFile, SYS_OPEN_FILE
SYSCALL_STUB2 ReadFile, SYS_READ_FILE
SYSCALL_STUB2 SeekFile, SYS_SEEK_FILE
SYSCALL_STUB2 GetFileSize, SYS_GET_FILE_SIZE
SYSCALL_STUB2 WriteFile, SYS_WRITE_FILE ; handle, buf, size, *bytesWritten
SYSCALL_STUB2 CreateFile, SYS_CREATE_FILE ; *path, *handleOut
SYSCALL_STUB2 DeleteFile, SYS_DELETE_FILE ; *path
SYSCALL_STUB2 TruncateFile, SYS_TRUNCATE_FILE ; handle, newSize
SYSCALL_STUB2 GetFilePos, SYS_GET_FILE_POS ; handle -> offset
; ---------------------------------------------------------------------------
; Screen
@@ -144,4 +149,21 @@ SYSCALL_STUB2 CreateProcess, SYS_CREATE_PROCESS
SYSCALL_STUB2 WaitForProcess, SYS_WAIT_FOR_PROCESS
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
; ---------------------------------------------------------------------------
; Console output (white text, for user programs)
; ---------------------------------------------------------------------------
SYSCALL_STUB2 WriteConsole, SYS_WRITE_CONSOLE
; ---------------------------------------------------------------------------
; Directory enumeration
; ---------------------------------------------------------------------------
SYSCALL_STUB2 OpenDir, SYS_OPEN_DIR
SYSCALL_STUB2 ReadDir, SYS_READ_DIR
; ---------------------------------------------------------------------------
; Current Working Directory
; ---------------------------------------------------------------------------
SYSCALL_STUB2 SetCwd, SYS_SET_CWD
SYSCALL_STUB2 GetCwd, SYS_GET_CWD
END
+147 -61
View File
@@ -4,83 +4,169 @@
#include <intrin.h>
#include "bink.h"
void OutputDebugStringA(const char* s);
// ---------------------------------------------------------------------------
// Win32 Boilerplate & API declarations
// ---------------------------------------------------------------------------
typedef void* HWND;
typedef void* HDC;
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UINT;
typedef long LONG;
typedef int BOOL;
#define BI_RGB 0L
#define DIB_RGB_COLORS 0
#define SRCCOPY 0x00CC0020
#define STD_OUTPUT_HANDLE ((DWORD)-11)
typedef struct _RECT {
LONG left, top, right, bottom;
} RECT;
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
char rgbReserved[32];
} PAINTSTRUCT;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
unsigned char rgbBlue, rgbGreen, rgbRed, rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
// Kernel/Win32 Exports
void* GetStdHandle(DWORD nStdHandle);
int WriteConsoleA(void* hConsoleOutput, const void* lpBuffer, unsigned int nNumberOfCharsToWrite, unsigned int* lpNumberOfCharsWritten, void* lpReserved);
_Noreturn void ExitProcess(unsigned int code);
void* HeapAlloc(void* heap, unsigned long flags, size_t size);
void BitBlt(int x, int y, int w, int h, const uint32_t* pixels);
void* CreateFileA(const char* name, unsigned long access,
unsigned long share, void* sa,
unsigned long disp, unsigned long flags,
void* templ);
void* CreateFileA(const char* name, unsigned long access, unsigned long share, void* sa, unsigned long disp, unsigned long flags, void* templ);
unsigned long GetFileSize(void* handle, unsigned long* high);
int ReadFile(void* handle, void* buf, unsigned long toRead,
unsigned long* read, void* overlapped);
int ReadFile(void* handle, void* buf, unsigned long toRead, unsigned long* read, void* overlapped);
BOOL CloseHandle(void* hObject);
// New GDI/User32 Exports
HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint);
BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lpPaint);
int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight,
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
const void *lpBits, const BITMAPINFO *lpbmi,
UINT iUsage, DWORD rop);
int main() {
OutputDebugStringA("video: starting\n");
void* h = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
// void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
// if (!f) {
// OutputDebugStringA("video: failed to open video.bk2\n");
// ExitProcess(1);
// }
void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
if (!f) {
WriteConsoleA(h, "video: failed to open video.bk2\n", 32, 0, 0);
ExitProcess(1);
}
// unsigned long s = GetFileSize(f, 0);
// void* buf = HeapAlloc(0, 0, s);
// if (!buf) {
// OutputDebugStringA("video: HeapAlloc failed\n");
// ExitProcess(1);
// }
unsigned long s = GetFileSize(f, 0);
void* buf = HeapAlloc(0, 0, s);
if (!buf) {
WriteConsoleA(h, "video: HeapAlloc failed\n", 24, 0, 0);
ExitProcess(1);
}
// int r = ReadFile(f, buf, s, 0, 0);
// if (!r) {
// OutputDebugStringA("video: ReadFile failed\n");
// ExitProcess(1);
// }
// OutputDebugStringA("video: video.bk2 loaded\n");
int r = ReadFile(f, buf, s, 0, 0);
if (!r) {
WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0);
ExitProcess(1);
}
WriteConsoleA(h, "video: video.bk2 loaded\n", 24, 0, 0);
// HBINK bink = BinkOpen(buf, BINKFROMMEMORY);
// if (!bink) {
// OutputDebugStringA("video: BinkOpen failed: ");
// OutputDebugStringA(BinkGetError());
// OutputDebugStringA("\n");
// ExitProcess(1);
// }
CloseHandle(h);
// uint32_t width = bink->Width;
// uint32_t height = bink->Height;
HBINK bink = BinkOpen(buf, BINKFROMMEMORY);
if (!bink) {
WriteConsoleA(h, "video: BinkOpen failed: ", 24, 0, 0);
const char* err = BinkGetError();
WriteConsoleA(h, err, strlen(err), 0, 0);
WriteConsoleA(h, "\n", 1, 0, 0);
ExitProcess(1);
}
// uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
// if (!fb) {
// OutputDebugStringA("video: framebuffer alloc failed\n");
// ExitProcess(1);
// }
uint32_t width = bink->Width;
uint32_t height = bink->Height;
// while (1) {
// if (BinkWait(bink)) {
// _mm_pause();
// continue;
// }
uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
if (!fb) {
WriteConsoleA(h, "video: framebuffer alloc failed\n", 32, 0, 0);
ExitProcess(1);
}
// BinkDoFrame(bink);
// Prepare the Win32 BITMAPINFO structure
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -(LONG)height; // Negative means top-down drawing!
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
// if (BinkShouldSkip(bink)) {
// BinkNextFrame(bink);
// BinkDoFrame(bink);
// continue;
// }
while (1) {
if (BinkWait(bink)) {
_mm_pause();
continue;
}
// BinkCopyToBuffer(bink, fb, width * 4, height, 0, 0, BINKSURFACE32);
// BitBlt(0, 0, width, height, fb);
BinkDoFrame(bink);
// if (bink->FrameNum == bink->Frames)
// break;
if (BinkShouldSkip(bink)) {
BinkNextFrame(bink);
continue;
}
// BinkNextFrame(bink);
// }
BinkCopyToBuffer(bink, fb, width * 4, height, 0, 0,
BINKSURFACE32);
// BinkClose(bink);
for (int i = 0; i < 10000000; i++) _mm_pause();
OutputDebugStringA("video: done\n");
ExitProcess(0);
// Welcome to the GDI pipeline!
PAINTSTRUCT ps;
// Using NULL/0 as the HWND since we don't have a real window handle yet
HDC hdc = BeginPaint((HWND)0, &ps);
StretchDIBits(
hdc,
0, 0, width, height, // Destination rect
0, 0, width, height, // Source rect
fb,
&bmi,
DIB_RGB_COLORS,
SRCCOPY
);
EndPaint((HWND)0, &ps);
BinkNextFrame(bink);
}
WriteConsoleA(h, "shell: goodbye\n", 15, 0, 0);
return 0;
}
+83
View File
@@ -0,0 +1,83 @@
// gdi32.c - Win32 Graphics Device Interface stubs
#include <stdint.h>
#include <stddef.h>
// ---------------------------------------------------------------------------
// Syscall stubs (from syscall.dll)
// ---------------------------------------------------------------------------
extern void SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
// ---------------------------------------------------------------------------
// Win32 Base Types
// ---------------------------------------------------------------------------
typedef void* HDC;
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UINT;
typedef long LONG;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
// StretchDIBits Constants
#define BI_RGB 0L
#define DIB_RGB_COLORS 0
#define SRCCOPY (DWORD)0x00CC0020
// ---------------------------------------------------------------------------
// GDI Structs
// ---------------------------------------------------------------------------
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
// ---------------------------------------------------------------------------
// GDI Exports
// ---------------------------------------------------------------------------
__declspec(dllexport) int StretchDIBits(
HDC hdc,
int xDest, int yDest, int DestWidth, int DestHeight,
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
const void *lpBits, const BITMAPINFO *lpbmi,
UINT iUsage, DWORD rop)
{
(void)hdc; (void)xSrc; (void)ySrc; (void)SrcWidth; (void)SrcHeight;
(void)lpbmi; (void)iUsage; (void)rop;
// Direct mapping to kernel screen blit
SysScreenBlit(xDest, yDest, DestWidth, DestHeight, (const uint32_t*)lpBits);
// Return the number of scanlines copied
return DestHeight;
}
__declspec(dllexport) BOOL BitBlt(HDC hdcDest, int xDest, int yDest, int w, int h,
HDC hdcSrc, int xSrc, int ySrc, DWORD rop) {
(void)hdcDest; (void)xDest; (void)yDest; (void)w; (void)h;
(void)hdcSrc; (void)xSrc; (void)ySrc; (void)rop;
// Stubbed. Applications manipulating pixels directly will use StretchDIBits.
return TRUE;
}
+439 -45
View File
@@ -33,6 +33,12 @@ extern void* SysRealloc(void* ptr, size_t newSize);
extern uint32_t SysOpenFile(const char* path, uint32_t* handleOut);
extern uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesReadOut);
extern uint32_t SysWriteFile(uint32_t handle, const void* buf,
size_t size, size_t* bytesWrittenOut);
extern uint32_t SysCreateFile(const char* path, uint32_t* handleOut);
extern uint32_t SysDeleteFile(const char* path);
extern uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
extern uint64_t SysGetFilePos(uint32_t handle);
extern uint32_t SysSeekFile(uint32_t handle, uint64_t offset);
extern uint64_t SysGetFileSize(uint32_t handle);
extern void SysCloseHandle(uint32_t handle);
@@ -54,6 +60,13 @@ extern void SysSleep(uint32_t ms);
extern uint64_t SysGetTick(void);
extern void SysDebugPrint(const char* str);
// Console & directory & CWD stubs (syscall.dll)
extern void SysWriteConsole(const char* buf, size_t len);
extern uint32_t SysOpenDir(const char* dirPath, uint32_t* handleOut);
extern uint32_t SysReadDir(uint32_t handle, void* entryOut);
extern uint32_t SysSetCwd(const char* path);
extern uint32_t SysGetCwd(char* buf, size_t size);
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
@@ -61,16 +74,41 @@ typedef void* HANDLE;
typedef unsigned long DWORD;
typedef int BOOL;
#define MAX_PATH 260
#define TRUE 1
#define FALSE 0
#define INVALID_HANDLE_VALUE ((HANDLE)(uintptr_t)0)
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
// File-find structures
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
typedef struct _WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
char cFileName[260]; // MAX_PATH
char cAlternateFileName[14];
} WIN32_FIND_DATAA;
// CreateFileA disposition / access flags (subset used by Bink and similar)
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define OPEN_EXISTING 3
#define OPEN_ALWAYS 4
#define CREATE_ALWAYS 2
#define CREATE_NEW 1
#define TRUNCATE_EXISTING 5
#define FILE_BEGIN 0
#define FILE_CURRENT 1
#define FILE_END 2
@@ -200,41 +238,90 @@ __declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US
// File I/O
// ---------------------------------------------------------------------------
// CreateFileA - open an existing file for reading.
// Write access, CREATE_ALWAYS and other modes return INVALID_HANDLE_VALUE
// because the VFS has no write path yet.
// CreateFileA - opens or creates a file.
// Supported modes:
// OPEN_EXISTING + GENERIC_READ -> open existing, read-only
// OPEN_EXISTING + GENERIC_WRITE -> open existing, read-write
// OPEN_EXISTING + GENERIC_READ|GENERIC_WRITE -> open existing, read-write
// CREATE_ALWAYS + GENERIC_WRITE -> create/truncate, write
// OPEN_ALWAYS + GENERIC_WRITE -> create if absent, write
// TRUNCATE_EXISTING + GENERIC_WRITE -> open & truncate to zero
__declspec(dllexport) HANDLE CreateFileA(const char* name,
DWORD access, DWORD share,
void* sa, DWORD disposition,
DWORD flags, void* templ) {
(void)share; (void)sa; (void)flags; (void)templ;
// Only OPEN_EXISTING + read supported for now.
if (disposition != OPEN_EXISTING || (access & GENERIC_WRITE)) {
gLastError = 5; // ERROR_ACCESS_DENIED
return INVALID_HANDLE_VALUE;
int wantWrite = (access & GENERIC_WRITE) != 0;
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
// Open the file via the read path (it must already exist).
uint32_t kh = 0;
uint32_t s = SysOpenFile(name, &kh);
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
if (disposition == TRUNCATE_EXISTING && wantWrite) {
// Immediately truncate to zero.
uint32_t ts = SysTruncateFile(kh, 0);
if (ts != 0) {
SysCloseHandle(kh);
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
uint32_t kh = 0;
uint32_t s = SysOpenFile(name, &kh);
if (s != 0 /* STATUS_SUCCESS */) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE;
if (disposition == CREATE_ALWAYS && wantWrite) {
// Create-or-replace: try to create; if the file already exists,
// open it and truncate it to zero instead.
uint32_t kh = 0;
uint32_t s = SysCreateFile(name, &kh);
if (s != 0) {
// File may already exist - open and truncate.
s = SysOpenFile(name, &kh);
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
SysTruncateFile(kh, 0);
}
return KH_TO_HANDLE(kh);
}
return KH_TO_HANDLE(kh);
if (disposition == OPEN_ALWAYS && wantWrite) {
// Open if exists, create if not.
uint32_t kh = 0;
if (SysOpenFile(name, &kh) != 0) {
if (SysCreateFile(name, &kh) != 0) {
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
if (disposition == CREATE_NEW && wantWrite) {
// Fail if the file already exists (we can't check atomically,
// but SysCreateFile returns NAME_COLLISION for existing files).
uint32_t kh = 0;
uint32_t s = SysCreateFile(name, &kh);
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
return KH_TO_HANDLE(kh);
}
// Any other combination (e.g. read-only CREATE_ALWAYS) is not supported.
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
}
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; } // ERROR_INVALID_HANDLE
if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
}
// ReadFile - sequential read at the file's current position.
// Overlapped is not supported (must be NULL).
__declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
@@ -242,36 +329,73 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
if (bytesRead) *bytesRead = (DWORD)got;
if (s != 0) { gLastError = 1; return FALSE; } // ERROR_INVALID_FUNCTION
if (s != 0) { gLastError = 1; return FALSE; }
return TRUE;
}
// SetFilePointer - seek to an absolute or relative position.
// WriteFile - sequential write at the file's current position.
// Extends the file if writing past EOF. Overlapped I/O is not supported.
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)overlapped;
if (!hFile || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
if (bytesWritten) *bytesWritten = (DWORD)written;
if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT
return TRUE;
}
// SetFilePointer - seek to an absolute, relative, or end-relative position.
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t offset = 0;
int64_t delta = distHigh
? ((int64_t)(uint64_t)(uint32_t)*distHigh << 32) | (uint32_t)dist
: (int64_t)dist;
if (method == FILE_BEGIN) {
offset = (uint64_t)(int64_t)dist;
if (distHigh) offset |= (uint64_t)(uint32_t)*distHigh << 32;
} else if (method == FILE_CURRENT || method == FILE_END) {
// FILE_CURRENT and FILE_END require knowing the current offset or
// the file size; expose as unsupported for now.
gLastError = 1; // ERROR_INVALID_FUNCTION
return 0xFFFFFFFFUL;
offset = (uint64_t)delta;
} else if (method == FILE_CURRENT) {
uint64_t cur = SysGetFilePos(kh);
if (cur == (uint64_t)-1) { gLastError = 6; return 0xFFFFFFFFUL; }
offset = (uint64_t)((int64_t)cur + delta);
} else if (method == FILE_END) {
uint64_t sz = SysGetFileSize(kh);
offset = (uint64_t)((int64_t)sz + delta);
} else {
gLastError = 87; // ERROR_INVALID_PARAMETER
return 0xFFFFFFFFUL;
gLastError = 87; return 0xFFFFFFFFUL;
}
uint32_t s = SysSeekFile(HANDLE_TO_KH(hFile), offset);
uint32_t s = SysSeekFile(kh, offset);
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
if (distHigh) *distHigh = (long)(offset >> 32);
if (distHigh) *distHigh = (long)(int32_t)(offset >> 32);
return (DWORD)(offset & 0xFFFFFFFFULL);
}
// SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
uint32_t s = SysTruncateFile(kh, pos);
if (s != 0) { gLastError = 1; return FALSE; }
return TRUE;
}
// DeleteFileA - delete a file by name; no open handle required.
__declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
if (!lpFileName) { gLastError = 87; return FALSE; }
uint32_t s = SysDeleteFile(lpFileName);
if (s != 0) { gLastError = 2; return FALSE; } // ERROR_FILE_NOT_FOUND
return TRUE;
}
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject) { gLastError = 6; return FALSE; }
@@ -279,15 +403,6 @@ __declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
return TRUE;
}
// WriteFile stub - no write VFS support yet.
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)hFile; (void)buf; (void)toWrite; (void)overlapped;
if (bytesWritten) *bytesWritten = 0;
gLastError = 1; // ERROR_INVALID_FUNCTION
return FALSE;
}
// ===========================================================================
// Extended shims - funcs.txt additions
// All functions here return plausible values; no real implementation exists
@@ -474,20 +589,34 @@ __declspec(dllexport) BOOL CreateProcessA(
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpCurrentDirectory; (void)lpStartupInfo;
if (!lpApplicationName || !lpProcessInformation) {
// lpProcessInformation is mandatory
if (!lpProcessInformation) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return FALSE;
}
uint32_t kh = 0;
uint32_t s = SysCreateProcess(lpApplicationName, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND / generic launch failure
// Standard Win32 behavior: use lpCommandLine if lpApplicationName is NULL
const char* targetPath = lpApplicationName;
if (!targetPath) {
targetPath = lpCommandLine;
}
// If both are NULL, then it's truly an invalid parameter
if (!targetPath) {
gLastError = 87;
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
uint32_t kh = 0;
// Call the syscall.dll stub
uint32_t s = SysCreateProcess(targetPath, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
return TRUE;
@@ -581,6 +710,271 @@ __declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
return TRUE;
}
// WriteConsoleA: write nNumberOfCharsToWrite characters from lpBuffer to the
// console as white-on-black text via SYS_WRITE_CONSOLE.
__declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput,
const void* lpBuffer,
DWORD nNumberOfCharsToWrite,
DWORD* lpNumberOfCharsWritten,
void* lpReserved) {
(void)hConsoleOutput; (void)lpReserved;
if (!lpBuffer || nNumberOfCharsToWrite == 0) {
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
return TRUE;
}
SysWriteConsole((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;
}
// ---------------------------------------------------------------------------
// Directory enumeration (FindFirstFileA / FindNextFileA / FindClose)
// ---------------------------------------------------------------------------
// KDIR_ENTRY layout must exactly match the definition in io_file.h.
#define KDIR_NAME_MAX 260
typedef struct {
char Name[KDIR_NAME_MAX];
uint8_t Attr;
uint32_t FileSize;
} KDIR_ENTRY;
// FIND_HANDLE is a user-side wrapper: it stores the kernel dir handle and
// the wildcard pattern so FindNextFileA can filter subsequent entries.
// We return a pointer to this struct cast to HANDLE; it is NOT a kernel
// handle, so FindClose must NOT go through CloseHandle/SysCloseHandle.
typedef struct {
uint32_t kh; // kernel DIR_OBJECT handle (raw pointer)
char pattern[MAX_PATH]; // wildcard pattern, e.g. "*" or "*.txt"
} FIND_HANDLE;
// Minimal wildcard match: supports '*' (any sequence) and '?' (any char).
// Case-insensitive to match FAT32/Windows behaviour.
static int WildMatch(const char* pat, const char* name) {
while (*pat && *name) {
if (*pat == '*') {
// Skip consecutive stars
while (*pat == '*') pat++;
if (!*pat) return 1; // trailing * matches everything
// Try matching the rest of the pattern at every position
while (*name) {
if (WildMatch(pat, name)) return 1;
name++;
}
return 0;
}
char p = (*pat >= 'A' && *pat <= 'Z') ? *pat + 32 : *pat;
char n = (*name >= 'A' && *name <= 'Z') ? *name + 32 : *name;
if (*pat != '?' && p != n) return 0;
pat++; name++;
}
// Consume trailing stars in the pattern
while (*pat == '*') pat++;
return (*pat == '\0' && *name == '\0');
}
// Split a FindFirstFileA-style search path into directory and pattern parts.
// "apps\\*" -> dir="apps" pat="*"
// "*.txt" -> dir="" pat="*.txt"
// "*" -> dir="" pat="*"
// "apps\\" -> dir="apps" pat="*"
static void SplitFindPath(const char* searchPath,
char* dirOut, size_t dirSize,
char* patOut, size_t patSize) {
// Find last backslash or forward-slash
const char* last = NULL;
for (const char* p = searchPath; *p; p++)
if (*p == '\\' || *p == '/') last = p;
if (!last) {
// No separator: whole string is the pattern, dir is CWD
if (dirOut && dirSize) dirOut[0] = '\0';
if (patOut && patSize) {
size_t len = strlen(searchPath);
if (len + 1 > patSize) len = patSize - 1;
memcpy(patOut, searchPath, len);
patOut[len] = '\0';
}
} else {
// Everything before the last separator is the directory
if (dirOut) {
size_t len = (size_t)(last - searchPath);
if (len + 1 > dirSize) len = dirSize - 1;
memcpy(dirOut, searchPath, len);
dirOut[len] = '\0';
}
// Everything after the last separator is the pattern
if (patOut) {
const char* p = last + 1;
size_t len = strlen(p);
if (len + 1 > patSize) len = patSize - 1;
memcpy(patOut, p, len);
patOut[len] = '\0';
}
}
// Empty pattern defaults to "*"
if (patOut && patOut[0] == '\0') {
patOut[0] = '*'; patOut[1] = '\0';
}
}
// Fill a WIN32_FIND_DATAA from a KDIR_ENTRY.
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010UL
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020UL
#define FILE_ATTRIBUTE_NORMAL 0x00000080UL
static void KdirToFindData(const KDIR_ENTRY* ke, WIN32_FIND_DATAA* fd) {
// Zero the entire struct (covers all FILETIME and reserved fields)
unsigned char* p = (unsigned char*)fd;
for (size_t i = 0; i < sizeof(*fd); i++) p[i] = 0;
fd->dwFileAttributes = ke->Attr;
if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(fd->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
fd->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
fd->nFileSizeLow = ke->FileSize;
fd->nFileSizeHigh = 0;
size_t nameLen = strlen(ke->Name);
if (nameLen >= MAX_PATH) nameLen = MAX_PATH - 1;
memcpy(fd->cFileName, ke->Name, nameLen);
fd->cFileName[nameLen] = '\0';
}
__declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
WIN32_FIND_DATAA* lpFindFileData) {
if (!lpFileName || !lpFindFileData) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
}
// Split path into directory and wildcard pattern
char dir[MAX_PATH];
char pat[MAX_PATH];
SplitFindPath(lpFileName, dir, sizeof(dir), pat, sizeof(pat));
// Open the kernel directory iterator
uint32_t kh = 0;
uint32_t s = SysOpenDir(dir[0] ? dir : NULL, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE;
}
// Allocate our user-side FIND_HANDLE wrapper
FIND_HANDLE* fh = (FIND_HANDLE*)HeapAlloc(NULL, 0, sizeof(FIND_HANDLE));
if (!fh) {
SysCloseHandle(kh);
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return INVALID_HANDLE_VALUE;
}
fh->kh = kh;
size_t patLen = strlen(pat);
if (patLen >= MAX_PATH) patLen = MAX_PATH - 1;
memcpy(fh->pattern, pat, patLen);
fh->pattern[patLen] = '\0';
// Read the first entry that matches the pattern
KDIR_ENTRY ke;
for (;;) {
uint32_t rs = SysReadDir(kh, &ke);
if (rs != 0) {
// Directory exhausted or error
SysCloseHandle(kh);
HeapFree(NULL, 0, fh);
gLastError = 18; // ERROR_NO_MORE_FILES
return INVALID_HANDLE_VALUE;
}
if (WildMatch(fh->pattern, ke.Name)) {
KdirToFindData(&ke, lpFindFileData);
return (HANDLE)fh;
}
}
}
__declspec(dllexport) BOOL FindNextFileA(HANDLE hFindFile,
WIN32_FIND_DATAA* lpFindFileData) {
if (hFindFile == INVALID_HANDLE_VALUE || !lpFindFileData) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
KDIR_ENTRY ke;
for (;;) {
uint32_t rs = SysReadDir(fh->kh, &ke);
if (rs != 0) {
gLastError = 18; // ERROR_NO_MORE_FILES
return FALSE;
}
if (WildMatch(fh->pattern, ke.Name)) {
KdirToFindData(&ke, lpFindFileData);
return TRUE;
}
}
}
__declspec(dllexport) BOOL FindClose(HANDLE hFindFile) {
if (hFindFile == INVALID_HANDLE_VALUE) {
gLastError = 6;
return FALSE;
}
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
SysCloseHandle(fh->kh);
HeapFree(NULL, 0, fh);
return TRUE;
}
// ---------------------------------------------------------------------------
// Current Working Directory
// ---------------------------------------------------------------------------
__declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
if (!lpPathName) { gLastError = 87; return FALSE; }
uint32_t s = SysSetCwd(lpPathName);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
__declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) {
if (!lpBuffer || nBufferLength == 0) {
// Return required size (including NUL). Max CWD is 512 bytes + "C:\".
return 515;
}
// Retrieve the raw relative CWD from the kernel (e.g. "" or "apps\\data")
char raw[512];
uint32_t s = SysGetCwd(raw, sizeof(raw));
if (s != 0) { gLastError = 1; return 0; }
// Prepend the Windows-style drive letter so callers see "C:\apps\data"
// rather than just "apps\data".
char full[515];
if (raw[0] == '\0') {
// Root
full[0] = 'C'; full[1] = ':'; full[2] = '\\'; full[3] = '\0';
} else {
// "C:\<raw>"
full[0] = 'C'; full[1] = ':'; full[2] = '\\';
size_t rawLen = strlen(raw);
if (rawLen + 4 > sizeof(full)) rawLen = sizeof(full) - 4;
memcpy(full + 3, raw, rawLen);
full[3 + rawLen] = '\0';
}
size_t needed = strlen(full) + 1;
if ((DWORD)needed > nBufferLength) {
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return (DWORD)needed;
}
memcpy(lpBuffer, full, needed);
return (DWORD)(needed - 1); // length without NUL, matching Win32
}
// ---------------------------------------------------------------------------
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
+33 -53
View File
@@ -1,62 +1,53 @@
// user32.c - Win32 user interface stubs (no window manager yet)
// user32.c - Win32 User Interface
#include <stdint.h>
#include <stddef.h>
// ---------------------------------------------------------------------------
// Syscall stubs (from syscall.dll)
// ---------------------------------------------------------------------------
extern void SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
extern void SysYield(void);
extern void SysDebugPrint(const char* str);
extern uint32_t SysKbPoll(void);
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef void* HWND;
typedef void* HDC;
typedef long LONG;
typedef int BOOL;
#define IDOK 1
#define IDCANCEL 2
typedef struct _RECT { LONG left, top, right, bottom; } RECT;
typedef struct tagPAINTSTRUCT {
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
} PAINTSTRUCT;
// ---------------------------------------------------------------------------
// Keyboard
// ---------------------------------------------------------------------------
// GetAsyncKeyState: returns non-zero if a key is available in the input
// buffer. We have no per-key state, so the vKey argument is ignored and
// the high bit (key-down) is set whenever SysKbPoll reports data waiting.
__declspec(dllexport) short GetAsyncKeyState(int vKey) {
(void)vKey;
// High bit (0x8000 / INT16_MIN) = key is down.
// Low bit = key pressed since last call; we have no per-key history to
// track, so it is left clear.
return SysKbPoll() ? (short)(-32768) : 0;
// --- Painting Functions ---
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
(void)hwnd;
if (lpPaint) {
lpPaint->hdc = (HDC)1;
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
}
return (HDC)1;
}
// ---------------------------------------------------------------------------
// Screen output
// ---------------------------------------------------------------------------
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
// BitBlt is the only graphical primitive available at this stage.
__declspec(dllexport) void BitBlt(int x, int y, int w, int h, const uint32_t* pixels) {
SysScreenBlit(x, y, w, h, pixels);
// --- Message Loop Stubs ---
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
}
// ---------------------------------------------------------------------------
// Message loop (stubs - always return 0 / no messages)
// ---------------------------------------------------------------------------
__declspec(dllexport) int GetMessage(void* msg, HWND hwnd,
unsigned int min, unsigned int max) {
(void)msg; (void)hwnd; (void)min; (void)max;
SysYield();
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
(void)m; (void)h; (void)f; (void)l; (void)r;
return 0;
}
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
__declspec(dllexport) int DispatchMessage(const void* msg) { (void)msg; return 0; }
// --- Dialogs & UI ---
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
(void)h; (void)cap; (void)type;
SysDebugPrint("USER32: MessageBoxA: ");
SysDebugPrint(txt);
SysDebugPrint("\n");
return 1; // IDOK
}
// ---------------------------------------------------------------------------
// Window queries (stubs - no WM exists yet, all return NULL / 0)
// ---------------------------------------------------------------------------
// --- Window Queries ---
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
@@ -78,14 +69,3 @@ __declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned
if (pid) *pid = 1;
return 1; // Thread ID
}
// ---------------------------------------------------------------------------
// Dialogs
// ---------------------------------------------------------------------------
__declspec(dllexport) int MessageBoxA(HWND hwnd, const char* text,
const char* caption, unsigned int type) {
(void)hwnd; (void)text; (void)caption; (void)type;
// No UI available, console
SysDebugPrint(text);
return IDOK;
}
+2 -2
View File
@@ -65,7 +65,7 @@ drivers to be loaded have their path stored in `drivers.ini`, split into essenti
a disk driver fills in a `DISK_DRIVER` vtable and calls `IoRegisterDisk(index, &driver)`.
a filesystem driver fills in an `IO_FILESYSTEM` vtable and calls `IoRegisterFilesystem(index, &fs, &vol)`.
currently shipping: AHCI (interrupts), IDE (PIO/DMA), FAT32 (read-only).
currently shipping: AHCI (interrupts), IDE (PIO/DMA) and FAT32.
## how it boots
@@ -106,7 +106,7 @@ Q: Is the scheduler preemptive?
A: As much as it will ever be.
Q: Will it corrupt my data?
A: How would it? It's read-only.
A: I hope not.
Q: Is this finished?
A: Not in the slightest.