fix the return value of LdrLoadImage, make use of __declspec(dllexport), speed up blitting, add dummy TEB, redesign the syscall interface, fix stack alignent so movaps doesn't crash, implement a shitload of win32, implement a simple shell, add bink as video.exe 😎, minor kmain.c cleanup, HAL now supports power-off events, implemented \b in hal_screen.c, fix many deadlocks, add wait for exit event for processes, completely new error codes, small readme changes.
This commit is contained in:
1 parent
c13dd99150
commit
7f4052f41c
33 files changed
+6396
-735
No files matched your search
+1
-1
@@ -697,7 +697,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
|
||||
UINT64 start = desc->PhysicalStart;
|
||||
UINT64 end = start + (desc->NumberOfPages * PAGE_SIZE);
|
||||
|
||||
// CRITICAL FIX: Add 2 pages (8KB) of padding to the end of the region.
|
||||
// Add 2 pages (8KB) of padding to the end of the region.
|
||||
// This catches ACPI tables that cross page boundaries into unmapped
|
||||
// memory because of buggy UEFI size reporting.
|
||||
end += 2 * PAGE_SIZE;
|
||||
|
||||
@@ -60,6 +60,12 @@ typedef struct HAL_INTC {
|
||||
void (*SendEoi)(void);
|
||||
} HAL_INTC;
|
||||
|
||||
typedef struct HAL_POWER {
|
||||
// ACPI S5 soft-off. Disables interrupts, writes SLP_EN to PM1a/b_CNT,
|
||||
// and halts if firmware doesn't respond. Does not return.
|
||||
void (*Shutdown)(void);
|
||||
} HAL_POWER;
|
||||
|
||||
typedef struct HAL_API {
|
||||
uint32_t Version;
|
||||
void *(*PhysToVirt)(uint64_t physAddr);
|
||||
@@ -69,6 +75,7 @@ typedef struct HAL_API {
|
||||
HAL_TIMER Timer;
|
||||
HAL_PCI Pci;
|
||||
HAL_INTC Intc;
|
||||
HAL_POWER Power;
|
||||
} HAL_API;
|
||||
|
||||
typedef struct HAL_INIT_PARAMS {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// hal_keyboard.c - PS/2 keyboard, interrupt-driven, 256-entry ring buffer
|
||||
#include "hal_api.h"
|
||||
#include "hal_apic.h"
|
||||
#include "hal_debug.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
@@ -48,7 +49,6 @@ static uint8_t KbdReadScancode(void) {
|
||||
}
|
||||
|
||||
static char KbdReadChar(void) {
|
||||
if (gHead == gTail) return 0;
|
||||
uint8_t sc = KbdReadScancode();
|
||||
if (sc < 128) return gScanToAscii[sc];
|
||||
return 0;
|
||||
|
||||
+127
-10
@@ -81,6 +81,12 @@ static uintptr_t gLapicPhys = 0xFEE00000;
|
||||
static uintptr_t gIoapicPhys = 0xFEC00000;
|
||||
static uintptr_t gHpetPhys = 0xFED00000;
|
||||
|
||||
// ACPI power-off state (filled in by ParseFadt / ScanDsdtForS5)
|
||||
static uint16_t gPm1aCntBlk = 0;
|
||||
static uint16_t gPm1bCntBlk = 0;
|
||||
static uint8_t gSlpTypA = 5; // S5 SLP_TYP for PM1a; 5 is correct for QEMU/most VMs
|
||||
static uint8_t gSlpTypB = 5; // S5 SLP_TYP for PM1b
|
||||
|
||||
int debugEnabled = 0;
|
||||
int _fltused; // fuckass
|
||||
static void hal_putchar(char c) {
|
||||
@@ -99,6 +105,101 @@ static void *HalPhysToVirt(uint64_t phys) {
|
||||
return (void *)(phys + gPhysBase);
|
||||
}
|
||||
|
||||
// FADT (Fixed ACPI Description Table) - partial layout up to PM1b_CNT_BLK.
|
||||
// We only need offsets 40 (DSDT ptr), 64 (PM1a_CNT_BLK), 68 (PM1b_CNT_BLK).
|
||||
// The full table is ~276 bytes (ACPI 2.0+); we never access beyond offset 68.
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
ACPISDTHeader Header; // 0 (36 bytes)
|
||||
uint32_t FirmwareCtrl; // 36
|
||||
uint32_t Dsdt; // 40 - physical addr of DSDT
|
||||
uint8_t Reserved1; // 44
|
||||
uint8_t PreferredPmProfile; // 45
|
||||
uint16_t SciInt; // 46
|
||||
uint32_t SmiCmd; // 48
|
||||
uint8_t AcpiEnable; // 52
|
||||
uint8_t AcpiDisable; // 53
|
||||
uint8_t S4BiosReq; // 54
|
||||
uint8_t PstateCnt; // 55
|
||||
uint32_t Pm1aEvtBlk; // 56
|
||||
uint32_t Pm1bEvtBlk; // 60
|
||||
uint32_t Pm1aCntBlk; // 64 - I/O port for PM1a control register
|
||||
uint32_t Pm1bCntBlk; // 68 - I/O port for PM1b control register
|
||||
} FADT;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values.
|
||||
// The encoding looks like:
|
||||
// 0x08 '_' 'S' '5' '_' ; NameOp + "_S5_"
|
||||
// 0x12 <PkgLen> ; PackageOp + encoded length
|
||||
// <NumElements> ; usually 0x04
|
||||
// 0x0A <slp_typ_a> ; BytePrefix + value (or 0x00 for zero)
|
||||
// 0x0A <slp_typ_b>
|
||||
static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) {
|
||||
for (uint32_t i = 0; i + 9 < len; i++) {
|
||||
// Match NameOp + "_S5_" + PackageOp
|
||||
if (aml[i] != 0x08) continue;
|
||||
if (aml[i+1] != 0x5F || aml[i+2] != 0x53 ||
|
||||
aml[i+3] != 0x35 || aml[i+4] != 0x5F) continue;
|
||||
if (aml[i+5] != 0x12) continue;
|
||||
|
||||
// PkgLength encoding: low 6 bits = byte count (0 → only this byte);
|
||||
// bits [7:6] = number of following bytes in the length field.
|
||||
uint8_t pkgByte = aml[i+6];
|
||||
uint32_t pkgExtra = pkgByte >> 6; // 0..3 extra bytes
|
||||
uint32_t j = i + 7 + pkgExtra; // skip PkgLen field
|
||||
if (j + 2 >= len) break;
|
||||
j++; // skip NumElements byte
|
||||
|
||||
// SLP_TYP_A
|
||||
if (aml[j] == 0x0A && j + 1 < len) {
|
||||
gSlpTypA = aml[j + 1]; j += 2;
|
||||
} else if (aml[j] == 0x00) {
|
||||
gSlpTypA = 0; j++;
|
||||
} else {
|
||||
break; // unexpected encoding - keep defaults
|
||||
}
|
||||
|
||||
// SLP_TYP_B
|
||||
if (j < len && aml[j] == 0x0A && j + 1 < len)
|
||||
gSlpTypB = aml[j + 1];
|
||||
else if (j < len && aml[j] == 0x00)
|
||||
gSlpTypB = 0;
|
||||
|
||||
HalDbgPrint("HAL: _S5_ SLP_TYP_A=0x%02x SLP_TYP_B=0x%02x\n",
|
||||
gSlpTypA, gSlpTypB);
|
||||
return;
|
||||
}
|
||||
HalDbgPrint("HAL: _S5_ not found in DSDT, using defaults (A=0x%02x B=0x%02x)\n",
|
||||
gSlpTypA, gSlpTypB);
|
||||
}
|
||||
|
||||
static int validate_checksum(void* table, uint32_t length) {
|
||||
uint8_t sum = 0;
|
||||
uint8_t* bytes = (uint8_t*)table;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sum += bytes[i];
|
||||
}
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
static void ParseFadt(const FADT* fadt) {
|
||||
gPm1aCntBlk = (uint16_t)fadt->Pm1aCntBlk;
|
||||
gPm1bCntBlk = (uint16_t)fadt->Pm1bCntBlk;
|
||||
HalDbgPrint("HAL: FADT PM1a_CNT=0x%04x PM1b_CNT=0x%04x DSDT=0x%08x\n",
|
||||
gPm1aCntBlk, gPm1bCntBlk, fadt->Dsdt);
|
||||
|
||||
if (fadt->Dsdt) {
|
||||
const ACPISDTHeader* dsdt =
|
||||
(const ACPISDTHeader*)((uintptr_t)fadt->Dsdt + gPhysBase);
|
||||
if (memcmp(dsdt->Signature, "DSDT", 4) == 0 &&
|
||||
validate_checksum((void*)dsdt, dsdt->Length)) {
|
||||
const uint8_t* aml = (const uint8_t*)(dsdt + 1);
|
||||
ScanDsdtForS5(aml, dsdt->Length - (uint32_t)sizeof(ACPISDTHeader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseMadt(MADT* madt) {
|
||||
gLapicPhys = madt->LocalAPICAddress;
|
||||
uint8_t* p = (uint8_t *)(madt + 1);
|
||||
@@ -112,15 +213,6 @@ static void ParseMadt(MADT* madt) {
|
||||
}
|
||||
}
|
||||
|
||||
static int validate_checksum(void* table, uint32_t length) {
|
||||
uint8_t sum = 0;
|
||||
uint8_t* bytes = (uint8_t*)table;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sum += bytes[i];
|
||||
}
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
static void WalkAcpi(uintptr_t rsdpPhys) {
|
||||
if (!rsdpPhys) return;
|
||||
|
||||
@@ -188,7 +280,11 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
|
||||
// Parse MADT
|
||||
if (memcmp(header->Signature, "APIC", 4) == 0) {
|
||||
ParseMadt((MADT*)header);
|
||||
}
|
||||
}
|
||||
// Parse FADT (signature "FACP") for PM1 control ports and DSDT pointer
|
||||
else if (memcmp(header->Signature, "FACP", 4) == 0) {
|
||||
ParseFadt((const FADT*)header);
|
||||
}
|
||||
// Parse HPET
|
||||
else if (memcmp(header->Signature, "HPET", 4) == 0) {
|
||||
// HPET base address block is located at offset 44
|
||||
@@ -198,6 +294,25 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HalShutdown - ACPI S5 soft power-off. Does not return.
|
||||
// ---------------------------------------------------------------------------
|
||||
#include <intrin.h>
|
||||
|
||||
static void HalShutdown(void) {
|
||||
_disable();
|
||||
|
||||
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10)
|
||||
uint16_t valA = (uint16_t)((gSlpTypA << 10) | (1 << 13));
|
||||
uint16_t valB = (uint16_t)((gSlpTypB << 10) | (1 << 13));
|
||||
|
||||
if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA);
|
||||
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
|
||||
|
||||
// Firmware should have powered off by now. If it didn't, halt.
|
||||
for (;;) __halt();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
|
||||
if (gInitialised) return STATUS_ALREADY_EXISTS;
|
||||
@@ -236,6 +351,8 @@ KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
|
||||
gApi.Intc.UnmaskIrq = HalApicUnmaskIrq;
|
||||
gApi.Intc.SendEoi = HalApicSendEoi;
|
||||
|
||||
gApi.Power.Shutdown = HalShutdown;
|
||||
|
||||
gApi.Version = HAL_API_VERSION;
|
||||
gInitialised = 1;
|
||||
|
||||
|
||||
+50
-4
@@ -45,9 +45,47 @@ static void ScreenPutPixel(int x, int y, uint32_t color) {
|
||||
}
|
||||
|
||||
static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) {
|
||||
for (int row = 0; row < h; row++)
|
||||
for (int col = 0; col < w; col++)
|
||||
PutPixelRaw(x + col, y + row, pixels[row * w + col]);
|
||||
// Calculate clipping bounds
|
||||
int startX = (x < 0) ? 0 : x;
|
||||
int startY = (y < 0) ? 0 : y;
|
||||
int endX = (x + w > gWidth) ? gWidth : (x + w);
|
||||
int endY = (y + h > gHeight) ? gHeight : (y + h);
|
||||
|
||||
int drawW = endX - startX;
|
||||
int drawH = endY - startY;
|
||||
|
||||
// If the image is entirely off-screen, do nothing
|
||||
if (drawW <= 0 || drawH <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the source pointer if the image was clipped on the top or left
|
||||
int srcOffsetX = startX - x;
|
||||
int srcOffsetY = startY - y;
|
||||
|
||||
// Calculate bytes to copy per row
|
||||
size_t bytesPerRow = (size_t)drawW * sizeof(uint32_t);
|
||||
|
||||
// If the width we are drawing exactly matches the physical screen stride,
|
||||
// AND it matches the original source image width, memory is perfectly contiguous.
|
||||
if (drawW == gStride && drawW == w) {
|
||||
uint32_t* dst = gFb + (startY * gStride) + startX;
|
||||
const uint32_t* src = pixels + (srcOffsetY * w) + srcOffsetX;
|
||||
|
||||
memcpy(dst, src, bytesPerRow * drawH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy row-by-row
|
||||
for (int row = 0; row < drawH; row++) {
|
||||
// Destination in the actual framebuffer (accounting for stride)
|
||||
uint32_t* dst = gFb + ((startY + row) * gStride) + startX;
|
||||
|
||||
// Source from the provided pixel buffer
|
||||
const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX;
|
||||
|
||||
memcpy(dst, src, bytesPerRow);
|
||||
}
|
||||
}
|
||||
|
||||
static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; }
|
||||
@@ -68,7 +106,8 @@ static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) {
|
||||
for (int row = 0; row < 16; row++) {
|
||||
uint8_t bits = glyph[row];
|
||||
for (int col = 0; col < 8; col++) {
|
||||
uint32_t color = (bits & (1 << col)) ? fg : bg; // i don't know why, i don't want to know why, i shouldn't have to wonder why but the font is backwards unless we do this terriblness
|
||||
uint32_t color = (bits & (1 << col)) ? fg : bg; // i don't know why, i don't want to know why
|
||||
// i shouldn't have to wonder why but the font is backwards unless we do this terriblness
|
||||
PutPixelRaw(x + col, y + row, color);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +127,13 @@ static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
|
||||
gCursorY++;
|
||||
} else if (c == '\r') {
|
||||
gCursorX = 0;
|
||||
} else if (c == '\b') {
|
||||
if (gCursorX > 0) {
|
||||
gCursorX--;
|
||||
} else if (gCursorY > 0) {
|
||||
gCursorY--;
|
||||
gCursorX = cols - 1;
|
||||
}
|
||||
} else {
|
||||
ScreenPutChar(c, gCursorX * FONT_W, gCursorY * FONT_H, fg, bg);
|
||||
if (++gCursorX >= cols) { gCursorX = 0; gCursorY++; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// ke_process.c - process model
|
||||
#include "ke_process.h"
|
||||
#include "ke_thread.h"
|
||||
#include "ke_event.h"
|
||||
#include "mm_virt.h"
|
||||
#include "ob_object.h"
|
||||
#include <string.h>
|
||||
@@ -40,6 +41,12 @@ static void ProcessDestroy(OBJECT* obj) {
|
||||
}
|
||||
}
|
||||
|
||||
// Release the exit event.
|
||||
if (proc->ExitEvent) {
|
||||
ObDereferenceObject(&proc->ExitEvent->Header);
|
||||
proc->ExitEvent = (EVENT *)0;
|
||||
}
|
||||
|
||||
// Remove from global list.
|
||||
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
|
||||
ListRemove(&proc->GlobalLink);
|
||||
@@ -76,6 +83,12 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
|
||||
LIST_INIT(&proc->GlobalLink);
|
||||
|
||||
// Create the manual-reset exit event (initially unsignaled).
|
||||
if (!KSUCCESS(KeEventCreate(EventManualReset, 0, &proc->ExitEvent))) {
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
|
||||
ListInsertTail(&gProcessList, &proc->GlobalLink);
|
||||
KeReleaseSpinlock(&gProcessListLock, flags);
|
||||
@@ -90,11 +103,22 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
void KeExitProcess(PROCESS* process, int exitCode) {
|
||||
if (!process) return;
|
||||
process->ExitCode = exitCode;
|
||||
// Wake any threads waiting for this process to finish.
|
||||
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
|
||||
// The last thread exiting via KeExitThread will drop the process refcount.
|
||||
// This function is a policy hook; actual cleanup is in ProcessDestroy.
|
||||
ObDereferenceObject(&process->Header);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KeGetProcessExitCode
|
||||
// ---------------------------------------------------------------------------
|
||||
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut) {
|
||||
if (!process || !exitCodeOut) return STATUS_INVALID_PARAMETER;
|
||||
*exitCodeOut = process->ExitCode;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handle table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ob_object.h"
|
||||
#include "ke_list.h"
|
||||
#include "ke_spinlock.h"
|
||||
#include "ke_event.h"
|
||||
|
||||
#define PROCESS_NAME_MAX 64
|
||||
#define HANDLE_TABLE_SIZE 256
|
||||
@@ -26,11 +27,17 @@ typedef struct PROCESS {
|
||||
SPINLOCK HandleLock;
|
||||
|
||||
int ExitCode;
|
||||
|
||||
/* Signaled (manual-reset) when the process exits.
|
||||
Waiters block on this via KeWaitForEvent. */
|
||||
EVENT* ExitEvent;
|
||||
|
||||
LIST_ENTRY GlobalLink; /* node in the global process list */
|
||||
} PROCESS;
|
||||
|
||||
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
|
||||
void KeExitProcess(PROCESS* process, int exitCode);
|
||||
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
|
||||
|
||||
/* Handle table operations. */
|
||||
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
|
||||
|
||||
+11
-4
@@ -158,11 +158,11 @@ void KeYield(void) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||
cur->SchedLockFlags = flags;
|
||||
|
||||
// Issue 4 Fix: Deleted misleading voluntary promotion from here.
|
||||
|
||||
THREAD* next = PickNext();
|
||||
if (next == cur) {
|
||||
if (cur != gIdleThread)
|
||||
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead caller
|
||||
// has already placed SchedulerLink elsewhere (or is discarding the thread).
|
||||
if (cur != gIdleThread && cur->State == ThreadRunning)
|
||||
ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
|
||||
KeReleaseSpinlock(&gSchedLock, flags);
|
||||
return;
|
||||
@@ -171,8 +171,15 @@ void KeYield(void) {
|
||||
next->State = ThreadRunning;
|
||||
gCurrentThread = next;
|
||||
|
||||
if (cur != gIdleThread && cur->State == ThreadReady)
|
||||
// Only re-queue the outgoing thread if it is still in the running state.
|
||||
// If KeBlockThread set State = ThreadWaiting, SchedulerLink was already
|
||||
// inserted into the sync object's wait list — inserting it here too would
|
||||
// corrupt both lists simultaneously (same intrusive node in two lists).
|
||||
// If KeExitThread set State = ThreadDead, the thread must not be re-queued.
|
||||
if (cur != gIdleThread && cur->State == ThreadRunning) {
|
||||
cur->State = ThreadReady;
|
||||
ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
|
||||
}
|
||||
|
||||
gOutgoingThread = cur;
|
||||
KeSwitchContext(cur, next);
|
||||
|
||||
+25
-15
@@ -6,20 +6,30 @@
|
||||
|
||||
typedef uint32_t KSTATUS;
|
||||
|
||||
#define STATUS_SUCCESS ((KSTATUS)0x00000000)
|
||||
#define STATUS_FAILURE ((KSTATUS)0x00000001)
|
||||
#define STATUS_INVALID_PARAMETER ((KSTATUS)0x00000002)
|
||||
#define STATUS_NO_MEMORY ((KSTATUS)0x00000003)
|
||||
#define STATUS_NOT_FOUND ((KSTATUS)0x00000004)
|
||||
#define STATUS_ACCESS_DENIED ((KSTATUS)0x00000005)
|
||||
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x00000006)
|
||||
#define STATUS_NOT_SUPPORTED ((KSTATUS)0x00000007)
|
||||
#define STATUS_TIMEOUT ((KSTATUS)0x00000008)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x00000009)
|
||||
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0000000A)
|
||||
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0000000B)
|
||||
#define STATUS_QUEUE_FULL ((KSTATUS)0x0000000C)
|
||||
#define STATUS_QUEUE_EMPTY ((KSTATUS)0x0000000D)
|
||||
#define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0000000E)
|
||||
// --- General & Control (0x0000) ---
|
||||
#define STATUS_SUCCESS ((KSTATUS)0x0000)
|
||||
#define STATUS_FAILURE ((KSTATUS)0x0001)
|
||||
#define STATUS_NOT_SUPPORTED ((KSTATUS)0x0002)
|
||||
#define STATUS_TIMEOUT ((KSTATUS)0x0003)
|
||||
|
||||
// --- Validation & Parameters (0x0010) ---
|
||||
#define STATUS_INVALID_PARAMETER ((KSTATUS)0x0010)
|
||||
#define STATUS_INVALID_HANDLE ((KSTATUS)0x0011)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x0012)
|
||||
#define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0013)
|
||||
|
||||
// --- Resource & Access (0x0020) ---
|
||||
#define STATUS_NO_MEMORY ((KSTATUS)0x0020)
|
||||
#define STATUS_ACCESS_DENIED ((KSTATUS)0x0021)
|
||||
#define STATUS_NOT_FOUND ((KSTATUS)0x0022)
|
||||
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x0023)
|
||||
|
||||
// --- State & Lifecycle (0x0030) ---
|
||||
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0030)
|
||||
#define STATUS_QUEUE_FULL ((KSTATUS)0x0031)
|
||||
#define STATUS_QUEUE_EMPTY ((KSTATUS)0x0032)
|
||||
|
||||
// --- Hardware & External (0x0040) ---
|
||||
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0040)
|
||||
|
||||
#define KSUCCESS(s) ((s) == STATUS_SUCCESS)
|
||||
@@ -1,6 +1,6 @@
|
||||
; ke_switch.asm - context switch and thread entry stubs (MASM64 / ML64)
|
||||
;
|
||||
; Generated struct offsets (regenerate with gen_offsets.exe if structs change):
|
||||
; Struct offsets:
|
||||
; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8)
|
||||
; THREAD.SavedContext offset = 88
|
||||
; THREAD.TssRsp0 offset = 104
|
||||
@@ -8,6 +8,7 @@
|
||||
;
|
||||
THREAD_SavedContext EQU 88
|
||||
THREAD_TssRsp0 EQU 104
|
||||
THREAD_Teb EQU 112
|
||||
TSS_RSP0_OFFSET EQU 4
|
||||
|
||||
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
|
||||
@@ -33,6 +34,16 @@ KeSwitchContext PROC
|
||||
; rsp = to->SavedContext
|
||||
mov rsp, [rdx + THREAD_SavedContext]
|
||||
|
||||
; Update MSR_GS_BASE (0xC0000101) with to->Teb
|
||||
mov rax, [rdx + THREAD_Teb] ; Fetch the incoming TEB pointer
|
||||
mov ecx, 0C0000101h ; IA32_GS_BASE MSR
|
||||
mov r8, rdx ; Save RDX ('to' thread) as WRMSR clobbers EDX
|
||||
mov rdx, rax ; Copy TEB pointer to RDX
|
||||
shr rdx, 32 ; EDX = High 32 bits of TEB
|
||||
; EAX = Low 32 bits of TEB (upper 32 bits ignored by CPU)
|
||||
wrmsr ; Write to MSR
|
||||
mov rdx, r8 ; Restore RDX
|
||||
|
||||
; Update TSS.RSP0 so that the next interrupt/syscall uses the
|
||||
; incoming thread's kernel stack top.
|
||||
mov rax, [rdx + THREAD_TssRsp0]
|
||||
@@ -65,6 +76,8 @@ KeSwitchContext ENDP
|
||||
; We load RCX from the stack (first arg per MS x64), then IRETQ.
|
||||
; -------------------------------------------------------------------------
|
||||
EXTERN KeSchedulerPostSwitch : PROC
|
||||
EXTERN KeCurrentThread : PROC
|
||||
EXTERN KeExitThread : PROC
|
||||
|
||||
KeThreadEntry PROC
|
||||
sub rsp, 32 ; Reserve shadow space
|
||||
@@ -74,4 +87,45 @@ KeThreadEntry PROC
|
||||
iretq ; -> ring 3
|
||||
KeThreadEntry ENDP
|
||||
|
||||
; -------------------------------------------------------------------------
|
||||
; KeKernelThreadEntry - landing pad for kernel-mode threads created by
|
||||
; KeCreateKernelThread. Never touches ring 3; no IRETQ.
|
||||
;
|
||||
; Stack layout when we arrive here (above the KCONTEXT already popped by
|
||||
; KeSwitchContext's ret):
|
||||
; [rsp+00] fn ptr (kernel function: void fn(void*))
|
||||
; [rsp+08] arg ptr
|
||||
;
|
||||
; We pop both into non-volatile registers so KeSchedulerPostSwitch can
|
||||
; clobber volatile ones freely, then call fn(arg). On return we call
|
||||
; KeExitThread so the thread is cleanly destroyed.
|
||||
; -------------------------------------------------------------------------
|
||||
KeKernelThreadEntry PROC
|
||||
pop rbx ; rbx = fn ptr (non-volatile, survives the call below)
|
||||
pop r12 ; r12 = arg (non-volatile)
|
||||
|
||||
sub rsp, 32
|
||||
call KeSchedulerPostSwitch ; release scheduler spinlock, reap dead threads
|
||||
add rsp, 32
|
||||
|
||||
mov rcx, r12 ; first arg = arg
|
||||
sub rsp, 32
|
||||
call rbx ; fn(arg) -- still ring 0
|
||||
add rsp, 32
|
||||
|
||||
; Thread function returned -- exit cleanly.
|
||||
sub rsp, 32
|
||||
call KeCurrentThread ; rax = current THREAD*
|
||||
add rsp, 32
|
||||
mov rcx, rax ; arg1 = thread*
|
||||
xor edx, edx ; arg2 = exitCode 0
|
||||
sub rsp, 32
|
||||
call KeExitThread
|
||||
add rsp, 32
|
||||
|
||||
; KeExitThread -> KeYield -> KeSwitchContext never returns here, but
|
||||
; the assembler needs a defined end.
|
||||
@@: jmp @B
|
||||
KeKernelThreadEntry ENDP
|
||||
|
||||
END
|
||||
@@ -1,4 +1,4 @@
|
||||
; ke_syscall.asm - INT 0x2E kernel entry (MASM64)
|
||||
; ke_syscall.asm - INT 0x40 kernel entry (MASM64)
|
||||
;
|
||||
; On entry the CPU has pushed (ring-3 -> ring-0 transition):
|
||||
; SS, RSP, RFLAGS, CS, RIP (on the kernel stack via TSS.RSP0)
|
||||
|
||||
+665
-276
File diff suppressed because it is too large.
Load diff
+84
-1
@@ -66,7 +66,18 @@ KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
thread->UserStackBase = ustack;
|
||||
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE;
|
||||
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8; // i don't know why, i don't want to know why
|
||||
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness
|
||||
// this magic number will hunt me in my dreams
|
||||
|
||||
// Initialize the dummy TEB
|
||||
// offset 0x10 is StackLimit, which __chkstk checks.
|
||||
uint64_t* teb = (uint64_t*)ustack;
|
||||
teb[0] = 0; // ExceptionList (GS:0x00)
|
||||
teb[1] = 0; // StackBase (GS:0x08)
|
||||
teb[2] = 0; // StackLimit (GS:0x10) - __chkstk will see 0 and safely exit
|
||||
|
||||
thread->Teb = teb;
|
||||
|
||||
thread->UserEntry = entry;
|
||||
thread->UserArg = arg;
|
||||
@@ -108,6 +119,78 @@ void KeExitThread(THREAD* thread, int code) {
|
||||
KeYield();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KeCreateKernelThread
|
||||
// ---------------------------------------------------------------------------
|
||||
// Kernel threads never go to ring 3: they have no user stack, no TEB, and
|
||||
// their entry stub (KeKernelThreadEntry) calls the function directly then
|
||||
// calls KeExitThread instead of IRETQ-ing.
|
||||
//
|
||||
// Stack layout built here (from KernelStackTop downward):
|
||||
// [ksp - 8 ] arg <-- popped by KeKernelThreadEntry as r12
|
||||
// [ksp - 16] fn <-- popped by KeKernelThreadEntry as rbx
|
||||
// [ksp - 16 - sizeof(KCONTEXT)] KCONTEXT { .Rip = KeKernelThreadEntry }
|
||||
// ---------------------------------------------------------------------------
|
||||
extern void KeKernelThreadEntry(void);
|
||||
|
||||
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
|
||||
PROCESS* owner = idleThread ? idleThread->Owner : (PROCESS*)0;
|
||||
if (!owner) return STATUS_INVALID_PARAMETER;
|
||||
if (priority >= PriorityCount) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
THREAD* thread = (THREAD*)ObAllocateObject(ObjectTypeThread, sizeof(THREAD));
|
||||
if (!thread) return STATUS_NO_MEMORY;
|
||||
|
||||
thread->Header.Destroy = ThreadDestroy;
|
||||
thread->ThreadId = _InterlockedExchangeAdd((volatile long*)&gNextThreadId, 1);
|
||||
thread->State = ThreadReady;
|
||||
thread->Priority = priority;
|
||||
thread->QuantaConsumed = 0;
|
||||
thread->WaitObject = (OBJECT*)0;
|
||||
thread->SchedLockFlags = 0x202ULL;
|
||||
thread->UserStackBase = (void*)0; // no user stack
|
||||
thread->Teb = (void*)0; // no TEB; GS_BASE will be 0 in kernel ctx
|
||||
|
||||
void* kstack = MmAllocatePool(KSTACK_SIZE);
|
||||
if (!kstack) {
|
||||
ObDereferenceObject(&thread->Header);
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
memset(kstack, 0, KSTACK_SIZE);
|
||||
thread->KernelStackBase = kstack;
|
||||
thread->KernelStackTop = (uintptr_t)kstack + KSTACK_SIZE;
|
||||
thread->TssRsp0 = thread->KernelStackTop;
|
||||
|
||||
// Build the initial stack: arg, fn, then KCONTEXT.
|
||||
uintptr_t* ksp = (uintptr_t*)thread->KernelStackTop;
|
||||
*(--ksp) = arg; // popped second (-> r12)
|
||||
*(--ksp) = fn; // popped first (-> rbx)
|
||||
|
||||
ksp -= sizeof(KCONTEXT) / sizeof(uintptr_t);
|
||||
KCONTEXT* ctx = (KCONTEXT*)ksp;
|
||||
memset(ctx, 0, sizeof(KCONTEXT));
|
||||
ctx->Rip = (uint64_t)KeKernelThreadEntry;
|
||||
thread->SavedContext = ctx;
|
||||
|
||||
// Register with the owning process.
|
||||
ObReferenceObject(&owner->Header);
|
||||
thread->Owner = owner;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&owner->HandleLock);
|
||||
ListInsertTail(&owner->ThreadList, &thread->ProcessLink);
|
||||
owner->ThreadCount++;
|
||||
KeReleaseSpinlock(&owner->HandleLock, flags);
|
||||
|
||||
if (out) *out = thread;
|
||||
|
||||
KeReadyThread(thread);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void KeBlockThread(THREAD* thread, OBJECT* waitObject) {
|
||||
thread->State = ThreadWaiting;
|
||||
thread->WaitObject = waitObject;
|
||||
|
||||
+13
-3
@@ -46,7 +46,7 @@ typedef struct THREAD {
|
||||
LIST_ENTRY SchedulerLink; /* node in run queue or wait list */
|
||||
|
||||
// Kernel stack - never PAGE_USER, pointed to by TssRsp0.
|
||||
void* KernelStackBase;
|
||||
void* KernelStackBase;
|
||||
uintptr_t KernelStackTop;
|
||||
|
||||
// Saved context pointer - set by KeSwitchContext.
|
||||
@@ -58,17 +58,27 @@ typedef struct THREAD {
|
||||
// Updated on every context switch so the CPU loads the right kernel stack.
|
||||
uintptr_t TssRsp0;
|
||||
|
||||
// Thread Environment Block
|
||||
void* Teb;
|
||||
|
||||
// Ring-3 entry point and argument for newly created threads.
|
||||
uintptr_t UserEntry;
|
||||
uintptr_t UserArg;
|
||||
|
||||
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
|
||||
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
|
||||
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
|
||||
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
|
||||
} THREAD;
|
||||
|
||||
KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
|
||||
THREAD_PRIORITY priority, THREAD **out);
|
||||
|
||||
// Create a kernel-mode-only thread. fn is called directly in ring 0 with
|
||||
// arg; when fn returns the thread exits automatically. No user stack or TEB
|
||||
// is allocated. The thread is owned by the idle process and runs at the
|
||||
// given priority.
|
||||
KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
|
||||
THREAD_PRIORITY priority, THREAD **out);
|
||||
|
||||
// Mark a thread dead and deschedule it permanently.
|
||||
void KeExitThread(THREAD* thread, int code);
|
||||
|
||||
|
||||
+64
-32
@@ -74,6 +74,10 @@ typedef struct {
|
||||
|
||||
typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *);
|
||||
|
||||
// HAL pointer cached at boot so kernel threads can reach it without
|
||||
// threading a parameter through every call chain.
|
||||
static const HAL_API* gKmainHal;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Phase Helper: Resolve Imports for all registered modules
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -210,7 +214,7 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Phase Helper: Load a DLL from disk and register it as a system module
|
||||
// Phase Helper: Load a DLL from disk and register it
|
||||
// ---------------------------------------------------------------------------
|
||||
static void LoadAndRegisterDll(const char* filename) {
|
||||
KSTATUS s;
|
||||
@@ -219,8 +223,8 @@ static void LoadAndRegisterDll(const char* filename) {
|
||||
// Read the DLL from disk
|
||||
s = IoOpenFile(filename, &f);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
DbgPrint("KernelMain: Failed to open critical system DLL: %s\n", filename);
|
||||
_disable(); while(1) __halt();
|
||||
DbgPrint("KernelMain: Failed to open DLL: %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
@@ -229,7 +233,7 @@ static void LoadAndRegisterDll(const char* filename) {
|
||||
if (!KSUCCESS(IoReadFile(f, mem, size, &got))) {
|
||||
DbgPrint("KernelMain: Failed to read %s\n", filename);
|
||||
IoCloseFile(f);
|
||||
_disable(); while(1) __halt();
|
||||
return;
|
||||
}
|
||||
IoCloseFile(f);
|
||||
|
||||
@@ -239,7 +243,7 @@ static void LoadAndRegisterDll(const char* filename) {
|
||||
s = LdrLoadImage(mem, size, &mappedBase, true);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KernelMain: Failed to map %s\n", filename);
|
||||
_disable(); while(1) __halt();
|
||||
return;
|
||||
}
|
||||
|
||||
MmFreePool(mem); // Free the raw file buffer, we only need the mapped image now
|
||||
@@ -248,18 +252,20 @@ static void LoadAndRegisterDll(const char* filename) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Phase Helper: Load and launch the init process from the filesystem
|
||||
// Phase Helper: Load and launch a process from the filesystem.
|
||||
// Returns the new PROCESS* with one reference owned by the caller.
|
||||
// Returns NULL on failure.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void LaunchInitProcess(BOOT_INFO* bootInfo) {
|
||||
static PROCESS* LaunchUserProcess(const char* filePath) {
|
||||
KSTATUS s;
|
||||
FILE_OBJECT* f;
|
||||
|
||||
// Read the executable file into a raw buffer
|
||||
s = IoOpenFile(bootInfo->CommandLine, &f);
|
||||
s = IoOpenFile(filePath, &f);
|
||||
DbgPrint("KernelMain: IoOpenFile: %d\n", s);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
DbgPrint("Failed to load file: \"%s\"!\n", bootInfo->CommandLine);
|
||||
_disable(); while(1) __halt();
|
||||
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
|
||||
return (PROCESS*)0;
|
||||
}
|
||||
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
@@ -278,29 +284,46 @@ static void LaunchInitProcess(BOOT_INFO* bootInfo) {
|
||||
PROCESS* proc;
|
||||
THREAD* thread;
|
||||
|
||||
s = KeCreateProcess("init", &proc);
|
||||
s = KeCreateProcess(filePath, &proc);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("Failed to create init process. System halted.\n");
|
||||
_disable(); while(1) __halt();
|
||||
DbgPrint("Failed to create process. System halted.\n");
|
||||
return (PROCESS*)0;
|
||||
}
|
||||
DbgPrint("Created init process (PID %u) with command line: %s\n", proc->ProcessId, bootInfo->CommandLine);
|
||||
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
|
||||
|
||||
// Parse the raw PE file and map it into executable memory
|
||||
uintptr_t entryPoint = 0;
|
||||
s = LdrLoadImage(mem, size, &entryPoint, true);
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImage(mem, size, &imageBase, true);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KernelMain: LdrLoadImage failed with status: %d\n", s);
|
||||
_disable(); while(1) __halt();
|
||||
}
|
||||
DbgPrint("KernelMain: PE Image mapped! Entry point at 0x%llX\n", (void*)entryPoint);
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
DbgPrint("KernelMain: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint);
|
||||
|
||||
// Create the main thread, pointing it to the PE Entry Point
|
||||
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("KernelMain: Failed to create init thread. Status: %d\n", s);
|
||||
_disable(); while(1) __halt();
|
||||
DbgPrint("KernelMain: Failed to create thread. Status: %d\n", s);
|
||||
}
|
||||
DbgPrint("KernelMain: Thread created and queued!\n");
|
||||
|
||||
// Caller owns the reference returned by KeCreateProcess.
|
||||
return proc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Kernel thread: wait for a process to exit, then log a message and shut down.
|
||||
// Takes ownership of the PROCESS* reference passed as arg; releases it on exit.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void ProcessWatcherThread(void* arg) {
|
||||
PROCESS* proc = (PROCESS*)arg;
|
||||
KeWaitForEvent(proc->ExitEvent);
|
||||
DbgPrint("KernelMain: Process \"%s\" (PID %u) exited with code %d.\n",
|
||||
proc->Name, proc->ProcessId, proc->ExitCode);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
|
||||
DbgPrint("KernelMain: Shutting down.\n");
|
||||
gKmainHal->Power.Shutdown(); // does not return
|
||||
}
|
||||
|
||||
static void PrintProcessList() {
|
||||
@@ -360,6 +383,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
KSTATUS s = HalInitialize(&halParams);
|
||||
if (!KSUCCESS(s)) for (;;) __halt(); // there's nothing we can do.
|
||||
const HAL_API* hal = HalGetApi();
|
||||
gKmainHal = hal;
|
||||
|
||||
KeInitDebug(hal->Screen.PrintChar, bootInfo->DebugEnabled); // Enable debug
|
||||
|
||||
@@ -382,11 +406,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
// Scheduler Prep & Status Update
|
||||
//
|
||||
KeInitializeScheduler();
|
||||
// hal->Screen.Clear(0x000000);
|
||||
hal->Screen.Clear(0x000000);
|
||||
|
||||
DbgPrint("KernelMain: Loading system DLLs...\n");
|
||||
LoadAndRegisterDll("syscall.dll");
|
||||
LoadAndRegisterDll("win32.dll");
|
||||
LoadAndRegisterDll("bink2w64.dll");
|
||||
|
||||
// Now that they are in memory and registered, we link them together
|
||||
// (e.g., win32.dll resolving its imports against syscall.dll)
|
||||
@@ -395,7 +420,20 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
//
|
||||
// User-Mode Launch
|
||||
//
|
||||
LaunchInitProcess(bootInfo);
|
||||
PROCESS* shellProc = LaunchUserProcess(bootInfo->CommandLine);
|
||||
|
||||
// Spawn a kernel thread that blocks on the process exit event and prints
|
||||
// when it fires. The watcher takes ownership of the LaunchUserProcess
|
||||
// reference so we do not hold a dangling pointer in KernelMain.
|
||||
if (shellProc) {
|
||||
KSTATUS ws = KeCreateKernelThread((uintptr_t)ProcessWatcherThread,
|
||||
(uintptr_t)shellProc,
|
||||
PriorityNormal, (THREAD**)0);
|
||||
if (!KSUCCESS(ws)) {
|
||||
DbgPrint("KernelMain: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws);
|
||||
}
|
||||
// Ownership of shellProc transferred to watcher; do not use it below.
|
||||
}
|
||||
|
||||
//
|
||||
// Ignite the System
|
||||
@@ -403,18 +441,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
DbgPrint("KernelMain: Starting scheduler... Handing over control.\n");
|
||||
_enable();
|
||||
|
||||
float freeMB = (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f);
|
||||
float kernelMB = (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f);
|
||||
float userMB = (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f);
|
||||
|
||||
DbgPrint("Memory Status:\n");
|
||||
DbgPrint(" -> Free (PMM): %.2f MB\n", freeMB);
|
||||
DbgPrint(" -> Kernel Pool: %.2f MB\n", kernelMB);
|
||||
DbgPrint(" -> User Heap: %.2f MB\n", userMB);
|
||||
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));
|
||||
|
||||
while (1) {
|
||||
//DbgPrint("KernelMain: Idle loop. Free memory: %.2f MB.\n",
|
||||
//(float)(MmFreeFrameCount() * PAGE_SIZE) / (1024.0f * 1024.0f));
|
||||
__halt(); // nothing to do
|
||||
KeYield(); // nothing to do
|
||||
}
|
||||
}
|
||||
@@ -299,6 +299,6 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
|
||||
KSTATUS status = LdrResolveImports((uintptr_t)mem);
|
||||
if (status != STATUS_SUCCESS) return status;
|
||||
|
||||
*baseOut = (uintptr_t)mem + opt->AddressOfEntryPoint;
|
||||
*baseOut = (uintptr_t)mem;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
+108
-32
@@ -1,37 +1,113 @@
|
||||
#pragma once
|
||||
// syscall_nr.h - system call numbers
|
||||
// Shared between kernel (ke_syscall.c) and user mode (syscall.dll).
|
||||
// Keep in sync with the dispatch table in ke_syscall.c.
|
||||
// Keep in sync with gSyscallTable in ke_syscall.c and SYS_MAX below.
|
||||
//
|
||||
// Calling convention (INT 0x40, MS x64):
|
||||
// RAX = syscall number
|
||||
// RCX, RDX, R8, R9 = args 0-3
|
||||
// R10, R11 = args 4-5 (loaded by SYSCALL_STUB6 in syscall_stubs.asm)
|
||||
// Return value in RAX (KSTATUS or data).
|
||||
//
|
||||
// Handle semantics:
|
||||
// Every kernel object (file, mutex, semaphore, event, queue) is reference-
|
||||
// counted. SYS_CLOSE_HANDLE is the ONE way to close any handle regardless
|
||||
// of the object type behind it. KeCloseHandle calls ObDereferenceObject,
|
||||
// which fires the type-specific Destroy callback (e.g. FileDestroy)
|
||||
// automatically when the refcount reaches zero. There is no separate
|
||||
// SYS_CLOSE_FILE - that was redundant.
|
||||
|
||||
#define SYS_EXIT_THREAD 0
|
||||
#define SYS_EXIT_PROCESS 1
|
||||
#define SYS_SPAWN 2
|
||||
#define SYS_SLEEP 3
|
||||
#define SYS_YIELD 4
|
||||
#define SYS_ALLOC 5
|
||||
#define SYS_FREE 6
|
||||
#define SYS_CREATE_THREAD 7
|
||||
#define SYS_OPEN_HANDLE 8
|
||||
#define SYS_CLOSE_HANDLE 9
|
||||
#define SYS_DBG_PRINT 10
|
||||
#define SYS_MUTEX_CREATE 11
|
||||
#define SYS_MUTEX_ACQUIRE 12
|
||||
#define SYS_MUTEX_RELEASE 13
|
||||
#define SYS_SEM_CREATE 14
|
||||
#define SYS_SEM_WAIT 15
|
||||
#define SYS_SEM_SIGNAL 16
|
||||
#define SYS_EVENT_CREATE 17
|
||||
#define SYS_EVENT_SET 18
|
||||
#define SYS_EVENT_WAIT 19
|
||||
#define SYS_QUEUE_CREATE 20
|
||||
#define SYS_QUEUE_SEND 21
|
||||
#define SYS_QUEUE_RECV 22
|
||||
#define SYS_SCREEN_BLIT 23
|
||||
#define SYS_GET_TICK 24
|
||||
#define SYS_OPEN 25
|
||||
#define SYS_READ 26
|
||||
#define SYS_FCLOSE 27
|
||||
#define SYS_FSEEK 28
|
||||
#define SYS_FSIZE 29
|
||||
// ---------------------------------------------------------------------------
|
||||
// Process & Thread
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_EXIT_PROCESS 0 // (int exitCode) -> void
|
||||
#define SYS_EXIT_THREAD 1 // (int exitCode) -> void
|
||||
#define SYS_CREATE_THREAD 2 // (uint64_t entry, uint64_t arg, uint32_t priority, uint32_t* tidOut) -> KSTATUS
|
||||
#define SYS_GET_CURRENT_TID 3 // () -> uint32_t
|
||||
#define SYS_GET_CURRENT_PID 4 // () -> uint32_t
|
||||
#define SYS_YIELD 5 // () -> void
|
||||
#define SYS_SLEEP 6 // (uint32_t ms) -> void
|
||||
#define SYS_SET_THREAD_PRIORITY 7 // (uint32_t priority) -> KSTATUS
|
||||
|
||||
#define SYS_MAX 30
|
||||
// ---------------------------------------------------------------------------
|
||||
// Memory
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_ALLOC 8 // (size_t size) -> void* (NULL on failure)
|
||||
#define SYS_FREE 9 // (void* ptr, size_t size) -> void
|
||||
#define SYS_REALLOC 10 // (void* ptr, size_t newSize) -> void*
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Synchronisation Objects
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_MUTEX_CREATE 11 // (uint32_t* handleOut) -> KSTATUS
|
||||
#define SYS_MUTEX_ACQUIRE 12 // (uint32_t handle) -> KSTATUS
|
||||
#define SYS_MUTEX_TRY_ACQUIRE 13 // (uint32_t handle) -> KSTATUS
|
||||
#define SYS_MUTEX_RELEASE 14 // (uint32_t handle) -> KSTATUS
|
||||
|
||||
#define SYS_SEM_CREATE 15 // (int32_t initial, int32_t max, uint32_t* handleOut) -> KSTATUS
|
||||
#define SYS_SEM_WAIT 16 // (uint32_t handle) -> KSTATUS
|
||||
#define SYS_SEM_TRY_WAIT 17 // (uint32_t handle) -> KSTATUS
|
||||
#define SYS_SEM_SIGNAL 18 // (uint32_t handle) -> void
|
||||
|
||||
#define SYS_EVENT_CREATE 19 // (uint32_t type, uint32_t initialState, uint32_t* handleOut) -> KSTATUS
|
||||
#define SYS_EVENT_SIGNAL 20 // (uint32_t handle) -> void
|
||||
#define SYS_EVENT_CLEAR 21 // (uint32_t handle) -> void
|
||||
#define SYS_EVENT_WAIT 22 // (uint32_t handle) -> KSTATUS
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Message Queues
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_QUEUE_CREATE 23 // (uint32_t* handleOut) -> KSTATUS
|
||||
#define SYS_QUEUE_SEND 24 // (uint32_t handle, const QUEUE_MESSAGE* msg, uint32_t blocking) -> KSTATUS
|
||||
#define SYS_QUEUE_RECV 25 // (uint32_t handle, QUEUE_MESSAGE* msg, uint32_t blocking) -> KSTATUS
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handle Management
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_CLOSE_HANDLE 26 // (uint32_t handle) -> void
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// File I/O
|
||||
// (Close a file handle via SYS_CLOSE_HANDLE, same as any other object.)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_OPEN_FILE 27 // (const char* path, uint32_t* handleOut) -> KSTATUS
|
||||
#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
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen / Graphics
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_SCREEN_BLIT 31 // (int x, int y, int w, int h, const uint32_t* pixels) -> void
|
||||
#define SYS_SCREEN_CLEAR 32 // (uint32_t color) -> void
|
||||
#define SYS_SCREEN_GET_SIZE 33 // (int* widthOut, int* heightOut) -> void
|
||||
#define SYS_SCREEN_PUT_CHAR 34 // (char c, int x, int y, uint32_t fg, uint32_t bg) -> void
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Keyboard
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_KB_POLL 35 // () -> uint32_t (1 if key available, 0 otherwise)
|
||||
#define SYS_KB_READ_CHAR 36 // () -> char (blocks, yields while waiting)
|
||||
#define SYS_KB_READ_SCANCODE 37 // () -> uint8_t (blocks, yields while waiting)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_GET_TICK 38 // () -> uint64_t (milliseconds since boot)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Debug
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_DEBUG_PRINT 39 // (const char* str) -> void
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Process launch & lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_CREATE_PROCESS 40 // (const char* path, uint32_t* handleOut) -> KSTATUS
|
||||
#define SYS_WAIT_FOR_PROCESS 41 // (uint32_t handle) -> KSTATUS
|
||||
#define SYS_GET_PROCESS_EXIT_CODE 42 // (uint32_t handle, int* exitCodeOut) -> KSTATUS
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sentinel
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SYS_MAX 43
|
||||
+159
-29
@@ -1,47 +1,177 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
void BitBlt(int x, int y, int w, int h, const uint32_t* pixels);
|
||||
void* HeapAlloc(void* heap, unsigned long flags, size_t size);
|
||||
int HeapFree(void* heap, unsigned long flags, void* mem);
|
||||
// ---------------------------------------------------------------------------
|
||||
// Win32 imports
|
||||
// ---------------------------------------------------------------------------
|
||||
_Noreturn void ExitProcess(unsigned int code);
|
||||
_Noreturn void ExitThread(unsigned long code);
|
||||
void OutputDebugStringA(const char* s);
|
||||
|
||||
int main() {
|
||||
OutputDebugStringA("Hello from the shell!\n");
|
||||
int width = 300;
|
||||
int height = 300;
|
||||
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)
|
||||
|
||||
// Allocate space for uint32_t (4 bytes per pixel)
|
||||
uint32_t* image = (uint32_t *)HeapAlloc(NULL, 0, width * height * sizeof(uint32_t));
|
||||
|
||||
int tileSize = 8;
|
||||
HANDLE GetStdHandle(DWORD nStdHandle);
|
||||
BOOL ReadConsoleA(HANDLE hConsoleInput, void* lpBuffer,
|
||||
DWORD nNumberOfCharsToRead, DWORD* lpNumberOfCharsRead,
|
||||
void* pInputControl);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t color1 = 0xFFFFFFFF; // White
|
||||
uint32_t color2 = 0xFF000000; // Black
|
||||
typedef struct {
|
||||
DWORD cb;
|
||||
char* lpReserved;
|
||||
char* lpDesktop;
|
||||
char* lpTitle;
|
||||
DWORD dwX, dwY, dwXSize, dwYSize;
|
||||
DWORD dwXCountChars, dwYCountChars;
|
||||
DWORD dwFillAttribute, dwFlags;
|
||||
unsigned short wShowWindow, cbReserved2;
|
||||
unsigned char* lpReserved2;
|
||||
HANDLE hStdInput, hStdOutput, hStdError;
|
||||
} STARTUPINFOA;
|
||||
|
||||
int tileX = x / tileSize;
|
||||
int tileY = y / tileSize;
|
||||
typedef struct {
|
||||
HANDLE hProcess;
|
||||
HANDLE hThread;
|
||||
DWORD dwProcessId;
|
||||
DWORD dwThreadId;
|
||||
} PROCESS_INFORMATION;
|
||||
|
||||
if ((tileX + tileY) % 2 == 0) {
|
||||
image[y * width + x] = color1;
|
||||
} else {
|
||||
image[y * width + x] = color2;
|
||||
}
|
||||
BOOL CreateProcessA(const char* lpApp, char* lpCmdLine,
|
||||
void* lpPA, void* lpTA, BOOL bInherit,
|
||||
DWORD dwFlags, void* lpEnv, const char* lpDir,
|
||||
STARTUPINFOA* lpSI, PROCESS_INFORMATION* lpPI);
|
||||
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMs);
|
||||
BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode);
|
||||
BOOL CloseHandle(HANDLE hObject);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Output helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
static void print(const char* s) {
|
||||
OutputDebugStringA(s);
|
||||
}
|
||||
|
||||
static void printUInt(uint32_t n) {
|
||||
char buf[12];
|
||||
int i = 0;
|
||||
if (n == 0) { print("0"); return; }
|
||||
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
|
||||
char rev[12];
|
||||
int j = 0;
|
||||
while (i > 0) rev[j++] = buf[--i];
|
||||
rev[j] = '\0';
|
||||
print(rev);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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) {
|
||||
int pos = 0;
|
||||
while (1) {
|
||||
char c;
|
||||
DWORD nRead;
|
||||
ReadConsoleA(hIn, &c, 1, &nRead, 0);
|
||||
if (!nRead) continue;
|
||||
|
||||
if (c == '\r' || c == '\n') {
|
||||
print("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((c == '\b' || c == 127) && pos > 0) {
|
||||
pos--;
|
||||
print("\b \b");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c < 0x20 || c == 127)
|
||||
continue;
|
||||
|
||||
if (pos < len - 1) {
|
||||
buf[pos++] = c;
|
||||
char echo[2] = { c, '\0' };
|
||||
print(echo);
|
||||
}
|
||||
}
|
||||
buf[pos] = '\0';
|
||||
}
|
||||
|
||||
// Pass the 32-bit buffer directly to BitBlt
|
||||
BitBlt(600, 400, width, height, image);
|
||||
// ---------------------------------------------------------------------------
|
||||
// Trim leading/trailing spaces in-place; returns pointer into buf.
|
||||
// ---------------------------------------------------------------------------
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
while (len > 0 && s[len - 1] == ' ') len--;
|
||||
s[len] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
// for (size_t i = 0; i < 1000000000; i++) {
|
||||
// // burn!
|
||||
// }
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run one command. Returns 1 if the shell should exit.
|
||||
// ---------------------------------------------------------------------------
|
||||
static int runCommand(const char* cmd) {
|
||||
if (!cmd || *cmd == '\0')
|
||||
return 0;
|
||||
|
||||
OutputDebugStringA("Goodbye from the shell!\n");
|
||||
if (strcmp(cmd, "exit") == 0)
|
||||
return 1;
|
||||
|
||||
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)) {
|
||||
print("shell: could not launch '");
|
||||
print(cmd);
|
||||
print("'\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
DWORD exitCode = 0;
|
||||
GetExitCodeProcess(pi.hProcess, &exitCode);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
if (exitCode != 0) {
|
||||
print("shell: '");
|
||||
print(cmd);
|
||||
print("' exited with code ");
|
||||
printUInt(exitCode);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Entry point
|
||||
// ---------------------------------------------------------------------------
|
||||
int main() {
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: ready\n");
|
||||
|
||||
while (1) {
|
||||
print("> ");
|
||||
readLine(hIn, lineBuf, MAX_CMD);
|
||||
if (runCommand(trim(lineBuf)))
|
||||
break;
|
||||
}
|
||||
|
||||
print("shell: goodbye\n");
|
||||
ExitProcess(0);
|
||||
}
|
||||
+73
-31
@@ -1,32 +1,74 @@
|
||||
; syscall_nr.inc - assembled from syscall_nr.h for use in .asm files
|
||||
SYS_EXIT_THREAD EQU 0
|
||||
SYS_EXIT_PROCESS EQU 1
|
||||
SYS_SPAWN EQU 2
|
||||
SYS_SLEEP EQU 3
|
||||
SYS_YIELD EQU 4
|
||||
SYS_ALLOC EQU 5
|
||||
SYS_FREE EQU 6
|
||||
SYS_CREATE_THREAD EQU 7
|
||||
SYS_OPEN_HANDLE EQU 8
|
||||
SYS_CLOSE_HANDLE EQU 9
|
||||
SYS_DBG_PRINT EQU 10
|
||||
SYS_MUTEX_CREATE EQU 11
|
||||
SYS_MUTEX_ACQUIRE EQU 12
|
||||
SYS_MUTEX_RELEASE EQU 13
|
||||
SYS_SEM_CREATE EQU 14
|
||||
SYS_SEM_WAIT EQU 15
|
||||
SYS_SEM_SIGNAL EQU 16
|
||||
SYS_EVENT_CREATE EQU 17
|
||||
SYS_EVENT_SET EQU 18
|
||||
SYS_EVENT_WAIT EQU 19
|
||||
SYS_QUEUE_CREATE EQU 20
|
||||
SYS_QUEUE_SEND EQU 21
|
||||
SYS_QUEUE_RECV EQU 22
|
||||
SYS_SCREEN_BLIT EQU 23
|
||||
SYS_GET_TICK EQU 24
|
||||
SYS_OPEN EQU 25
|
||||
SYS_READ EQU 26
|
||||
SYS_FCLOSE EQU 27
|
||||
SYS_FSEEK EQU 28
|
||||
SYS_FSIZE EQU 29
|
||||
SYS_MAX EQU 30
|
||||
; Keep in sync with syscall_nr.h and ke_syscall.c.
|
||||
|
||||
; Process & Thread
|
||||
SYS_EXIT_PROCESS EQU 0
|
||||
SYS_EXIT_THREAD EQU 1
|
||||
SYS_CREATE_THREAD EQU 2
|
||||
SYS_GET_CURRENT_TID EQU 3
|
||||
SYS_GET_CURRENT_PID EQU 4
|
||||
SYS_YIELD EQU 5
|
||||
SYS_SLEEP EQU 6
|
||||
SYS_SET_THREAD_PRIORITY EQU 7
|
||||
|
||||
; Memory
|
||||
SYS_ALLOC EQU 8
|
||||
SYS_FREE EQU 9
|
||||
SYS_REALLOC EQU 10
|
||||
|
||||
; Mutex
|
||||
SYS_MUTEX_CREATE EQU 11
|
||||
SYS_MUTEX_ACQUIRE EQU 12
|
||||
SYS_MUTEX_TRY_ACQUIRE EQU 13
|
||||
SYS_MUTEX_RELEASE EQU 14
|
||||
|
||||
; Semaphore
|
||||
SYS_SEM_CREATE EQU 15
|
||||
SYS_SEM_WAIT EQU 16
|
||||
SYS_SEM_TRY_WAIT EQU 17
|
||||
SYS_SEM_SIGNAL EQU 18
|
||||
|
||||
; Event
|
||||
SYS_EVENT_CREATE EQU 19
|
||||
SYS_EVENT_SIGNAL EQU 20
|
||||
SYS_EVENT_CLEAR EQU 21
|
||||
SYS_EVENT_WAIT EQU 22
|
||||
|
||||
; Queue
|
||||
SYS_QUEUE_CREATE EQU 23
|
||||
SYS_QUEUE_SEND EQU 24
|
||||
SYS_QUEUE_RECV EQU 25
|
||||
|
||||
; Handle (unified - one close for every object type)
|
||||
SYS_CLOSE_HANDLE EQU 26
|
||||
|
||||
; File I/O
|
||||
SYS_OPEN_FILE EQU 27
|
||||
SYS_READ_FILE EQU 28
|
||||
SYS_SEEK_FILE EQU 29
|
||||
SYS_GET_FILE_SIZE EQU 30
|
||||
|
||||
; Screen
|
||||
SYS_SCREEN_BLIT EQU 31
|
||||
SYS_SCREEN_CLEAR EQU 32
|
||||
SYS_SCREEN_GET_SIZE EQU 33
|
||||
SYS_SCREEN_PUT_CHAR EQU 34
|
||||
|
||||
; Keyboard
|
||||
SYS_KB_POLL EQU 35
|
||||
SYS_KB_READ_CHAR EQU 36
|
||||
SYS_KB_READ_SCANCODE EQU 37
|
||||
|
||||
; Timer
|
||||
SYS_GET_TICK EQU 38
|
||||
|
||||
; Debug
|
||||
SYS_DEBUG_PRINT EQU 39
|
||||
|
||||
; Process launch & lifecycle
|
||||
SYS_CREATE_PROCESS EQU 40
|
||||
SYS_WAIT_FOR_PROCESS EQU 41
|
||||
SYS_GET_PROCESS_EXIT_CODE EQU 42
|
||||
|
||||
; Sentinel
|
||||
SYS_MAX EQU 43
|
||||
+125
-158
@@ -1,180 +1,147 @@
|
||||
; syscall_stubs.asm - user-mode INT 0x2E stubs exported by syscall.dll (MASM64)
|
||||
; Microsoft x64 ABI: args in RCX, RDX, R8, R9; args 5+6 in R10, R11.
|
||||
; syscall_stubs.asm - user-mode INT 0x40 stubs exported by syscall.dll (MASM64)
|
||||
;
|
||||
; Calling convention (kernel side):
|
||||
; RAX = syscall number
|
||||
; RCX = arg 0
|
||||
; RDX = arg 1
|
||||
; R8 = arg 2
|
||||
; R9 = arg 3
|
||||
; R10 = arg 4 (our extension - loaded from the stack by SYSCALL_STUB6)
|
||||
; R11 = arg 5 (our extension - loaded from the stack by SYSCALL_STUB6)
|
||||
;
|
||||
; User-mode callers follow standard MS x64: args 1-4 in RCX/RDX/R8/R9,
|
||||
; args 5+ on the stack above the home area ([rsp+28h], [rsp+30h], ...).
|
||||
;
|
||||
; SYSCALL_STUB2 - 4 or fewer args: pure register passing, no stack read.
|
||||
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
|
||||
;
|
||||
; Handle lifecycle:
|
||||
; SysCloseHandle is the single close stub for every object type.
|
||||
; There is no SysCloseFile - files are closed through the unified path,
|
||||
; exactly like mutexes, semaphores, events, and queues.
|
||||
|
||||
INCLUDE syscall_nr.inc ; SYS_* constants
|
||||
INCLUDE syscall_nr.inc
|
||||
|
||||
.CODE
|
||||
|
||||
SysExitThread PROC
|
||||
mov rax, SYS_EXIT_THREAD
|
||||
int 40h
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
SysExitThread ENDP
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
SysExitProcess PROC
|
||||
mov rax, SYS_EXIT_PROCESS
|
||||
int 40h
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB6 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov r10, [rsp+28h] ; 5th argument
|
||||
mov r11, [rsp+30h] ; 6th argument
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
SysExitProcess ENDP
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
SysYield PROC
|
||||
mov rax, SYS_YIELD
|
||||
int 40h
|
||||
ret
|
||||
SysYield ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Process & Thread
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 ExitProcess, SYS_EXIT_PROCESS
|
||||
SYSCALL_STUB2 ExitThread, SYS_EXIT_THREAD
|
||||
SYSCALL_STUB2 CreateThread, SYS_CREATE_THREAD
|
||||
SYSCALL_STUB2 GetCurrentTid, SYS_GET_CURRENT_TID
|
||||
SYSCALL_STUB2 GetCurrentPid, SYS_GET_CURRENT_PID
|
||||
SYSCALL_STUB2 Yield, SYS_YIELD
|
||||
SYSCALL_STUB2 Sleep, SYS_SLEEP
|
||||
SYSCALL_STUB2 SetThreadPriority, SYS_SET_THREAD_PRIORITY
|
||||
|
||||
SysSleep PROC ; RCX = milliseconds
|
||||
mov rax, SYS_SLEEP
|
||||
int 40h
|
||||
ret
|
||||
SysSleep ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Memory
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 Alloc, SYS_ALLOC
|
||||
SYSCALL_STUB2 Free, SYS_FREE
|
||||
SYSCALL_STUB2 Realloc, SYS_REALLOC
|
||||
|
||||
SysAlloc PROC ; RCX = size -> RAX = ptr
|
||||
mov rax, SYS_ALLOC
|
||||
int 40h
|
||||
ret
|
||||
SysAlloc ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Mutex
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 MutexCreate, SYS_MUTEX_CREATE
|
||||
SYSCALL_STUB2 MutexAcquire, SYS_MUTEX_ACQUIRE
|
||||
SYSCALL_STUB2 MutexTryAcquire, SYS_MUTEX_TRY_ACQUIRE
|
||||
SYSCALL_STUB2 MutexRelease, SYS_MUTEX_RELEASE
|
||||
|
||||
SysFree PROC ; RCX = ptr, RDX = size
|
||||
mov rax, SYS_FREE
|
||||
int 40h
|
||||
ret
|
||||
SysFree ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Semaphore
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 SemCreate, SYS_SEM_CREATE
|
||||
SYSCALL_STUB2 SemWait, SYS_SEM_WAIT
|
||||
SYSCALL_STUB2 SemTryWait, SYS_SEM_TRY_WAIT
|
||||
SYSCALL_STUB2 SemSignal, SYS_SEM_SIGNAL
|
||||
|
||||
SysCreateThread PROC ; RCX=entry, RDX=arg, R8=priority -> RAX=handle
|
||||
mov rax, SYS_CREATE_THREAD
|
||||
int 40h
|
||||
ret
|
||||
SysCreateThread ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Event
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 EventCreate, SYS_EVENT_CREATE
|
||||
SYSCALL_STUB2 EventSignal, SYS_EVENT_SIGNAL
|
||||
SYSCALL_STUB2 EventClear, SYS_EVENT_CLEAR
|
||||
SYSCALL_STUB2 EventWait, SYS_EVENT_WAIT
|
||||
|
||||
SysCloseHandle PROC ; RCX = handle
|
||||
mov rax, SYS_CLOSE_HANDLE
|
||||
int 40h
|
||||
ret
|
||||
SysCloseHandle ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Queue
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 QueueCreate, SYS_QUEUE_CREATE
|
||||
SYSCALL_STUB2 QueueSend, SYS_QUEUE_SEND
|
||||
SYSCALL_STUB2 QueueRecv, SYS_QUEUE_RECV
|
||||
|
||||
SysDbgPrint PROC ; RCX = string ptr
|
||||
mov rax, SYS_DBG_PRINT
|
||||
int 40h
|
||||
ret
|
||||
SysDbgPrint ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Handle (unified - use for files, mutexes, semaphores, events, queues)
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 CloseHandle, SYS_CLOSE_HANDLE
|
||||
|
||||
SysMutexCreate PROC ; -> RAX = handle
|
||||
mov rax, SYS_MUTEX_CREATE
|
||||
int 40h
|
||||
ret
|
||||
SysMutexCreate ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; File I/O
|
||||
; (To close: SysCloseHandle(handle) - no dedicated SysCloseFile)
|
||||
; ---------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
SysMutexAcquire PROC ; RCX = handle
|
||||
mov rax, SYS_MUTEX_ACQUIRE
|
||||
int 40h
|
||||
ret
|
||||
SysMutexAcquire ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Screen
|
||||
; SysScreenBlit has 5 args (x, y, w, h, pixels) - needs SYSCALL_STUB6.
|
||||
; SysScreenPutChar has 5 args (c, x, y, fg, bg) - needs SYSCALL_STUB6.
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB6 ScreenBlit, SYS_SCREEN_BLIT
|
||||
SYSCALL_STUB2 ScreenClear, SYS_SCREEN_CLEAR
|
||||
SYSCALL_STUB2 ScreenGetSize, SYS_SCREEN_GET_SIZE
|
||||
SYSCALL_STUB6 ScreenPutChar, SYS_SCREEN_PUT_CHAR
|
||||
|
||||
SysMutexRelease PROC ; RCX = handle
|
||||
mov rax, SYS_MUTEX_RELEASE
|
||||
int 40h
|
||||
ret
|
||||
SysMutexRelease ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Keyboard
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 KbPoll, SYS_KB_POLL
|
||||
SYSCALL_STUB2 KbReadChar, SYS_KB_READ_CHAR
|
||||
SYSCALL_STUB2 KbReadScancode, SYS_KB_READ_SCANCODE
|
||||
|
||||
SysSemCreate PROC ; RCX=initial, RDX=max -> RAX=handle
|
||||
mov rax, SYS_SEM_CREATE
|
||||
int 40h
|
||||
ret
|
||||
SysSemCreate ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Timer
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 GetTick, SYS_GET_TICK
|
||||
|
||||
SysSemWait PROC ; RCX = handle
|
||||
mov rax, SYS_SEM_WAIT
|
||||
int 40h
|
||||
ret
|
||||
SysSemWait ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Debug
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 DebugPrint, SYS_DEBUG_PRINT
|
||||
|
||||
SysSemSignal PROC ; RCX = handle
|
||||
mov rax, SYS_SEM_SIGNAL
|
||||
int 40h
|
||||
ret
|
||||
SysSemSignal ENDP
|
||||
|
||||
SysEventCreate PROC ; RCX=manualReset, RDX=initialState -> RAX=handle
|
||||
mov rax, SYS_EVENT_CREATE
|
||||
int 40h
|
||||
ret
|
||||
SysEventCreate ENDP
|
||||
|
||||
SysEventSet PROC ; RCX = handle
|
||||
mov rax, SYS_EVENT_SET
|
||||
int 40h
|
||||
ret
|
||||
SysEventSet ENDP
|
||||
|
||||
SysEventWait PROC ; RCX = handle
|
||||
mov rax, SYS_EVENT_WAIT
|
||||
int 40h
|
||||
ret
|
||||
SysEventWait ENDP
|
||||
|
||||
SysQueueCreate PROC ; -> RAX = handle
|
||||
mov rax, SYS_QUEUE_CREATE
|
||||
int 40h
|
||||
ret
|
||||
SysQueueCreate ENDP
|
||||
|
||||
SysQueueSend PROC ; RCX=handle, RDX=*msg, R8=blocking
|
||||
mov rax, SYS_QUEUE_SEND
|
||||
int 40h
|
||||
ret
|
||||
SysQueueSend ENDP
|
||||
|
||||
SysQueueRecv PROC ; RCX=handle, RDX=*msg, R8=blocking
|
||||
mov rax, SYS_QUEUE_RECV
|
||||
int 40h
|
||||
ret
|
||||
SysQueueRecv ENDP
|
||||
|
||||
SysScreenBlit PROC
|
||||
; RCX=x, RDX=y, R8=w, R9=h
|
||||
; The 5th arg (pixels) is on the stack above the 32-byte shadow space and return address.
|
||||
mov r10, [rsp + 40]
|
||||
|
||||
mov rax, SYS_SCREEN_BLIT
|
||||
int 40h
|
||||
ret
|
||||
SysScreenBlit ENDP
|
||||
|
||||
SysGetTick PROC ; -> RAX = milliseconds
|
||||
mov rax, SYS_GET_TICK
|
||||
int 40h
|
||||
ret
|
||||
SysGetTick ENDP
|
||||
|
||||
SysOpen PROC ; RCX = *path -> RAX = handle (0 = failure)
|
||||
mov rax, SYS_OPEN
|
||||
int 40h
|
||||
ret
|
||||
SysOpen ENDP
|
||||
|
||||
SysRead PROC ; RCX = handle, RDX = *buf, R8 = size -> RAX = bytes read
|
||||
mov rax, SYS_READ
|
||||
int 40h
|
||||
ret
|
||||
SysRead ENDP
|
||||
|
||||
SysFClose PROC ; RCX = handle
|
||||
mov rax, SYS_FCLOSE
|
||||
int 40h
|
||||
ret
|
||||
SysFClose ENDP
|
||||
|
||||
SysFSeek PROC ; RCX = handle, RDX = offset
|
||||
mov rax, SYS_FSEEK
|
||||
int 40h
|
||||
ret
|
||||
SysFSeek ENDP
|
||||
|
||||
SysFSize PROC ; RCX = handle -> RAX = size in bytes
|
||||
mov rax, SYS_FSIZE
|
||||
int 40h
|
||||
ret
|
||||
SysFSize ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Process launch & lifecycle
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 CreateProcess, SYS_CREATE_PROCESS
|
||||
SYSCALL_STUB2 WaitForProcess, SYS_WAIT_FOR_PROCESS
|
||||
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
|
||||
|
||||
END
|
||||
+1075
File diff suppressed because it is too large.
Load diff
Binary file not shown.
+2322
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,86 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include "bink.h"
|
||||
|
||||
void OutputDebugStringA(const char* s);
|
||||
_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);
|
||||
unsigned long GetFileSize(void* handle, unsigned long* high);
|
||||
int ReadFile(void* handle, void* buf, unsigned long toRead,
|
||||
unsigned long* read, void* overlapped);
|
||||
|
||||
int main() {
|
||||
OutputDebugStringA("video: starting\n");
|
||||
|
||||
// void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
|
||||
// if (!f) {
|
||||
// OutputDebugStringA("video: failed to open video.bk2\n");
|
||||
// ExitProcess(1);
|
||||
// }
|
||||
|
||||
// unsigned long s = GetFileSize(f, 0);
|
||||
// void* buf = HeapAlloc(0, 0, s);
|
||||
// if (!buf) {
|
||||
// OutputDebugStringA("video: HeapAlloc failed\n");
|
||||
// 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");
|
||||
|
||||
// HBINK bink = BinkOpen(buf, BINKFROMMEMORY);
|
||||
// if (!bink) {
|
||||
// OutputDebugStringA("video: BinkOpen failed: ");
|
||||
// OutputDebugStringA(BinkGetError());
|
||||
// OutputDebugStringA("\n");
|
||||
// ExitProcess(1);
|
||||
// }
|
||||
|
||||
// uint32_t width = bink->Width;
|
||||
// uint32_t height = bink->Height;
|
||||
|
||||
// uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
|
||||
// if (!fb) {
|
||||
// OutputDebugStringA("video: framebuffer alloc failed\n");
|
||||
// ExitProcess(1);
|
||||
// }
|
||||
|
||||
// while (1) {
|
||||
// if (BinkWait(bink)) {
|
||||
// _mm_pause();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// BinkDoFrame(bink);
|
||||
|
||||
// if (BinkShouldSkip(bink)) {
|
||||
// BinkNextFrame(bink);
|
||||
// BinkDoFrame(bink);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// BinkCopyToBuffer(bink, fb, width * 4, height, 0, 0, BINKSURFACE32);
|
||||
// BitBlt(0, 0, width, height, fb);
|
||||
|
||||
// if (bink->FrameNum == bink->Frames)
|
||||
// break;
|
||||
|
||||
// BinkNextFrame(bink);
|
||||
// }
|
||||
|
||||
// BinkClose(bink);
|
||||
for (int i = 0; i < 10000000; i++) _mm_pause();
|
||||
OutputDebugStringA("video: done\n");
|
||||
ExitProcess(0);
|
||||
}
|
||||
+992
-91
File diff suppressed because it is too large.
Load diff
+81
-2
@@ -1,2 +1,81 @@
|
||||
// ole32.c - stub; no COM at this stage.
|
||||
static void stub(void) { }
|
||||
// ole32.c - Win32 COM / OLE stubs
|
||||
// No COM infrastructure at this stage; all functions fail gracefully.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef long HRESULT;
|
||||
typedef void* IUnknown;
|
||||
|
||||
#define S_OK ((HRESULT)0x00000000L)
|
||||
#define E_NOTIMPL ((HRESULT)0x80004001L)
|
||||
#define E_OUTOFMEMORY ((HRESULT)0x8007000EL)
|
||||
#define COINIT_MULTITHREADED 0x0
|
||||
|
||||
// GUID - 16-byte COM interface identifier.
|
||||
typedef struct {
|
||||
unsigned int Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[8];
|
||||
} GUID;
|
||||
|
||||
// PROPVARIANT - variant type used by shell / media APIs.
|
||||
typedef struct {
|
||||
unsigned short vt;
|
||||
unsigned short wReserved1, wReserved2, wReserved3;
|
||||
union {
|
||||
long long hVal;
|
||||
double dblVal;
|
||||
void* punkVal;
|
||||
unsigned char bVal;
|
||||
unsigned int ulVal;
|
||||
// ... (only common fields; full union is ~16 bytes)
|
||||
unsigned char padding[16];
|
||||
};
|
||||
} PROPVARIANT;
|
||||
|
||||
extern void SysFree(void* ptr, size_t size);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// COM initialisation
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) HRESULT CoInitializeEx(void* reserved, unsigned long coInit) {
|
||||
(void)reserved; (void)coInit;
|
||||
return S_OK; // Pretend success; no actual apartment is created.
|
||||
}
|
||||
|
||||
__declspec(dllexport) void CoUninitialize(void) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Object creation
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) HRESULT CoCreateInstance(const GUID* clsid, IUnknown* outer,
|
||||
unsigned long ctx, const GUID* iid,
|
||||
void** ppv) {
|
||||
(void)clsid; (void)outer; (void)ctx; (void)iid;
|
||||
if (ppv) *ppv = (void*)0;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Memory
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) void CoTaskMemFree(void* pv) {
|
||||
if (pv) SysFree(pv, 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PROPVARIANT
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) HRESULT PropVariantClear(PROPVARIANT* pvar) {
|
||||
if (!pvar) return E_NOTIMPL;
|
||||
// Zero the entire structure; no deep release needed for stub types.
|
||||
unsigned char* p = (unsigned char*)pvar;
|
||||
for (size_t i = 0; i < sizeof(*pvar); i++) p[i] = 0;
|
||||
return S_OK;
|
||||
}
|
||||
+79
-8
@@ -1,20 +1,91 @@
|
||||
// user32.c - minimal user32 stubs (no window manager yet)
|
||||
// user32.c - Win32 user interface stubs (no window manager yet)
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Screen blit is the only graphical primitive available at this stage.
|
||||
// ---------------------------------------------------------------------------
|
||||
// Syscall stubs (from syscall.dll)
|
||||
// ---------------------------------------------------------------------------
|
||||
extern void SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
|
||||
|
||||
// syscall.dll
|
||||
extern void SysYield(void);
|
||||
extern void SysDebugPrint(const char* str);
|
||||
extern uint32_t SysKbPoll(void);
|
||||
|
||||
void BitBlt(int x, int y, int w, int h, const uint32_t* pixels) {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef void* HWND;
|
||||
|
||||
#define IDOK 1
|
||||
#define IDCANCEL 2
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen output
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// 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 - always return 0.
|
||||
int GetMessage(void* msg, void* hwnd, unsigned int min, unsigned int max) {
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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();
|
||||
return 0;
|
||||
}
|
||||
int DispatchMessage(const void* msg) { (void)msg; return 0; }
|
||||
|
||||
__declspec(dllexport) int DispatchMessage(const void* msg) { (void)msg; return 0; }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Window queries (stubs - no WM exists yet, all return NULL / 0)
|
||||
// ---------------------------------------------------------------------------
|
||||
__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; }
|
||||
|
||||
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
|
||||
(void)hwnd; (void)cmd; return (HWND)0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
|
||||
(void)hwnd; (void)index; return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
|
||||
(void)hwnd; (void)index; return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
|
||||
(void)hwnd;
|
||||
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;
|
||||
}
|
||||
+128
-2
@@ -1,2 +1,128 @@
|
||||
// winmm.c - stub; no multimedia at this stage.
|
||||
static void stub(void) { }
|
||||
// winmm.c - Win32 multimedia: waveform audio, timer
|
||||
// Stubs only; no audio hardware access at this stage.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef unsigned int MMRESULT;
|
||||
typedef void* HWAVEOUT;
|
||||
|
||||
#define MMSYSERR_NOERROR 0
|
||||
#define MMSYSERR_NOTSUPPORTED 8
|
||||
#define WAVERR_STILLPLAYING 33
|
||||
|
||||
// WAVEFORMATEX - standard PCM audio format descriptor.
|
||||
typedef struct {
|
||||
unsigned short wFormatTag;
|
||||
unsigned short nChannels;
|
||||
unsigned int nSamplesPerSec;
|
||||
unsigned int nAvgBytesPerSec;
|
||||
unsigned short nBlockAlign;
|
||||
unsigned short wBitsPerSample;
|
||||
unsigned short cbSize;
|
||||
} WAVEFORMATEX;
|
||||
|
||||
// WAVEHDR - buffer header submitted to waveOutWrite.
|
||||
typedef struct _WAVEHDR {
|
||||
char* lpData;
|
||||
unsigned int dwBufferLength;
|
||||
unsigned int dwBytesRecorded;
|
||||
uintptr_t dwUser;
|
||||
unsigned int dwFlags;
|
||||
unsigned int dwLoops;
|
||||
struct _WAVEHDR* lpNext;
|
||||
uintptr_t reserved;
|
||||
} WAVEHDR;
|
||||
|
||||
// MMTIME - used by waveOutGetPosition.
|
||||
typedef struct {
|
||||
unsigned int wType;
|
||||
union {
|
||||
unsigned int ms;
|
||||
unsigned int sample;
|
||||
unsigned int cb;
|
||||
unsigned int ticks;
|
||||
} u;
|
||||
} MMTIME;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// timeGetTime is the lightweight alternative to GetTickCount; both return
|
||||
// milliseconds since system start. We forward to the kernel tick counter.
|
||||
extern uint64_t SysGetTick(void);
|
||||
|
||||
__declspec(dllexport) unsigned long timeGetTime(void) {
|
||||
return (unsigned long)SysGetTick();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Waveform audio (stubs - no audio hardware at this stage)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutOpen(HWAVEOUT* phwo, unsigned int deviceId,
|
||||
const WAVEFORMATEX* fmt, uintptr_t callback,
|
||||
uintptr_t callbackInstance, unsigned long flags) {
|
||||
(void)deviceId; (void)fmt; (void)callback; (void)callbackInstance; (void)flags;
|
||||
if (phwo) *phwo = (void*)0;
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutClose(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutWrite(HWAVEOUT hwo, WAVEHDR* hdr, unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutPause(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutRestart(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutReset(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutSetVolume(HWAVEOUT hwo, unsigned long volume) {
|
||||
(void)hwo; (void)volume; return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutGetPosition(HWAVEOUT hwo, MMTIME* mmt,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)size;
|
||||
if (mmt) { mmt->wType = 0; mmt->u.ms = 0; }
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer resolution control
|
||||
// No hardware timer reprogramming at this stage; pretend success so callers
|
||||
// that wrap their audio/game loop in timeBeginPeriod(1) don't bail out.
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) MMRESULT timeBeginPeriod(unsigned int uPeriod) {
|
||||
(void)uPeriod;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT timeEndPeriod(unsigned int uPeriod) {
|
||||
(void)uPeriod;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
Reference in new issue
Block a user