diff --git a/OVMF/OVMF_VARS_4M.fd b/OVMF/OVMF_VARS_4M.fd index d0452e9..f38688d 100644 Binary files a/OVMF/OVMF_VARS_4M.fd and b/OVMF/OVMF_VARS_4M.fd differ diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index 4f52247..9992ad6 100644 Binary files a/OVMF/OVMF_VARS_4M_d.fd and b/OVMF/OVMF_VARS_4M_d.fd differ diff --git a/build.bat b/build.bat index 05b35fb..54c3661 100644 --- a/build.bat +++ b/build.bat @@ -14,7 +14,8 @@ mkdir "obj\hal" 2>nul mkdir "obj\syscall" 2>nul mkdir "obj\win32" 2>nul mkdir "obj\drivers" 2>nul -mkdir "obj\shell" 2>nul +mkdir "obj\shell" 2>nul +mkdir "obj\video" 2>nul mkdir "bin" 2>nul set CFLAGS=/nologo /W4 /WX /Od /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi /Zi /std:c17 /D_CRT_SECURE_NO_WARNINGS /I"%SRC%\kernel" /I"%SRC%\hal" @@ -77,16 +78,7 @@ link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:KernelMain /OUT:"bin\kernel.exe" ^ :: ---- Compile syscall.dll ---- echo Compiling syscall.dll... link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^ - /OUT:"bin\syscall.dll" ^ - /EXPORT:SysYield /EXPORT:SysAlloc /EXPORT:SysFree ^ - /EXPORT:SysCreateThread /EXPORT:SysCloseHandle ^ - /EXPORT:SysMutexCreate /EXPORT:SysMutexAcquire /EXPORT:SysMutexRelease ^ - /EXPORT:SysSemCreate /EXPORT:SysSemWait /EXPORT:SysSemSignal ^ - /EXPORT:SysEventCreate /EXPORT:SysEventSet /EXPORT:SysEventWait ^ - /EXPORT:SysQueueCreate /EXPORT:SysQueueSend /EXPORT:SysQueueRecv ^ - /EXPORT:SysScreenBlit /EXPORT:SysGetTick /EXPORT:SysSleep ^ - /EXPORT:SysExitThread /EXPORT:SysExitProcess /EXPORT:SysDbgPrint ^ - obj\syscall\stubs.obj || exit /b 1 + /OUT:"bin\syscall.dll" obj\syscall\stubs.obj || exit /b 1 :: ---- Compile win32.dll ---- echo Compiling win32.dll... @@ -94,15 +86,7 @@ for %%f in (%SRC%\win32\*.c) do ( cl %CFLAGS% /c /Fo"obj\win32\%%~nf.obj" "%%f" || exit /b 1 ) link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^ - /OUT:"bin\win32.dll" ^ - /EXPORT:HeapAlloc /EXPORT:HeapFree /EXPORT:HeapReAlloc ^ - /EXPORT:CreateThread /EXPORT:ExitProcess /EXPORT:ExitThread ^ - /EXPORT:Sleep /EXPORT:SwitchToThread ^ - /EXPORT:CreateMutex /EXPORT:ReleaseMutex /EXPORT:WaitForSingleObject ^ - /EXPORT:CreateEvent /EXPORT:SetEvent /EXPORT:CloseHandle ^ - /EXPORT:GetTickCount /EXPORT:GetTickCount64 ^ - /EXPORT:BitBlt /EXPORT:GetMessage /EXPORT:DispatchMessage /EXPORT:OutputDebugStringA ^ - bin\syscall.lib obj\win32\*.obj || exit /b 1 + /OUT:"bin\win32.dll" bin\syscall.lib obj\libc.obj obj\win32\*.obj || exit /b 1 :: ---- Compile drivers ---- echo Compiling drivers... @@ -137,6 +121,17 @@ echo Linking shell.exe... link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ /OUT:"bin\shell.exe" /PDB:"bin\shell.pdb" bin\win32.lib obj\libc.obj obj\shell\*.obj || exit /b 1 +:: ---- Compile video player ---- +echo Compiling video... +for %%f in (%SRC%\video\*.c) do ( + cl %CFLAGS% /c /Fo"obj\video\%%~nf.obj" "%%f" || exit /b 1 +) + +:: ---- Link video.exe ---- +echo Linking video.exe... +link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ + /OUT:"bin\video.exe" /PDB:"bin\video.pdb" bin\win32.lib obj\libc.obj %SRC%\video\bink2w64.lib obj\video\*.obj || exit /b 1 + :: ---- Copy to ESP layout ---- echo Assembling ESP... mkdir "%DEST%\drivers" 2>nul @@ -150,7 +145,8 @@ copy /y "bin\ide.sys" "%DEST%\drivers\" copy /y "bin\ahci.sys" "%DEST%\drivers\" copy /y "bin\fat32.sys" "%DEST%\drivers\" copy /y "bin\usb.sys" "%DEST%\drivers\" -copy /y "bin\shell.exe" "%DEST%\" +copy /y "bin\shell.exe" "%DEST%\" +copy /y "bin\video.exe" "%DEST%\" copy /y "%SRC%\boot.ini" "%DEST%\" copy /y "%SRC%\drivers\drivers.ini" "%DEST%\drivers\" diff --git a/code/boot/main.c b/code/boot/main.c index 15db6e2..d1d9fe4 100644 --- a/code/boot/main.c +++ b/code/boot/main.c @@ -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; diff --git a/code/hal/hal_api.h b/code/hal/hal_api.h index f367b45..24792b8 100644 --- a/code/hal/hal_api.h +++ b/code/hal/hal_api.h @@ -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 { diff --git a/code/hal/hal_keyboard.c b/code/hal/hal_keyboard.c index 11bee3e..5f4bf59 100644 --- a/code/hal/hal_keyboard.c +++ b/code/hal/hal_keyboard.c @@ -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 #include #include @@ -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; diff --git a/code/hal/hal_main.c b/code/hal/hal_main.c index d10593b..32d37ca 100644 --- a/code/hal/hal_main.c +++ b/code/hal/hal_main.c @@ -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 ; PackageOp + encoded length +// ; usually 0x04 +// 0x0A ; BytePrefix + value (or 0x00 for zero) +// 0x0A +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 + +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; diff --git a/code/hal/hal_screen.c b/code/hal/hal_screen.c index 7343d47..0ce6892 100644 --- a/code/hal/hal_screen.c +++ b/code/hal/hal_screen.c @@ -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++; } diff --git a/code/kernel/ke_process.c b/code/kernel/ke_process.c index c5a7049..7c16f42 100644 --- a/code/kernel/ke_process.c +++ b/code/kernel/ke_process.c @@ -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 @@ -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 // --------------------------------------------------------------------------- diff --git a/code/kernel/ke_process.h b/code/kernel/ke_process.h index 34420e4..0b3c0b2 100644 --- a/code/kernel/ke_process.h +++ b/code/kernel/ke_process.h @@ -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); diff --git a/code/kernel/ke_sched.c b/code/kernel/ke_sched.c index d1c179a..818bdd1 100644 --- a/code/kernel/ke_sched.c +++ b/code/kernel/ke_sched.c @@ -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); diff --git a/code/kernel/ke_status.h b/code/kernel/ke_status.h index 2ec3e5e..0275a8b 100644 --- a/code/kernel/ke_status.h +++ b/code/kernel/ke_status.h @@ -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) diff --git a/code/kernel/ke_switch.asm b/code/kernel/ke_switch.asm index 6357c51..db7143b 100644 --- a/code/kernel/ke_switch.asm +++ b/code/kernel/ke_switch.asm @@ -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 diff --git a/code/kernel/ke_syscall.asm b/code/kernel/ke_syscall.asm index f572b95..d768b03 100644 --- a/code/kernel/ke_syscall.asm +++ b/code/kernel/ke_syscall.asm @@ -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) diff --git a/code/kernel/ke_syscall.c b/code/kernel/ke_syscall.c index cec46f0..212618c 100644 --- a/code/kernel/ke_syscall.c +++ b/code/kernel/ke_syscall.c @@ -1,8 +1,35 @@ -// ke_syscall.c - syscall dispatch +// ke_syscall.c - system call dispatcher // -// KeSyscallEntry (MASM64) pushes all arguments onto the kernel stack and -// calls KeSyscallDispatch. We read them from the stack frame. -//#include "ke_syscall.h" +// KeSyscallEntry (ke_syscall.asm) handles the INT 0x40 ring-3 -> ring-0 +// transition, re-orders the arguments to match MS x64, and calls +// KeSyscallDispatch with: +// +// nr = original RAX (syscall number) +// a0 .. a5 = RCX, RDX, R8, R9, R10, R11 (user arguments) +// +// Every handler receives the same (a0..a5) signature even if it uses fewer +// arguments; unused parameters are silently ignored. +// +// Return value (RAX) is a KSTATUS code *or* an arbitrary 64-bit datum +// (pointer, size, tick count, etc.) depending on the call; conventions are +// documented in syscall_nr.h. +// +// --------------------------------------------------------------------------- +// Unified handle design +// --------------------------------------------------------------------------- +// All kernel objects (FILE_OBJECT, MUTEX, SEMAPHORE, EVENT, QUEUE) are +// reference-counted OBJECTs stored in the per-process handle table. +// SYS_CLOSE_HANDLE is the single close path for every type: +// +// KeCloseHandle(proc, handle) +// -> ObDereferenceObject(obj) +// -> obj->Destroy(obj) [type-specific cleanup, e.g. FileDestroy] +// -> MmFreePool(obj) +// +// There is no separate SyscallCloseFile. Removing that stub was the right +// call: the object model already handled it; the dedicated syscall was only +// adding a footgun where callers could bypass the unified refcount path. + #include "ke_thread.h" #include "ke_sched.h" #include "ke_process.h" @@ -12,36 +39,128 @@ #include "ke_queue.h" #include "ke_debug.h" #include "mm_virt.h" +#include "ldr_image.h" #include "syscall_nr.h" #include "hal_api.h" #include "io_file.h" #include #include +#include -// hal pointer set at boot +// --------------------------------------------------------------------------- +// HAL pointer - set once at boot by KeSyscallSetHal() +// --------------------------------------------------------------------------- static const HAL_API* gHal; + void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; } // --------------------------------------------------------------------------- -// Individual syscall handlers +// Helper: get the current process, or NULL if unavailable. // --------------------------------------------------------------------------- - -static uint64_t SyscallExitThread(uint64_t code, 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; - KeExitThread(KeCurrentThread(), (int)code); - return STATUS_SUCCESS; +static inline PROCESS* CurrentProcess(void) { + THREAD* t = KeCurrentThread(); + return t ? t->Owner : NULL; } -static uint64_t SyscallExitProcess(uint64_t code, uint64_t a1, uint64_t a2, +// --------------------------------------------------------------------------- +// Helper: look up a handle and type-check it in one call. +// Bumps the refcount on success; caller must ObDereferenceObject when done. +// Returns NULL if the handle is invalid or the type does not match. +// --------------------------------------------------------------------------- +static OBJECT* HandleToObject(uint32_t handle, OBJECT_TYPE expected) { + PROCESS* proc = CurrentProcess(); + if (!proc) return NULL; + OBJECT* obj = KeLookupHandle(proc, handle); // bumps refcount + if (!obj) return NULL; + if (obj->Type != expected) { + ObDereferenceObject(obj); // undo the bump + return NULL; + } + return obj; +} + +// --------------------------------------------------------------------------- +// Helper: allocate a handle in the current process's table for 'obj'. +// Takes one reference on behalf of the handle slot. +// On failure, drops the reference it would have stored. +// --------------------------------------------------------------------------- +static KSTATUS AllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) { + PROCESS* proc = CurrentProcess(); + if (!proc) return STATUS_FAILURE; + KSTATUS s = KeAllocateHandle(proc, obj, handleOut); + if (!KSUCCESS(s)) ObDereferenceObject(obj); + return s; +} + +// =========================================================================== +// Individual syscall handlers +// =========================================================================== + +// --------------------------------------------------------------------------- +// SYS_EXIT_PROCESS (0) a0 = exitCode +// --------------------------------------------------------------------------- +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(); + PROCESS* proc = t ? t->Owner : NULL; + if (proc) KeExitProcess(proc, (int)a0); + if (t) KeExitThread(t, (int)a0); + for (;;) ; // unreachable - KeExitThread does not return +} + +// --------------------------------------------------------------------------- +// SYS_EXIT_THREAD (1) a0 = exitCode +// --------------------------------------------------------------------------- +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(); - if (t && t->Owner) KeExitProcess(t->Owner, (int)code); - KeExitThread(t, (int)code); + if (t) KeExitThread(t, (int)a0); + for (;;) ; +} + +// --------------------------------------------------------------------------- +// SYS_CREATE_THREAD (2) a0=entry a1=arg a2=priority a3=*tidOut +// --------------------------------------------------------------------------- +static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a4; (void)a5; + PROCESS* proc = CurrentProcess(); + if (!proc) return STATUS_FAILURE; + if (a2 >= PriorityCount) return STATUS_INVALID_PARAMETER; + + THREAD* newt = NULL; + KSTATUS s = KeCreateThread(proc, (uintptr_t)a0, (uintptr_t)a1, + (THREAD_PRIORITY)a2, &newt); + if (!KSUCCESS(s)) return s; + if (a3) *(uint32_t*)(uintptr_t)a3 = newt->ThreadId; return STATUS_SUCCESS; } +// --------------------------------------------------------------------------- +// SYS_GET_CURRENT_TID (3) +// --------------------------------------------------------------------------- +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(); + return t ? t->ThreadId : 0; +} + +// --------------------------------------------------------------------------- +// SYS_GET_CURRENT_PID (4) +// --------------------------------------------------------------------------- +static uint64_t SyscallGetCurrentPid(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; + PROCESS* p = CurrentProcess(); + return p ? p->ProcessId : 0; +} + +// --------------------------------------------------------------------------- +// SYS_YIELD (5) +// --------------------------------------------------------------------------- static uint64_t SyscallYield(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; @@ -49,329 +168,599 @@ static uint64_t SyscallYield(uint64_t a0, uint64_t a1, uint64_t a2, return STATUS_SUCCESS; } -static uint64_t SyscallSleep(uint64_t ms, uint64_t a1, uint64_t a2, +// --------------------------------------------------------------------------- +// SYS_SLEEP (6) a0 = milliseconds +// --------------------------------------------------------------------------- +static uint64_t SyscallSleep(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; - // Simple busy-wait sleep via HAL timer. - if (gHal) { - uint64_t start = gHal->Timer.QueryTicks(); - while (gHal->Timer.QueryTicks() - start < ms) KeYield(); - } + if (!gHal) return STATUS_FAILURE; + uint64_t deadline = gHal->Timer.QueryTicks() + (uint64_t)a0; + while (gHal->Timer.QueryTicks() < deadline) + KeYield(); return STATUS_SUCCESS; } -static uint64_t SyscallAlloc(uint64_t size, uint64_t a1, uint64_t a2, - uint64_t a3, uint64_t a4, uint64_t a5) { +// --------------------------------------------------------------------------- +// SYS_SET_THREAD_PRIORITY (7) a0 = priority +// --------------------------------------------------------------------------- +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; - return (uint64_t)MmAllocateUserMemory((size_t)size); + if (a0 >= PriorityCount) return STATUS_INVALID_PARAMETER; + THREAD* t = KeCurrentThread(); + if (!t) return STATUS_FAILURE; + t->Priority = (THREAD_PRIORITY)a0; + return STATUS_SUCCESS; } -static uint64_t SyscallFree(uint64_t ptr, uint64_t size, uint64_t a2, +// --------------------------------------------------------------------------- +// SYS_ALLOC (8) a0 = size -> void* +// --------------------------------------------------------------------------- +static uint64_t SyscallAlloc(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 0; + return (uint64_t)(uintptr_t)MmAllocateUserMemory((size_t)a0); +} + +// --------------------------------------------------------------------------- +// SYS_FREE (9) a0 = ptr, a1 = size +// --------------------------------------------------------------------------- +static uint64_t SyscallFree(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; - MmFreeUserMemory((void *)ptr, (size_t)size); + if (a0) MmFreeUserMemory((void*)(uintptr_t)a0, (size_t)a1); return STATUS_SUCCESS; } -static uint64_t SyscallCreateThread(uint64_t entry, uint64_t arg, - uint64_t priority, uint64_t a3, - uint64_t a4, uint64_t a5) { - (void)a3; (void)a4; (void)a5; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return (uint64_t)STATUS_FAILURE; - THREAD* newThread; - KSTATUS s = KeCreateThread(cur->Owner, entry, arg, - (THREAD_PRIORITY)priority, &newThread); - if (!KSUCCESS(s)) return (uint64_t)s; - uint32_t handle; - KeAllocateHandle(cur->Owner, &newThread->Header, &handle); - return (uint64_t)handle; -} - -static uint64_t SyscallCloseHandle(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - KeCloseHandle(cur->Owner, (uint32_t)handle); - return STATUS_SUCCESS; -} - -static uint64_t SyscallDbgPrint(uint64_t strPtr, 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* s = (const char *)(uintptr_t)strPtr; - DbgPrint(s); - return STATUS_SUCCESS; +// --------------------------------------------------------------------------- +// SYS_REALLOC (10) a0 = ptr, a1 = newSize -> void* +// --------------------------------------------------------------------------- +static uint64_t SyscallRealloc(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; + return (uint64_t)(uintptr_t)MmReallocateUserMemory( + (void*)(uintptr_t)a0, (size_t)a1); } +// --------------------------------------------------------------------------- +// SYS_MUTEX_CREATE (11) a0 = *handleOut +// --------------------------------------------------------------------------- static uint64_t SyscallMutexCreate(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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - MUTEX* m; if (!KSUCCESS(MutexCreate(&m))) return 0; - uint32_t h; KeAllocateHandle(cur->Owner, &m->Header, &h); - ObDereferenceObject(&m->Header); // handle holds the ref - return h; -} - -static uint64_t SyscallMutexAcquire(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeMutex) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KSTATUS s = KeAcquireMutex((MUTEX *)obj); - ObDereferenceObject(obj); - return s; -} - -static uint64_t SyscallMutexRelease(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeMutex) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KeReleaseMutex((MUTEX *)obj); - ObDereferenceObject(obj); + if (!a0) return STATUS_INVALID_PARAMETER; + MUTEX* mx = NULL; + KSTATUS s = MutexCreate(&mx); + if (!KSUCCESS(s)) return s; + uint32_t h = 0; + s = AllocHandleForCurrent(&mx->Header, &h); // takes ownership of the ref + if (!KSUCCESS(s)) return s; + *(uint32_t*)(uintptr_t)a0 = h; return STATUS_SUCCESS; } -static uint64_t SyscallSemCreate(uint64_t initial, uint64_t max, uint64_t a2, - uint64_t a3, uint64_t a4, uint64_t a5) { - (void)a2; (void)a3; (void)a4; (void)a5; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - SEMAPHORE* s; if (!KSUCCESS(SemaphoreCreate((int32_t)initial,(int32_t)max,&s))) return 0; - uint32_t h; KeAllocateHandle(cur->Owner, &s->Header, &h); - ObDereferenceObject(&s->Header); - return h; +// --------------------------------------------------------------------------- +// SYS_MUTEX_ACQUIRE (12) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallMutexAcquire(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; + MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex); + if (!mx) return STATUS_NOT_FOUND; + KSTATUS s = KeAcquireMutex(mx); + ObDereferenceObject(&mx->Header); + return s; } -static uint64_t SyscallSemWait(uint64_t handle, uint64_t a1, uint64_t a2, +// --------------------------------------------------------------------------- +// SYS_MUTEX_TRY_ACQUIRE (13) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallMutexTryAcquire(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; + MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex); + if (!mx) return STATUS_NOT_FOUND; + KSTATUS s = KeTryAcquireMutex(mx); + ObDereferenceObject(&mx->Header); + return s; +} + +// --------------------------------------------------------------------------- +// SYS_MUTEX_RELEASE (14) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallMutexRelease(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; + MUTEX* mx = (MUTEX*)HandleToObject((uint32_t)a0, ObjectTypeMutex); + if (!mx) return STATUS_NOT_FOUND; + KeReleaseMutex(mx); + ObDereferenceObject(&mx->Header); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_SEM_CREATE (15) a0=initial a1=max a2=*handleOut +// --------------------------------------------------------------------------- +static uint64_t SyscallSemCreate(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a3; (void)a4; (void)a5; + if (!a2) return STATUS_INVALID_PARAMETER; + SEMAPHORE* sem = NULL; + KSTATUS s = SemaphoreCreate((int32_t)a0, (int32_t)a1, &sem); + if (!KSUCCESS(s)) return s; + uint32_t h = 0; + s = AllocHandleForCurrent(&sem->Header, &h); + if (!KSUCCESS(s)) return s; + *(uint32_t*)(uintptr_t)a2 = h; + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_SEM_WAIT (16) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallSemWait(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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeSemaphore) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KSTATUS s = KeWaitSemaphore((SEMAPHORE *)obj); - ObDereferenceObject(obj); + SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore); + if (!sem) return STATUS_NOT_FOUND; + KSTATUS s = KeWaitSemaphore(sem); + ObDereferenceObject(&sem->Header); return s; } -static uint64_t SyscallSemSignal(uint64_t handle, uint64_t a1, uint64_t a2, - uint64_t a3, uint64_t a4, uint64_t a5) { +// --------------------------------------------------------------------------- +// SYS_SEM_TRY_WAIT (17) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallSemTryWait(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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeSemaphore) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KeSignalSemaphore((SEMAPHORE *)obj); - ObDereferenceObject(obj); - return STATUS_SUCCESS; -} - -static uint64_t SyscallEventCreate(uint64_t manualReset, uint64_t initial, - uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { - (void)a2; (void)a3; (void)a4; (void)a5; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - EVENT* e; - EVENT_TYPE t = manualReset ? EventManualReset : EventAutoReset; - if (!KSUCCESS(KeEventCreate(t, (uint32_t)initial, &e))) return 0; - uint32_t h; KeAllocateHandle(cur->Owner, &e->Header, &h); - ObDereferenceObject(&e->Header); - return h; -} - -static uint64_t SyscallEventSet(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeEvent) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KeSignalEvent((EVENT *)obj); - ObDereferenceObject(obj); - return STATUS_SUCCESS; -} - -static uint64_t SyscallEventWait(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeEvent) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KSTATUS s = KeWaitForEvent((EVENT *)obj); - ObDereferenceObject(obj); + SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore); + if (!sem) return STATUS_NOT_FOUND; + KSTATUS s = KeTrySemaphore(sem); + ObDereferenceObject(&sem->Header); return s; } +// --------------------------------------------------------------------------- +// SYS_SEM_SIGNAL (18) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallSemSignal(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; + SEMAPHORE* sem = (SEMAPHORE*)HandleToObject((uint32_t)a0, ObjectTypeSemaphore); + if (!sem) return STATUS_NOT_FOUND; + KeSignalSemaphore(sem); + ObDereferenceObject(&sem->Header); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_EVENT_CREATE (19) a0=type a1=initialState a2=*handleOut +// --------------------------------------------------------------------------- +static uint64_t SyscallEventCreate(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a3; (void)a4; (void)a5; + if (!a2 || a0 > EventManualReset) return STATUS_INVALID_PARAMETER; + EVENT* ev = NULL; + KSTATUS s = KeEventCreate((EVENT_TYPE)a0, (uint32_t)a1, &ev); + if (!KSUCCESS(s)) return s; + uint32_t h = 0; + s = AllocHandleForCurrent(&ev->Header, &h); + if (!KSUCCESS(s)) return s; + *(uint32_t*)(uintptr_t)a2 = h; + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_EVENT_SIGNAL (20) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallEventSignal(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; + EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent); + if (!ev) return STATUS_NOT_FOUND; + KeSignalEvent(ev); + ObDereferenceObject(&ev->Header); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_EVENT_CLEAR (21) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallEventClear(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; + EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent); + if (!ev) return STATUS_NOT_FOUND; + KeClearEvent(ev); + ObDereferenceObject(&ev->Header); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_EVENT_WAIT (22) a0 = handle +// --------------------------------------------------------------------------- +static uint64_t SyscallEventWait(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; + EVENT* ev = (EVENT*)HandleToObject((uint32_t)a0, ObjectTypeEvent); + if (!ev) return STATUS_NOT_FOUND; + KSTATUS s = KeWaitForEvent(ev); + ObDereferenceObject(&ev->Header); + return s; +} + +// --------------------------------------------------------------------------- +// SYS_QUEUE_CREATE (23) a0 = *handleOut +// --------------------------------------------------------------------------- static uint64_t SyscallQueueCreate(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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - QUEUE* q; if (!KSUCCESS(QueueCreate(&q))) return 0; - uint32_t h; KeAllocateHandle(cur->Owner, &q->Header, &h); + (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; + if (!a0) return STATUS_INVALID_PARAMETER; + QUEUE* q = NULL; + KSTATUS s = QueueCreate(&q); + if (!KSUCCESS(s)) return s; + uint32_t h = 0; + s = AllocHandleForCurrent(&q->Header, &h); + if (!KSUCCESS(s)) return s; + *(uint32_t*)(uintptr_t)a0 = h; + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_QUEUE_SEND (24) a0=handle a1=*msg a2=blocking +// --------------------------------------------------------------------------- +static uint64_t SyscallQueueSend(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a3; (void)a4; (void)a5; + if (!a1) return STATUS_INVALID_PARAMETER; + QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue); + if (!q) return STATUS_NOT_FOUND; + KSTATUS s = KeSendMessage(q, (const QUEUE_MESSAGE*)(uintptr_t)a1, (bool)a2); ObDereferenceObject(&q->Header); - return h; -} - -static uint64_t SyscallQueueSend(uint64_t handle, uint64_t msgPtr, - uint64_t blocking, uint64_t a3, uint64_t a4, uint64_t a5) { - (void)a3; (void)a4; (void)a5; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeQueue) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KSTATUS s = KeSendMessage((QUEUE *)obj, (const QUEUE_MESSAGE *)msgPtr, (bool)blocking); - ObDereferenceObject(obj); return s; } -static uint64_t SyscallQueueRecv(uint64_t handle, uint64_t msgPtr, - uint64_t blocking, uint64_t a3, uint64_t a4, uint64_t a5) { +// --------------------------------------------------------------------------- +// SYS_QUEUE_RECV (25) a0=handle a1=*msg a2=blocking +// --------------------------------------------------------------------------- +static uint64_t SyscallQueueRecv(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj || obj->Type != ObjectTypeQueue) { if(obj) ObDereferenceObject(obj); return STATUS_INVALID_PARAMETER; } - KSTATUS s = KeReceiveMessage((QUEUE *)obj, (QUEUE_MESSAGE *)msgPtr, (bool)blocking); - ObDereferenceObject(obj); + if (!a1) return STATUS_INVALID_PARAMETER; + QUEUE* q = (QUEUE*)HandleToObject((uint32_t)a0, ObjectTypeQueue); + if (!q) return STATUS_NOT_FOUND; + KSTATUS s = KeReceiveMessage(q, (QUEUE_MESSAGE*)(uintptr_t)a1, (bool)a2); + ObDereferenceObject(&q->Header); return s; } -static uint64_t SyscallScreenBlit(uint64_t x, uint64_t y, uint64_t w, - uint64_t h, uint64_t pixels, uint64_t a5) { - (void)a5; - if (gHal) { - gHal->Screen.Blit((int)x, (int)y, (int)w, (int)h, (const uint32_t *)pixels); - return STATUS_SUCCESS; +// --------------------------------------------------------------------------- +// SYS_CLOSE_HANDLE (26) a0 = handle +// +// The one and only close path. KeCloseHandle drops the handle-table reference +// on 'obj'. If that was the last reference, ObDereferenceObject runs the +// type-specific Destroy callback (FileDestroy, etc.) and frees the pool memory. +// No per-type close syscall needed or wanted. +// --------------------------------------------------------------------------- +static uint64_t SyscallCloseHandle(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; + PROCESS* proc = CurrentProcess(); + if (!proc) return STATUS_FAILURE; + KeCloseHandle(proc, (uint32_t)a0); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_OPEN_FILE (27) a0=*path a1=*handleOut +// --------------------------------------------------------------------------- +static uint64_t SyscallOpenFile(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 = IoOpenFile((const char*)(uintptr_t)a0, &file); + if (!KSUCCESS(s)) return s; + uint32_t h = 0; + s = AllocHandleForCurrent(&file->Header, &h); + if (!KSUCCESS(s)) { + // AllocHandleForCurrent already dropped the ref on failure; + // the refcount is now 0, so ObDereferenceObject will run FileDestroy. + ObDereferenceObject(&file->Header); + return s; } - DbgPrint("!! SyscallScreenBlit: what?\n"); - return STATUS_FAILURE; + *(uint32_t*)(uintptr_t)a1 = h; + return STATUS_SUCCESS; } +// --------------------------------------------------------------------------- +// SYS_READ_FILE (28) a0=handle a1=buf a2=size a3=*bytesReadOut +// --------------------------------------------------------------------------- +static uint64_t SyscallReadFile(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 got = 0; + KSTATUS s = IoReadFile(file, (void*)(uintptr_t)a1, (size_t)a2, &got); + if (a3) *(size_t*)(uintptr_t)a3 = got; + ObDereferenceObject(&file->Header); + return s; +} + +// --------------------------------------------------------------------------- +// SYS_SEEK_FILE (29) a0=handle a1=offset +// --------------------------------------------------------------------------- +static uint64_t SyscallSeekFile(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 = IoSeekFile(file, (uint64_t)a1); + ObDereferenceObject(&file->Header); + return s; +} + +// --------------------------------------------------------------------------- +// SYS_GET_FILE_SIZE (30) a0=handle -> uint64_t +// --------------------------------------------------------------------------- +static uint64_t SyscallGetFileSize(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 0; + uint64_t sz = IoGetFileSize(file); + ObDereferenceObject(&file->Header); + return sz; +} + +// --------------------------------------------------------------------------- +// SYS_SCREEN_BLIT (31) a0=x a1=y a2=w a3=h a4=*pixels +// --------------------------------------------------------------------------- +static uint64_t SyscallScreenBlit(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a5; + if (!gHal || !a4) return STATUS_FAILURE; + gHal->Screen.Blit((int)a0, (int)a1, (int)a2, (int)a3, + (const uint32_t*)(uintptr_t)a4); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_SCREEN_CLEAR (32) a0=color +// --------------------------------------------------------------------------- +static uint64_t SyscallScreenClear(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 (!gHal) return STATUS_FAILURE; + gHal->Screen.Clear((uint32_t)a0); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_SCREEN_GET_SIZE (33) a0=*widthOut a1=*heightOut +// --------------------------------------------------------------------------- +static uint64_t SyscallScreenGetSize(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 (!gHal) return STATUS_FAILURE; + int w = 0, h = 0; + gHal->Screen.GetSize(&w, &h); + if (a0) *(int*)(uintptr_t)a0 = w; + if (a1) *(int*)(uintptr_t)a1 = h; + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_SCREEN_PUT_CHAR (34) a0=c a1=x a2=y a3=fg a4=bg +// --------------------------------------------------------------------------- +static uint64_t SyscallScreenPutChar(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a5; + if (!gHal) return STATUS_FAILURE; + gHal->Screen.PutChar((char)a0, (int)a1, (int)a2, + (uint32_t)a3, (uint32_t)a4); + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_KB_POLL (35) -> 1 or 0 +// --------------------------------------------------------------------------- +static uint64_t SyscallKbPoll(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; + if (!gHal) return 0; + return gHal->Keyboard.Poll() ? 1u : 0u; +} + +// --------------------------------------------------------------------------- +// SYS_KB_READ_CHAR (36) -> char (blocks, yields while waiting) +// --------------------------------------------------------------------------- +static uint64_t SyscallKbReadChar(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; + if (!gHal) return 0; + while (!gHal->Keyboard.Poll()) KeYield(); + return (uint64_t)(uint8_t)gHal->Keyboard.ReadChar(); +} + +// --------------------------------------------------------------------------- +// SYS_KB_READ_SCANCODE (37) -> uint8_t (blocks, yields while waiting) +// --------------------------------------------------------------------------- +static uint64_t SyscallKbReadScancode(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; + if (!gHal) return 0; + while (!gHal->Keyboard.Poll()) KeYield(); + return (uint64_t)gHal->Keyboard.ReadScancode(); +} + +// --------------------------------------------------------------------------- +// SYS_GET_TICK (38) -> uint64_t milliseconds +// --------------------------------------------------------------------------- static uint64_t SyscallGetTick(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; return gHal ? gHal->Timer.QueryTicks() : 0; } -static uint64_t SyscallOpen(uint64_t pathPtr, uint64_t a1, uint64_t a2, - uint64_t a3, uint64_t a4, uint64_t a5) { +// --------------------------------------------------------------------------- +// SYS_DEBUG_PRINT (39) a0=*str +// --------------------------------------------------------------------------- +static uint64_t SyscallDebugPrint(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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - - FILE_OBJECT* f; - KSTATUS s = IoOpenFile((const char *)pathPtr, &f); - if (!KSUCCESS(s)) return 0; - - uint32_t handle; - s = KeAllocateHandle(cur->Owner, &f->Header, &handle); - ObDereferenceObject(&f->Header); // handle holds the ref now - if (!KSUCCESS(s)) return 0; - return (uint64_t)handle; -} - -static uint64_t SyscallRead(uint64_t handle, uint64_t bufPtr, uint64_t size, - uint64_t a3, uint64_t a4, uint64_t a5) { - (void)a3; (void)a4; (void)a5; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj) return 0; - // FILE_OBJECT reuses ObjectTypeQueue slot - check it holds a Fs pointer. - FILE_OBJECT* f = (FILE_OBJECT *)obj; - size_t bytesRead = 0; - IoReadFile(f, (void *)bufPtr, (size_t)size, &bytesRead); - ObDereferenceObject(obj); - return (uint64_t)bytesRead; -} - -static uint64_t SyscallFClose(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - KeCloseHandle(cur->Owner, (uint32_t)handle); + if (a0) DbgPrint("%s", (const char*)(uintptr_t)a0); return STATUS_SUCCESS; } -static uint64_t SyscallFSeek(uint64_t handle, uint64_t offset, uint64_t a2, - uint64_t a3, uint64_t a4, uint64_t a5) { +// --------------------------------------------------------------------------- +// SYS_CREATE_PROCESS (40) a0=*path a1=*handleOut +// --------------------------------------------------------------------------- +static uint64_t SyscallCreateProcess(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; - THREAD* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return STATUS_FAILURE; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj) return STATUS_NOT_FOUND; - KSTATUS s = IoSeekFile((FILE_OBJECT *)obj, offset); - ObDereferenceObject(obj); + + const char* path = (const char*)(uintptr_t)a0; + uint32_t* handleOut = (uint32_t*)(uintptr_t)a1; + if (!path || !handleOut) return STATUS_INVALID_PARAMETER; + + // Read the executable from disk. + FILE_OBJECT* f; + KSTATUS s = IoOpenFile(path, &f); + if (!KSUCCESS(s)) return s; + + uint64_t size = IoGetFileSize(f); + void* mem = MmAllocateUserMemory(size); + if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; } + + size_t got; + s = IoReadFile(f, mem, size, &got); + IoCloseFile(f); + if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; } + + // Create the process object. + PROCESS* proc; + s = KeCreateProcess(path, &proc); + if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; } + + // Map the PE image into memory. + uintptr_t imageBase = 0; + s = LdrLoadImage(mem, size, &imageBase, true); + MmFreeUserMemory(mem, size); + if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } + + // Spin up the initial thread at the PE entry point. + THREAD* thread; + uintptr_t entryPoint = LdrGetEntryPoint(imageBase); + s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread); + if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } + + // Give the caller a handle to the new process so they can wait on it. + PROCESS* caller = CurrentProcess(); + if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; } + + uint32_t handle; + s = KeAllocateHandle(caller, &proc->Header, &handle); + if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } + + *handleOut = handle; + return STATUS_SUCCESS; +} + +// --------------------------------------------------------------------------- +// SYS_WAIT_FOR_PROCESS (41) a0=handle +// Blocks until the target process signals its ExitEvent. +// --------------------------------------------------------------------------- +static uint64_t SyscallWaitForProcess(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; + + PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess); + if (!proc) return STATUS_INVALID_HANDLE; + + KSTATUS s = KeWaitForEvent(proc->ExitEvent); + ObDereferenceObject(&proc->Header); return s; } -static uint64_t SyscallFSize(uint64_t handle, 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* cur = KeCurrentThread(); - if (!cur || !cur->Owner) return 0; - OBJECT* obj = KeLookupHandle(cur->Owner, (uint32_t)handle); - if (!obj) return 0; - uint64_t sz = IoGetFileSize((FILE_OBJECT *)obj); - ObDereferenceObject(obj); - return sz; +// --------------------------------------------------------------------------- +// SYS_GET_PROCESS_EXIT_CODE (42) a0=handle a1=*exitCodeOut +// --------------------------------------------------------------------------- +static uint64_t SyscallGetProcessExitCode(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; + + PROCESS* proc = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess); + if (!proc) return STATUS_INVALID_HANDLE; + + int* exitCodeOut = (int*)(uintptr_t)a1; + KSTATUS s = KeGetProcessExitCode(proc, exitCodeOut); + ObDereferenceObject(&proc->Header); + return s; } -// --------------------------------------------------------------------------- -// Dispatch table -// --------------------------------------------------------------------------- -typedef uint64_t (*SYSCALL_FN)(uint64_t, uint64_t, uint64_t, - uint64_t, uint64_t, uint64_t); +// =========================================================================== +// Dispatch table - index must match SYS_* in syscall_nr.h exactly. +// =========================================================================== +typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t, + uint64_t, uint64_t, uint64_t); -static const SYSCALL_FN gSyscallTable[SYS_MAX] = { - [SYS_EXIT_THREAD] = SyscallExitThread, - [SYS_EXIT_PROCESS] = SyscallExitProcess, - [SYS_SPAWN] = (SYSCALL_FN)0, // TODO: process spawn - [SYS_SLEEP] = SyscallSleep, - [SYS_YIELD] = SyscallYield, - [SYS_ALLOC] = SyscallAlloc, - [SYS_FREE] = SyscallFree, - [SYS_CREATE_THREAD]= SyscallCreateThread, - [SYS_OPEN_HANDLE] = (SYSCALL_FN)0, // TODO - [SYS_CLOSE_HANDLE] = SyscallCloseHandle, - [SYS_DBG_PRINT] = SyscallDbgPrint, - [SYS_MUTEX_CREATE] = SyscallMutexCreate, - [SYS_MUTEX_ACQUIRE]= SyscallMutexAcquire, - [SYS_MUTEX_RELEASE]= SyscallMutexRelease, - [SYS_SEM_CREATE] = SyscallSemCreate, - [SYS_SEM_WAIT] = SyscallSemWait, - [SYS_SEM_SIGNAL] = SyscallSemSignal, - [SYS_EVENT_CREATE] = SyscallEventCreate, - [SYS_EVENT_SET] = SyscallEventSet, - [SYS_EVENT_WAIT] = SyscallEventWait, - [SYS_QUEUE_CREATE] = SyscallQueueCreate, - [SYS_QUEUE_SEND] = SyscallQueueSend, - [SYS_QUEUE_RECV] = SyscallQueueRecv, - [SYS_SCREEN_BLIT] = SyscallScreenBlit, - [SYS_GET_TICK] = SyscallGetTick, - [SYS_OPEN] = SyscallOpen, - [SYS_READ] = SyscallRead, - [SYS_FCLOSE] = SyscallFClose, - [SYS_FSEEK] = SyscallFSeek, - [SYS_FSIZE] = SyscallFSize, +static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = { + /* 00 SYS_EXIT_PROCESS */ SyscallExitProcess, + /* 01 SYS_EXIT_THREAD */ SyscallExitThread, + /* 02 SYS_CREATE_THREAD */ SyscallCreateThread, + /* 03 SYS_GET_CURRENT_TID */ SyscallGetCurrentTid, + /* 04 SYS_GET_CURRENT_PID */ SyscallGetCurrentPid, + /* 05 SYS_YIELD */ SyscallYield, + /* 06 SYS_SLEEP */ SyscallSleep, + /* 07 SYS_SET_THREAD_PRIORITY */ SyscallSetThreadPriority, + /* 08 SYS_ALLOC */ SyscallAlloc, + /* 09 SYS_FREE */ SyscallFree, + /* 10 SYS_REALLOC */ SyscallRealloc, + /* 11 SYS_MUTEX_CREATE */ SyscallMutexCreate, + /* 12 SYS_MUTEX_ACQUIRE */ SyscallMutexAcquire, + /* 13 SYS_MUTEX_TRY_ACQUIRE */ SyscallMutexTryAcquire, + /* 14 SYS_MUTEX_RELEASE */ SyscallMutexRelease, + /* 15 SYS_SEM_CREATE */ SyscallSemCreate, + /* 16 SYS_SEM_WAIT */ SyscallSemWait, + /* 17 SYS_SEM_TRY_WAIT */ SyscallSemTryWait, + /* 18 SYS_SEM_SIGNAL */ SyscallSemSignal, + /* 19 SYS_EVENT_CREATE */ SyscallEventCreate, + /* 20 SYS_EVENT_SIGNAL */ SyscallEventSignal, + /* 21 SYS_EVENT_CLEAR */ SyscallEventClear, + /* 22 SYS_EVENT_WAIT */ SyscallEventWait, + /* 23 SYS_QUEUE_CREATE */ SyscallQueueCreate, + /* 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, }; -// Called from ke_syscall.asm - args already on the stack in order. -uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1, - uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { - if (nr >= SYS_MAX || !gSyscallTable[nr]) return (uint64_t)STATUS_NOT_SUPPORTED; - return gSyscallTable[nr](a0, a1, a2, a3, a4, a5); +// --------------------------------------------------------------------------- +// Entry point - called from ke_syscall.asm after argument re-ordering +// --------------------------------------------------------------------------- +KSTATUS KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1, + uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { + if (nr >= SYS_MAX || !gSyscallTable[nr]) return STATUS_NOT_SUPPORTED; + return (KSTATUS)gSyscallTable[nr](a0, a1, a2, a3, a4, a5); } diff --git a/code/kernel/ke_thread.c b/code/kernel/ke_thread.c index a9c71f5..c118fce 100644 --- a/code/kernel/ke_thread.c +++ b/code/kernel/ke_thread.c @@ -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; diff --git a/code/kernel/ke_thread.h b/code/kernel/ke_thread.h index fd26ae6..3dd1b6f 100644 --- a/code/kernel/ke_thread.h +++ b/code/kernel/ke_thread.h @@ -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); diff --git a/code/kernel/kmain.c b/code/kernel/kmain.c index 07b08fe..4ea5ae6 100644 --- a/code/kernel/kmain.c +++ b/code/kernel/kmain.c @@ -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 } } diff --git a/code/kernel/ldr_image.c b/code/kernel/ldr_image.c index 888dde7..ef60de3 100644 --- a/code/kernel/ldr_image.c +++ b/code/kernel/ldr_image.c @@ -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; } diff --git a/code/kernel/syscall_nr.h b/code/kernel/syscall_nr.h index 1521c58..aa351a6 100644 --- a/code/kernel/syscall_nr.h +++ b/code/kernel/syscall_nr.h @@ -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 diff --git a/code/shell/shell.c b/code/shell/shell.c index f018899..10c6661 100644 --- a/code/shell/shell.c +++ b/code/shell/shell.c @@ -1,47 +1,177 @@ #include #include +#include -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); } diff --git a/code/syscall/syscall_nr.inc b/code/syscall/syscall_nr.inc index ad45840..9a7cbcc 100644 --- a/code/syscall/syscall_nr.inc +++ b/code/syscall/syscall_nr.inc @@ -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 diff --git a/code/syscall/syscall_stubs.asm b/code/syscall/syscall_stubs.asm index 12bce77..3e05ad9 100644 --- a/code/syscall/syscall_stubs.asm +++ b/code/syscall/syscall_stubs.asm @@ -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 diff --git a/code/video/bink.h b/code/video/bink.h new file mode 100644 index 0000000..6a0b50b --- /dev/null +++ b/code/video/bink.h @@ -0,0 +1,1075 @@ +#ifndef __BINKH__ +#define __BINKH__ + +#ifndef EXPMACRO +#define EXPMACRO +#endif + +#ifndef EXPGROUP +#define EXPGROUP(dummy) +#endif + +EXPGROUP(_NullGroup) +#define BinkDate "2014-04-03" EXPMACRO + +#define Bink2Version "2.4d" +#define Bink2MajorVersion 2 +#define Bink2MinorVersion 4 +#define Bink2BuildNumber 3 +#define Bink2Customization 0 + +#define Bink1Version "1.994d" EXPMACRO +#define Bink1MajorVersion 1 EXPMACRO +#define Bink1MinorVersion 999 EXPMACRO +#define Bink1BuildNumber 4 EXPMACRO +#define Bink1Customization 3 EXPMACRO + +#define BinkVersion "2.4d/1.994d" EXPMACRO +#define BinkMajorVersion 0 EXPMACRO +#define BinkMinorVersion 4 EXPMACRO +#define BinkBuildNumber 3 EXPMACRO +#define BinkCustomization 0 EXPMACRO + +EXPGROUP(_RootGroup) + + + +#ifndef __RADRES__ + +#ifndef __RADRR_COREH__ +#include "rrCore.h" +#endif + +RADDEFSTART + +#if defined(BINKUDK) || (defined(UDK) && UDK == 1) + #undef RADEXPFUNC + #define RADEXPFUNC RADDEFFUNC + + #define BinkOpen BinkOpenUDK + #define BinkDoFrame BinkDoFrameUDK +#endif + +typedef struct BINK * HBINK; + +struct BINKIO; +typedef S32 (RADLINK * BINKIOOPEN) (struct BINKIO * Bnkio, const char *name, U32 flags); +typedef U32 (RADLINK * BINKIOREADHEADER) (struct BINKIO * Bnkio, S32 Offset, void * Dest,U32 Size); +typedef U32 (RADLINK * BINKIOREADFRAME) (struct BINKIO * Bnkio, U32 Framenum,S32 origofs,void * dest,U32 size); +typedef U32 (RADLINK * BINKIOGETBUFFERSIZE)(struct BINKIO * Bnkio, U32 Size); +typedef void (RADLINK * BINKIOSETINFO) (struct BINKIO * Bnkio, void * Buf,U32 Size,U32 FileSize,U32 simulate); +typedef U32 (RADLINK * BINKIOIDLE) (struct BINKIO * Bnkio); +typedef void (RADLINK * BINKIOCLOSE) (struct BINKIO * Bnkio); +typedef S32 (RADLINK * BINKIOBGCONTROL) (struct BINKIO * Bnkio, U32 Control); + +typedef void (RADLINK * BINKCBSUSPEND) (struct BINKIO * Bnkio); +typedef S32 (RADLINK * BINKCBTRYSUSPEND) (struct BINKIO * Bnkio); +typedef void (RADLINK * BINKCBRESUME) (struct BINKIO * Bnkio); +typedef void (RADLINK * BINKCBIDLE) (struct BINKIO * Bnkio); +typedef void (RADLINK * BINKCBSIMULATE) (struct BINKIO * Bnkio, U32 amt, U32 timer ); +typedef U32 (RADLINK * BINKCBTIMER) ( void ); +typedef void (RADLINK * BINKCBFLIPENDIAN) ( void * ptr, U32 size ); +typedef U32 (RADLINK * BINKCBLOCKEDADD) ( U32 volatile * ptr, S32 amt ); + +typedef struct BINKIO { + BINKIOREADHEADER ReadHeader; + BINKIOREADFRAME ReadFrame; + BINKIOGETBUFFERSIZE GetBufferSize; + BINKIOSETINFO SetInfo; + + BINKIOIDLE Idle; + BINKIOCLOSE Close; + BINKIOBGCONTROL BGControl; + HBINK bink; + + // filled in by the bink + BINKCBSUSPEND suspend_callback; + BINKCBTRYSUSPEND try_suspend_callback; + BINKCBRESUME resume_callback; + BINKCBIDLE idle_on_callback; + + BINKCBSIMULATE simulate_callback; + BINKCBTIMER timer_callback; + BINKCBFLIPENDIAN flipendian_callback; + BINKCBLOCKEDADD lockedadd_callback; + + volatile U32 ReadError; + volatile U32 DoingARead; + volatile U32 BytesRead; + volatile U32 Working; + + volatile U32 TotalTime; + volatile U32 ForegroundTime; + volatile U32 IdleTime; + volatile U32 ThreadTime; + + volatile U32 BufSize; + volatile U32 BufHighUsed; + volatile U32 CurBufSize; + volatile U32 CurBufUsed; + + volatile U32 Suspended; + U32 align[ 3 ]; + + volatile U8 iodata[128+32]; +} BINKIO; + +struct BINKSND; +typedef S32 (RADLINK * BINKSNDOPEN) (struct BINKSND * BnkSnd, U32 freq, S32 bits, S32 chans, U32 flags, HBINK bink); +typedef S32 (RADLINK * BINKSNDREADY) (struct BINKSND * BnkSnd); +typedef S32 (RADLINK * BINKSNDLOCK) (struct BINKSND * BnkSnd, U8 * * addr, U32 * len); +typedef S32 (RADLINK * BINKSNDUNLOCK) (struct BINKSND * BnkSnd, U32 filled); +typedef void (RADLINK * BINKSNDVOLUME) (struct BINKSND * BnkSnd, S32 volume); +typedef void (RADLINK * BINKSNDPAN) (struct BINKSND * BnkSnd, S32 pan); +typedef void (RADLINK * BINKSNDSPKOUTVOLS) (struct BINKSND * BnkSnd, F32 * volumes, U32 total ); +typedef S32 (RADLINK * BINKSNDONOFF) (struct BINKSND * BnkSnd, S32 status); +typedef S32 (RADLINK * BINKSNDPAUSE) (struct BINKSND * BnkSnd, S32 status); +typedef void (RADLINK * BINKSNDCLOSE) (struct BINKSND * BnkSnd); +typedef S32 (RADLINK * BINKSNDPROP) (struct BINKSND * BnkSnd, S32 prop, UINTa* value); + +typedef U32 (RADLINK * BINKTIMERREAD) (void); +RADEXPFUNC void RADEXPLINK BinkSetTimerRead( BINKTIMERREAD uread ); + +typedef BINKSNDOPEN (RADLINK * BINKSNDSYSOPEN) (UINTa param); +typedef BINKSNDOPEN (RADLINK * BINKSNDSYSOPEN2) (UINTa param1, UINTa param2); + +typedef struct BINKSND { + // for the spu, we *send* through sndreadpos, but we only return sndwritepos + U8 * sndwritepos; // current write position + + U32 audiodecompsize; + + U32 sndbufsize; // sound buffer size + U8 * sndbuf; // sound buffer + U8 * sndend; // end of the sound buffer + UINTa sndcomp; // sound compression handle + + U8 * sndreadpos; // current read position + +#if defined( BINK_SPU_PROCESS ) + struct binksnd_hide // put variables that we don't want to accidentally + { // use on the spu into a structure +#endif + U32 orig_freq; + U32 freq; + S32 bits,chans; + + S32 BestSizeIn16; + U32 BestSizeMask; + S32 OnOff; + U32 Latency; + U32 VideoScale; + + U32 sndendframe; // frame number that the sound ends on + + U32 sndpad; // padded this much audio + S32 sndprime; // amount of data to prime the playahead + + S32 ThreadServiceType; // 0 = normal (calls ready/lock/unlock for each BINKSND) + // -num = global (calls unlock and expects the sound handler + // to mix *all* BINKSNDS) "num" is how often to call + // unlock, for example "-5" is every 5 ms) + // 1 = No background thread processing at all (sound system + // will do it's own threading) + // 2 = No background thread processing at all, but setup + // thread mutexes, so that BinkServiceSound() can be + // call from an external background thread. + + U32 SoundDroppedOut; + U32 sndconvert8; // convert back to 8-bit sound at runtime + U8 snddata[256]; + + //UINTa align; + + BINKSNDREADY Ready; + BINKSNDLOCK Lock; + BINKSNDUNLOCK Unlock; + BINKSNDVOLUME Volume; + BINKSNDPAN Pan; + BINKSNDPAUSE Pause; + BINKSNDONOFF SetOnOff; + BINKSNDCLOSE Close; + BINKSNDSPKOUTVOLS SpeakerVols; + BINKSNDPROP Property; +#if defined( BINK_SPU_PROCESS ) + } spu_hide; +#endif + +} BINKSND; + +typedef struct BINKRECT { + S32 Left,Top,Width,Height; +} BINKRECT; + +#define BINKMAXDIRTYRECTS 8 + +typedef struct BUNDLEPOINTERS { + void* typeptr; + void* type16ptr; + void* colorptr; + void* bits2ptr; + + void* motionXptr; + void* motionYptr; + void* dctptr; + void* mdctptr; + + void* patptr; +} BUNDLEPOINTERS; + +typedef struct BINKPLANE +{ + S32 Allocate; + void * Buffer; + U32 BufferPitch; +} BINKPLANE; + +typedef struct BINKFRAMEPLANESET +{ + BINKPLANE YPlane; + BINKPLANE cRPlane; + BINKPLANE cBPlane; + BINKPLANE APlane; +} BINKFRAMEPLANESET; + +#define BINKMAXFRAMEBUFFERS 2 + +typedef struct BINKFRAMEBUFFERS +{ + S32 TotalFrames; + U32 YABufferWidth; + U32 YABufferHeight; + U32 cRcBBufferWidth; + U32 cRcBBufferHeight; + + U32 FrameNum; + BINKFRAMEPLANESET Frames[ BINKMAXFRAMEBUFFERS ]; +} BINKFRAMEBUFFERS; + +#define BINKMAXPLANES 4 +#define BINKMAXSLICES 13 + +typedef struct BINKGPUDATABUFFERS BINKGPUDATABUFFERS; + +#ifdef __RAD3DS__ + +typedef struct BINKSWIZZLEBUFFERS +{ + U32 Width; + U32 Height; + U8* YBuffer; + U8* ABuffer; + U8* cRBuffer; + U8* cBBuffer; +} BINKSWIZZLEBUFFERS; + +#endif + +typedef struct BINKSLICES +{ + U32 slice_inc; + U32 slice_count; + U32 slice_pitch; + U32 slices[BINKMAXSLICES]; +} BINKSLICES; + +typedef struct BINK { + U32 Width; // Width (1 based, 640 for example) + U32 Height; // Height (1 based, 480 for example) + U32 Frames; // Number of frames (1 based, 100 = 100 frames) + U32 FrameNum; // Frame to *be* displayed (1 based) + + U32 LastFrameNum; // Last frame decompressed or skipped (1 based) + U32 FrameRate; // Frame Rate Numerator + U32 FrameRateDiv; // Frame Rate Divisor (frame rate=numerator/divisor) + U32 ReadError; // Non-zero if a read error has ocurred + + U32 OpenFlags; // flags used on open + U32 BinkType; // Bink flags + U32 Size; // size of file + U32 FrameSize; // The current frame's size in bytes + + U32 SndSize; // The current frame sound tracks' size in bytes + U32 FrameChangePercent; // very rough percentage of the frame that changed + S32 NumTracks; // how many tracks + S32 NumRects; + + U32 soundon; // sound turned on? + U32 videoon; // video turned on? + U32 needio; // set to 1, if we need an io before the next binkframe + S32 closing; // are we closing? + + BINKRECT FrameRects[BINKMAXDIRTYRECTS];// Dirty rects from BinkGetRects + + U64 FileOffset; // Offset into the file where the Bink starts + U32 Highest1SecRate; // Highest 1 sec data rate + U32 Highest1SecFrame; // Highest 1 sec data rate starting frame + + BINKFRAMEBUFFERS * FrameBuffers; // Bink frame buffers that we decompress to + void * MaskPlane; // pointer to the mask plane (Ywidth/16*Yheight/16) + void * AsyncMaskPlane; // pointer to the mask plane for async data + void * InUseMaskPlane; // pointer to the mask plane in use + + // everything below is for internal Bink use + + void * LastMaskPlane; // pointer to the last mask plane + struct BINK * next_bink; // linked list of open binks that are being access from threads + void * compframe; // compressed frame data + S32 * trackindexes; // track indexes + + U32 MaskPitch; // Mask Pitch + U32 MaskLength; // total length of the mask plane + U32 LargestFrameSize; // Largest frame size + U32 InternalFrames; // how many frames were potentially compressed + + S32 async_in_progress[8]; // is an async decompression in progress + + U32 marker; + S32 Paused; // is the bink movie paused? + U32 skippedlastblit; // skipped last frame? + U32 align; + + BINKSLICES slices; + + U32 compframesize; // compressed frame size + U32 compframekey; // if this frame a key frame + U32 playingtracks; // how many tracks are playing + U32 changepercent; // how much roughly did the current frame change? + + BUNDLEPOINTERS bunp; // pointers to internal temporary memory + + // we are at ptr ofs 1 here (bunp has 9 ptrs) + BINKSND * bsnd; // SND structures + + BINKGPUDATABUFFERS * gpu; // pointer to gpu data pointers + +#if defined( BINK_SPU_PROCESS ) + struct bink_hide // put variables that we don't want to accidentally + { // use on the spu into a structure +#endif + + // now we are at ptr ofs 3 here + U32* frameoffsets; // offsets of each of the frames + + U32 bink_unique; // number + S32 in_bink_list; // is this bink in the linked list? + U32 decompwidth; // width not include scaling + U32 decompheight; // height not include scaling + + U32 * tracksizes; // largest single frame of track + U32 * tracktypes; // type of each sound track + S32 * trackIDs; // external track numbers + void * preloadptr; // preloaded compressed frame data + + S32 simulate; // simulate speed variables + S32 adjustsim; + U32 compframeoffset; // compressed frame offset + U32 compframenum; // frame to background load + + BINKIO bio; // IO structure + + U8 pri_io_mutex[ 128 ]; // taken when priority io in progress + + U8 io_mutex[ 128 ]; // taken when background io in progress + + U8 snd_mutex[ 128 ]; // taken when background snd in progress + + U8 * ioptr; // io buffer ptr + U32 * rtframetimes; // start times for runtime frames + U32 * rtadecomptimes; // decompress times for runtime frames + U32 * rtvdecomptimes; // decompress times for runtime frames + + U32 * rtblittimes; // blit times for runtime frames + U32 * rtreadtimes; // read times for runtime frames + U32 * rtidlereadtimes; // idle read times for runtime frames + U32 * rtthreadreadtimes; // thread read times for runtime frames + + U32 runtimeframes; // max frames for runtime analysis + S32 rtindex; // index of where we are in the runtime frames + U32 iosize; // io buffer size + U32 numrects; // number of rects from BinkGetRects + + U32 playedframes; // how many frames have we played + U32 firstframetime; // very first frame start + U32 totalmem; // total memory used + U32 soundskips; // number of sound stops + + U32 startblittime; // start of blit period + U32 startsynctime; // start of synched time + U32 startsyncframe; // frame of startsynctime + U32 twoframestime; // two frames worth of time + + U32 slowestframetime; // slowest frame in ms + U32 slowestframe; // slowest frame number + U32 slowest2frametime; // second slowest frame in ms + U32 slowest2frame; // second slowest frame + + U32 timevdecomp; // total time decompressing video + U32 timeadecomp; // total time decompressing audio + U32 timeblit; // total time blitting + U32 timeopen; // total open time + + U32 fileframerate; // frame rate originally in the file + U32 fileframeratediv; + U32 lastblitflags; // flags used on last blit + U32 lastdecompframe; // last frame number decompressed + + U32 lastfinisheddoframe; // time that the last do frame finished + U32 lastresynctime; // last loop point that we did a resync on + U32 doresync; // should we do a resync in the next doframe? + U32 skipped_status_this_frame;//0=not checked this frame, 1=no skip, 2=skip + + U32 very_delayed; // is this frame more than 725 ms late? + U32 skippedblits; // how many blits were skipped + U32 skipped_in_a_row; // how many frames have we skipped in a row + U32 paused_sync_diff; // sync delta at the time of a pause + + U32 last_time_almost_empty; // time of last almost empty IO buffer + S32 allkeys; // are all frames keyframes? + U32 lastfileread; // last place we read binkfile + U32 limit_speakers; + + void * alloccompframe; // compressed frame data base address + BINKFRAMEBUFFERS * allocatedframebuffers; // pointer to internally allocated buffers + void * seam; + void * binkaudiomem; // memory for the bink audio structures + + void * opt_ptr; + U32 opt_bytes; + +#if defined(__RAD3DS__) + BINKSWIZZLEBUFFERS* swizzle_buffers; +#endif + +#if defined( BINK_SPU_PROCESS ) + } spu_hide; +#endif +} BINK; + + +typedef struct BINKSUMMARY { + U32 Width; // Width of frames + U32 Height; // Height of frames + U32 TotalTime; // total time (ms) + U32 FileFrameRate; // frame rate + U32 FileFrameRateDiv; // frame rate divisor + U32 FrameRate; // frame rate + U32 FrameRateDiv; // frame rate divisor + U32 TotalOpenTime; // Time to open and prepare for decompression + U32 TotalFrames; // Total Frames + U32 TotalPlayedFrames; // Total Frames played + U32 SkippedFrames; // Total number of skipped frames + U32 SkippedBlits; // Total number of skipped blits + U32 SoundSkips; // Total number of sound skips + U32 TotalBlitTime; // Total time spent blitting + U32 TotalReadTime; // Total time spent reading + U32 TotalVideoDecompTime; // Total time spent decompressing video + U32 TotalAudioDecompTime; // Total time spent decompressing audio + U32 TotalIdleReadTime; // Total time spent reading while idle + U32 TotalBackReadTime; // Total time spent reading in background + U32 TotalReadSpeed; // Total io speed (bytes/second) + U32 SlowestFrameTime; // Slowest single frame time (ms) + U32 Slowest2FrameTime; // Second slowest single frame time (ms) + U32 SlowestFrameNum; // Slowest single frame number + U32 Slowest2FrameNum; // Second slowest single frame number + U32 AverageDataRate; // Average data rate of the movie + U32 AverageFrameSize; // Average size of the frame + U32 HighestMemAmount; // Highest amount of memory allocated + U32 TotalIOMemory; // Total extra memory allocated + U32 HighestIOUsed; // Highest extra memory actually used + U32 Highest1SecRate; // Highest 1 second rate + U32 Highest1SecFrame; // Highest 1 second start frame +} BINKSUMMARY; + + +typedef struct BINKREALTIME { + U32 FrameNum; // Current frame number + U32 FrameRate; // frame rate + U32 FrameRateDiv; // frame rate divisor + U32 Frames; // frames in this sample period + U32 FramesTime; // time is ms for these frames + U32 FramesVideoDecompTime; // time decompressing these frames + U32 FramesAudioDecompTime; // time decompressing these frames + U32 FramesReadTime; // time reading these frames + U32 FramesIdleReadTime; // time reading these frames at idle + U32 FramesThreadReadTime; // time reading these frames in background + U32 FramesBlitTime; // time blitting these frames + U32 ReadBufferSize; // size of read buffer + U32 ReadBufferUsed; // amount of read buffer currently used + U32 FramesDataRate; // data rate for these frames +} BINKREALTIME; + +#define BINKMARKER1 'fKIB' +#define BINKMARKER2 'gKIB' // new Bink files use this tag +#define BINKMARKER3 'hKIB' // newer Bink files use this tag +#define BINKMARKER4 'iKIB' // even newer Bink files use this tag + +#define BINKMARKERV2 'f2BK' +#define BINKMARKERV22 'g2BK' // bink 2.2 +#define BINKMARKERV23 'h2BK' // bink 2.3 +#define BINKMARKERV23D 'i2BK' // bink 2.3d version + +typedef struct BINKHDR { + U32 Marker; // Bink marker + U32 Size; // size of the file-8 + U32 Frames; // Number of frames (1 based, 100 = 100 frames) + U32 LargestFrameSize; // Size in bytes of largest frame + U32 InternalFrames; // Number of internal frames + + U32 Width; // Width (1 based, 640 for example) + U32 Height; // Height (1 based, 480 for example) + U32 FrameRate; // frame rate + U32 FrameRateDiv; // frame rate divisor (framerate/frameratediv=fps) + + U32 Flags; // bink flags + U32 NumTracks; // number of tracks +} BINKHDR; + + +typedef struct BINK_OPEN_OPTIONS +{ + U64 FileOffset; + S32 ForceRate; + S32 ForceRateDiv; + S32 IOBufferSize; + S32 Simulate; + U32 TotTracks; + S32 * TrackNums; + BINKIOOPEN UserOpen; +} BINK_OPEN_OPTIONS; + +//======================================================================= +#define BINKYAINVERT 0x00000800L // Reverse Y and A planes when blitting (for debugging) +#define BINKFRAMERATE 0x00001000L // Override fr (call BinkFrameRate first) +#define BINKPRELOADALL 0x00002000L // Preload the entire animation +#define BINKSNDTRACK 0x00004000L // Set the track number to play +#define BINKOLDFRAMEFORMAT 0x00008000L // using the old Bink frame format (internal use only) +#define BINKRBINVERT 0x00010000L // use reversed R and B planes (internal use only) +#define BINKGRAYSCALE 0x00020000L // Force Bink to use grayscale +//#define BINKNOMMX 0x00040000L // Don't use MMX +#define BINKNOSKIP 0x00080000L // Don't skip frames if falling behind +#define BINKALPHA 0x00100000L // Decompress alpha plane (if present) +#define BINKNOFILLIOBUF 0x00200000L // Don't Fill the IO buffer (in BinkOpen and BinkCopyTo) +#define BINKSIMULATE 0x00400000L // Simulate the speed (call BinkSim first) +#define BINKFILEHANDLE 0x00800000L // Use when passing in a file handle +#define BINKIOSIZE 0x01000000L // Set an io size (call BinkIOSize first) +#define BINKIOPROCESSOR 0x02000000L // Set an io processor (call BinkIO first) +#define BINKFROMMEMORY 0x04000000L // Use when passing in a pointer to the file +#define BINKNOTHREADEDIO 0x08000000L // Don't use a background thread for IO +#define BINKNOFRAMEBUFFERS 0x00000400L // Don't allocate internal frame buffers - application must call BinkRegisterFrameBuffers +#define BINKNOYPLANE 0x00000200L // Don't decompress the Y plane (internal flag) +#define BINKGPU 0x00000100L // Open Bink in GPU mode +#define BINKWILLLOOP 0x00000080L // You are planning to loop this Bink. +#define BINKDONTCLOSETHREADS 0x00000040L // Don't close threads on BinkClose (call BinkFreeGlobals to close threads) +#define BINKFILEOFFSET 0x00000020L // Use file offset specified by BinkSetFileOffset +#define BINKYCRCBNEW 0x00000010L // File uses the new ycrcb colorspace (usually Bink 2) +#define BINK_SLICES_2 0x00000000L // Bink 2 file has two slices +#define BINK_SLICES_3 0x00000001L // Bink 2 file has three slices +#define BINK_SLICES_4 0x00000002L // Bink 2 file has four slices +#define BINK_SLICES_8 0x00000003L // Bink 2 file has eight slices +#define BINK_SLICES_MASK 0x00000003L // mask against openflags to get the slice flags + +#define BINKSURFACEFAST 0x00000000L +#define BINKSURFACESLOW 0x08000000L +#define BINKSURFACEDIRECT 0x04000000L + +#define BINKCOPYALL 0x80000000L // copy all pixels (not just changed) +#define BINKCOPY2XH 0x10000000L // Force doubling height scaling +#define BINKCOPY2XHI 0x20000000L // Force interleaving height scaling +#define BINKCOPY2XW 0x30000000L // copy the width zoomed by two +#define BINKCOPY2XWH 0x40000000L // copy the width and height zoomed by two +#define BINKCOPY2XWHI 0x50000000L // copy the width and height zoomed by two +#define BINKCOPY1XI 0x60000000L // copy the width and height zoomed by two +#define BINKCOPYNOSCALING 0x70000000L // Force scaling off + +//#define BINKNOFILLIOBUF 0x00200000L // Don't Fill the IO buffer (in BinkOpen and BinkCopyTo) +//#define BINKALPHA 0x00100000L // Decompress alpha plane (if present) +//#define BINKNOSKIP 0x00080000L // don't skip the blit if behind in sound +//#define BINKNOMMX 0x00040000L // No MMX +//#define BINKGRAYSCALE 0x00020000L // force Bink to use grayscale +//#define BINKRBINVERT 0x00010000L // use reversed R and B planes + +#define BINKSURFACEP8 0 +#define BINKSURFACE24 1 +#define BINKSURFACE24R 2 +#define BINKSURFACE32 3 +#define BINKSURFACE32R 4 +#define BINKSURFACE32A 5 +#define BINKSURFACE32RA 6 +#define BINKSURFACE4444 7 +#define BINKSURFACE5551 8 +#define BINKSURFACE555 9 +#define BINKSURFACE565 10 +#define BINKSURFACE655 11 +#define BINKSURFACE664 12 +#define BINKSURFACEYUY2 13 +#define BINKSURFACEUYVY 14 +#define BINKSURFACEYV12 15 +#define BINKSURFACEMASK 15 + +#define BINKGOTOQUICK 1 +#define BINKGOTOQUICKSOUND 2 + +#define BINKGETKEYPREVIOUS 0 +#define BINKGETKEYNEXT 1 +#define BINKGETKEYCLOSEST 2 +#define BINKGETKEYNOTEQUAL 128 + +#define BINKDOFRAMEALL 1 +#define BINKDOFRAMEHALF 2 +#define BINKDOFRAMETHIRD 3 +#define BINKDOFRAMEFOURTH 4 +#define BINKDOFRAMEEIGHTH 5 +#define BINKDOFRAMESTART 256 +#define BINKDOFRAMEEND 512 + +#define BINKPLATFORMSOUNDTHREAD 1 +#define BINKPLATFORMIOTHREAD 2 +#define BINKPLATFORMSUBMITTHREADCOUNT 3 +#define BINKPLATFORMSUBMITTHREADS 4 + +#ifdef __RADIPHONE__ + #define BINKPLATFORMIOSINTERRUPTEND 5 +#endif + +#define BINKPLATFORMASYNCTHREADS 1024 // +0 to 7 for which async + +//======================================================================= + +#ifdef __RADNDS__ + RADEXPFUNC HBINK RADEXPLINK BinkNDSOpen(void /*FSFileID*/ * fid,U32 flags); +#endif + +RADEXPFUNC void * RADEXPLINK BinkLogoAddress(void); + +RADEXPFUNC void RADEXPLINK BinkSetError(char const * err); +RADEXPFUNC char * RADEXPLINK BinkGetError(void); + +RADEXPFUNC HBINK RADEXPLINK BinkOpen(char const * name,U32 flags); +RADEXPFUNC HBINK RADEXPLINK BinkOpenWithOptions(char const * name, BINK_OPEN_OPTIONS const * boo,U32 flags); + +RADEXPFUNC void RADEXPLINK BinkGetFrameBuffersInfo( HBINK bink, BINKFRAMEBUFFERS * fbset ); +RADEXPFUNC void RADEXPLINK BinkRegisterFrameBuffers( HBINK bink, BINKFRAMEBUFFERS * fbset ); +#ifdef __RAD3DS__ +RADEXPFUNC void RADEXPLINK BinkRegisterSwizzleBuffers( HBINK bink, BINKSWIZZLEBUFFERS* swizzle_buffers); +#endif +RADEXPFUNC S32 RADEXPLINK BinkDoFrame(HBINK bnk); +RADEXPFUNC S32 RADEXPLINK BinkDoFramePlane(HBINK bnk,U32 which_planes); +RADEXPFUNC void RADEXPLINK BinkNextFrame(HBINK bnk); +RADEXPFUNC S32 RADEXPLINK BinkWait(HBINK bnk); +RADEXPFUNC void RADEXPLINK BinkClose(HBINK bnk); +RADEXPFUNC S32 RADEXPLINK BinkPause(HBINK bnk,S32 pause); +RADEXPFUNC S32 RADEXPLINK BinkCopyToBuffer(HBINK bnk,void* dest,S32 destpitch,U32 destheight,U32 destx,U32 desty,U32 flags); +RADEXPFUNC S32 RADEXPLINK BinkCopyToBufferRect(HBINK bnk,void* dest,S32 destpitch,U32 destheight,U32 destx,U32 desty,U32 srcx, U32 srcy, U32 srcw, U32 srch, U32 flags); +RADEXPFUNC S32 RADEXPLINK BinkGetRects(HBINK bnk,U32 flags); +RADEXPFUNC void RADEXPLINK BinkGoto(HBINK bnk,U32 frame,S32 flags); // use 1 for the first frame +RADEXPFUNC U32 RADEXPLINK BinkGetKeyFrame(HBINK bnk,U32 frame,S32 flags); +RADEXPFUNC void RADEXPLINK BinkFreeGlobals( void ); +RADEXPFUNC S32 RADEXPLINK BinkGetPlatformInfo( S32 bink_platform_enum, void * output ); +RADEXPFUNC void RADEXPLINK BinkRegisterGPUDataBuffers( HBINK bink, BINKGPUDATABUFFERS * gpu ); +RADEXPFUNC S32 RADEXPLINK BinkGetGPUDataBuffersInfo( HBINK bink, BINKGPUDATABUFFERS * gpu ); + +RADEXPFUNC S32 RADEXPLINK BinkSetVideoOnOff(HBINK bnk,S32 onoff); +RADEXPFUNC S32 RADEXPLINK BinkSetSoundOnOff(HBINK bnk,S32 onoff); +RADEXPFUNC void RADEXPLINK BinkSetVolume(HBINK bnk, U32 trackid, S32 volume); +RADEXPFUNC void RADEXPLINK BinkSetPan(HBINK bnk,U32 trackid, S32 pan); +RADEXPFUNC void RADEXPLINK BinkSetSpeakerVolumes(HBINK bnk,U32 trackid, U32 * speaker_indexes, S32 * volumes, U32 total); +#define BinkSetMixBins(bnk,trackid,speaker_indexes,total) BinkSetSpeakerVolumes(bnk,trackid,speaker_indexes,0,total) +#define BinkSetMixBinVolumes(bnk,trackid,speaker_indexes,volumes,total) BinkSetSpeakerVolumes(bnk,trackid,speaker_indexes,volumes,total) +RADEXPFUNC void RADEXPLINK BinkService(HBINK bink); + +RADEXPFUNC S32 RADEXPLINK BinkShouldSkip(HBINK bink); + +RADEXPFUNC void RADEXPLINK BinkGetPalette( void * out_pal ); + +#define BINKBGIOSUSPEND 1 +#define BINKBGIORESUME 2 +#define BINKBGIOWAIT 0x80000000 +#define BINKBGIOTRYWAIT 0x40000000 + +RADEXPFUNC S32 RADEXPLINK BinkControlBackgroundIO(HBINK bink,U32 control); + +RADEXPFUNC void RADEXPLINK BinkSetWillLoop(HBINK bink,S32 onoff); + + +#if !defined( __RAD3DS__ ) && !defined (__RADPS2__) || !defined( __RADNDS__ ) + +RADEXPFUNC S32 RADEXPLINK BinkStartAsyncThread( S32 thread_num, void const * param ); +RADEXPFUNC S32 RADEXPLINK BinkDoFrameAsync(HBINK bink, U32 thread_one, U32 thread_two ); +RADEXPFUNC S32 RADEXPLINK BinkDoFrameAsyncMulti(HBINK bink, U32* threads, S32 thread_count ); +RADEXPFUNC S32 RADEXPLINK BinkDoFrameAsyncWait(HBINK bink, S32 us); +RADEXPFUNC S32 RADEXPLINK BinkRequestStopAsyncThread( S32 thread_num ); +RADEXPFUNC S32 RADEXPLINK BinkWaitStopAsyncThread( S32 thread_num ); + +#endif + +#ifdef __RADBIGENDIAN__ + +RADEXPFUNC void RADEXPLINK BinkFlipEndian32( void * buffer, U32 bytes ); + +#endif + + + +typedef struct BINKTRACK * HBINKTRACK; + +typedef struct BINKTRACK +{ + U32 Frequency; + U32 Bits; + U32 Channels; + U32 MaxSize; + + HBINK bink; + UINTa sndcomp; + S32 trackindex; +} BINKTRACK; + + +RADEXPFUNC HBINKTRACK RADEXPLINK BinkOpenTrack(HBINK bnk,U32 trackindex); +RADEXPFUNC void RADEXPLINK BinkCloseTrack(HBINKTRACK bnkt); +RADEXPFUNC U32 RADEXPLINK BinkGetTrackData(HBINKTRACK bnkt,void * dest); + +RADEXPFUNC U32 RADEXPLINK BinkGetTrackType(HBINK bnk,U32 trackindex); +RADEXPFUNC U32 RADEXPLINK BinkGetTrackMaxSize(HBINK bnk,U32 trackindex); +RADEXPFUNC U32 RADEXPLINK BinkGetTrackID(HBINK bnk,U32 trackindex); + +RADEXPFUNC void RADEXPLINK BinkGetSummary(HBINK bnk,BINKSUMMARY * sum); +RADEXPFUNC void RADEXPLINK BinkGetRealtime(HBINK bink,BINKREALTIME * run,U32 frames); + +RADEXPFUNC void RADEXPLINK BinkSetFileOffset(U64 offset); +RADEXPFUNC void RADEXPLINK BinkSetSoundTrack(U32 total_tracks, U32 * tracks); +RADEXPFUNC void RADEXPLINK BinkSetIO(BINKIOOPEN io); +RADEXPFUNC void RADEXPLINK BinkSetFrameRate(U32 forcerate,U32 forceratediv); +RADEXPFUNC void RADEXPLINK BinkSetSimulate(U32 sim); +RADEXPFUNC void RADEXPLINK BinkSetIOSize(U32 iosize); + +RADEXPFUNC S32 RADEXPLINK BinkSetSoundSystem(BINKSNDSYSOPEN open, UINTa param); +RADEXPFUNC S32 RADEXPLINK BinkSetSoundSystem2(BINKSNDSYSOPEN2 open, UINTa param1, UINTa param2); + +#ifdef __RADX86__ +#define BINK_CPU_MMX 1 +#define BINK_CPU_3DNOW 2 +#define BINK_CPU_SSE 4 +#define BINK_CPU_SSE2 8 +#elif defined( __RADMAC__ ) +#define BINK_CPU_ALTIVEC 1 +#endif + +RADEXPFUNC S32 RADEXPLINK BinkControlPlatformFeatures( S32 use, S32 dont_use ); + +#ifdef __RADWIIU__ + RADEXPFUNC void RADEXPLINK BinkSetWiiUFileClient(void* ptr_to_fsclient); +#endif + +#ifdef __RADIPHONE__ + RADEXPFUNC void RADEXPLINK BinkIOSPostAudioInterruptEnd(); +#endif + +#ifdef __RADANDROID__ + RADEXPFUNC void RADEXPLINK BinkSetAssetManager(void* asset_manager); + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS2(UINTa param1, UINTa param2); // don't call directly + + // To use your own sl engine and global mix, pass in the SLObjectItf to BinkSoundSetSLESPtrs + #define BinkSoundUseSLES(engine_ptr, global_mix_ptr) BinkSetSoundSystem2( BinkOpenRADSS2, (UINTa)(engine_ptr), (UINTa)(global_mix_ptr) ) + +#endif + +#ifdef __RADWIN__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenDirectSound(UINTa param); // don't call directly + #define BinkSoundUseDirectSound(lpDS) BinkSetSoundSystem(BinkOpenDirectSound,(UINTa)(lpDS)) + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenWaveOut(UINTa param); // don't call directly + #define BinkSoundUseWaveOut() BinkSetSoundSystem(BinkOpenWaveOut,0) + +#endif + + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenXAudio2(UINTa param); // don't call directly + #define BinkSoundUseXAudio2(IXAudio2_ptr) BinkSetSoundSystem(BinkOpenXAudio2,(UINTa)(IXAudio2_ptr)) + + +#ifndef __RADMAC__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenMiles(UINTa param); // don't call directly + #define BinkSoundUseMiles(hdigdriver) BinkSetSoundSystem(BinkOpenMiles,(UINTa)(hdigdriver)) + +#endif + +#ifdef __RADIPHONE__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS(UINTa param); // don't call directly + #define BinkSoundUseCoreAudio() BinkSetSoundSystem(BinkOpenRADSS, 0); + +#endif + +#ifdef __RADMAC__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenSoundManager(UINTa param); // don't call directly + #define BinkSoundUseSoundManager() BinkSetSoundSystem(BinkOpenSoundManager,0) + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS(UINTa param); // don't call directly + #define BinkSoundUseCoreAudio() BinkSetSoundSystem(BinkOpenRADSS,0) + +#endif + +#ifdef __RADLINUX__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS(UINTa param); // don't call directly + #define BinkSoundUseOpenAL() BinkSetSoundSystem(BinkOpenRADSS,0) + +#endif + +#if defined( __RADNGC__ ) || defined( __RADWII__ ) + + typedef void * (RADLINK * RADARAMALLOC) ( U32 num_bytes ); + typedef void (RADLINK * RADARAMFREE) ( void * ptr ); + + typedef struct RADARAMCALLBACKS + { + RADARAMALLOC aram_malloc; + RADARAMFREE aram_free; + } RADARAMCALLBACKS; + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenAX(U32 param); // don't call directly + #define BinkSoundUseAX( functions ) BinkSetSoundSystem(BinkOpenAX,(U32)(functions)) // takes a pointer to RADARAMCALLBACKS + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenMusyXSound(U32 param); // don't call directly + #define BinkSoundUseMusyX( ) BinkSetSoundSystem(BinkOpenMusyXSound,0) + +#endif + +#ifdef __RADPS2__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRAD_IOP(U32 param); // don't call directly + #define BinkSoundUseRAD_IOP( which_sound_core ) BinkSetSoundSystem(BinkOpenRAD_IOP,which_sound_core) + +#endif + +#ifdef __RADPS3__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenLibAudio( U32 param ); + #define BinkSoundUseLibAudio( number_of_speakers ) BinkSetSoundSystem( BinkOpenLibAudio, number_of_speakers ) + +#endif + +#ifdef __RADPSP__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenPSPSound( U32 param ); + #define BinkSoundUsePSPSound( which_pcm_channel ) BinkSetSoundSystem( BinkOpenPSPSound, ((U32)(S32)which_pcm_channel) ) + +#endif + +#ifdef __RADPSP2__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS( U32 param ); + #define BinkSoundUsePSP2Sound() BinkSetSoundSystem( BinkOpenRADSS, 0 ) + +#endif + + +#ifdef __RADPS4__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS( UINTa param ); + + // number_of_speakers is 2 or 8 at the moment. + #define BinkSoundUseSonySound( number_of_speakers ) BinkSetSoundSystem( BinkOpenRADSS, number_of_speakers ) + +#endif + +#ifdef __RADWIIU__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS( U32 param ); + #define BinkSoundUseWiiUSound( ) BinkSetSoundSystem( BinkOpenRADSS, 0 ) + +#endif + +#ifdef __RADNDS__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenNDSSound(U32 param); // don't call directly + #define BinkSoundUseNDSSound( param ) BinkSetSoundSystem(BinkOpenNDSSound, param) + + // Packs channel bitmask into loword and alarm bitmask into lobyte of hiword + #define BINK_PACK_CHANNEL( ch ) ((U32)(1 << ((ch) & 0xFFFF))) + #define BINK_PACK_ALARM( al ) ((U32)(1 << (16 + ((al) & 0xFF)))) + +#endif + +#ifdef __RAD3DS__ + + RADEXPFUNC BINKSNDOPEN RADEXPLINK BinkOpenRADSS(UINTa param); // don't call directly + #define BinkSoundUse3DSSound(DontServiceDSP) BinkSetSoundSystem(BinkOpenRADSS, DontServiceDSP); + +#endif + +#if defined(__RADWIN__) + +RADEXPFUNC S32 RADEXPLINK BinkDX8SurfaceType(void* lpD3Ds); + +#endif + +#if defined(__RADXENON__) || defined(__RADWIN__) + +RADEXPFUNC S32 RADEXPLINK BinkDX9SurfaceType(void* lpD3Ds); + +#endif + + +// The BinkBuffer API is only for Windows and Mac +#if defined(__RADNT__) || defined(__RADMAC__) + +//========================================================================= +typedef struct BINKBUFFER * HBINKBUFFER; + +#define BINKBUFFERSTRETCHXINT 0x80000000 +#define BINKBUFFERSTRETCHX 0x40000000 +#define BINKBUFFERSHRINKXINT 0x20000000 +#define BINKBUFFERSHRINKX 0x10000000 +#define BINKBUFFERSTRETCHYINT 0x08000000 +#define BINKBUFFERSTRETCHY 0x04000000 +#define BINKBUFFERSHRINKYINT 0x02000000 +#define BINKBUFFERSHRINKY 0x01000000 +#define BINKBUFFERSCALES 0xff000000 +#define BINKBUFFERRESOLUTION 0x00800000 + +#ifdef __RADMAC__ + + +typedef struct BINKBUFFER { + U32 Width; + U32 Height; + U32 WindowWidth; + U32 WindowHeight; + U32 SurfaceType; + void* Buffer; + S32 BufferPitch; + U32 ScreenWidth; + U32 ScreenHeight; + U32 ScreenDepth; + U32 ScaleFlags; + + S32 destx,desty; + S32 wndx,wndy; + U32 wnd; + + U32 type; + + void * gw; +} BINKBUFFER; + + +#define BINKBUFFERAUTO 0 +#define BINKBUFFERDIRECT 1 +#define BINKBUFFERGWORLD 2 +#define BINKBUFFERTYPEMASK 31 + +RADEXPFUNC HBINKBUFFER RADEXPLINK BinkBufferOpen( void* /*WindowPtr*/ wnd, U32 width, U32 height, U32 bufferflags); +RADEXPFUNC S32 RADEXPLINK BinkGDSurfaceType( void* /*GDHandle*/ gd ); + +#else + +typedef struct BINKBUFFER { + U32 Width; + U32 Height; + U32 WindowWidth; + U32 WindowHeight; + U32 SurfaceType; + void* Buffer; + S32 BufferPitch; + S32 ClientOffsetX; + S32 ClientOffsetY; + U32 ScreenWidth; + U32 ScreenHeight; + U32 ScreenDepth; + U32 ExtraWindowWidth; + U32 ExtraWindowHeight; + U32 ScaleFlags; + U32 StretchWidth; + U32 StretchHeight; + + S32 surface; + void* ddsurface; + void* ddclipper; + S32 destx,desty; + S32 wndx,wndy; + void* wnd; + S32 minimized; + S32 ddoverlay; + S32 ddoffscreen; + S32 lastovershow; + + S32 issoftcur; + U32 cursorcount; + void* buffertop; + U32 type; + S32 noclipping; + + S32 loadeddd; + S32 loadedwin; + + void* dibh; + void* dibbuffer; + S32 dibpitch; + void* dibinfo; + void* dibdc; + void* diboldbitmap; + +} BINKBUFFER; + + +#define BINKBUFFERAUTO 0 +#define BINKBUFFERPRIMARY 1 +#define BINKBUFFERDIBSECTION 2 +#define BINKBUFFERYV12OVERLAY 3 +#define BINKBUFFERYUY2OVERLAY 4 +#define BINKBUFFERUYVYOVERLAY 5 +#define BINKBUFFERYV12OFFSCREEN 6 +#define BINKBUFFERYUY2OFFSCREEN 7 +#define BINKBUFFERUYVYOFFSCREEN 8 +#define BINKBUFFERRGBOFFSCREENVIDEO 9 +#define BINKBUFFERRGBOFFSCREENSYSTEM 10 +#define BINKBUFFERLAST 10 +#define BINKBUFFERTYPEMASK 31 + +RADEXPFUNC HBINKBUFFER RADEXPLINK BinkBufferOpen( void* /*HWND*/ wnd, U32 width, U32 height, U32 bufferflags); +RADEXPFUNC S32 RADEXPLINK BinkBufferSetHWND( HBINKBUFFER buf, void* /*HWND*/ newwnd); +RADEXPFUNC S32 RADEXPLINK BinkDDSurfaceType(void * lpDDS); +RADEXPFUNC S32 RADEXPLINK BinkIsSoftwareCursor(void * lpDDSP, void* /*HCURSOR*/ cur); +RADEXPFUNC S32 RADEXPLINK BinkCheckCursor(void* /*HWND*/ wnd,S32 x,S32 y,S32 w,S32 h); +RADEXPFUNC S32 RADEXPLINK BinkBufferSetDirectDraw(void * lpDirectDraw, void * lpPrimary); + +#endif + +RADEXPFUNC void RADEXPLINK BinkBufferClose( HBINKBUFFER buf); +RADEXPFUNC S32 RADEXPLINK BinkBufferLock( HBINKBUFFER buf); +RADEXPFUNC S32 RADEXPLINK BinkBufferUnlock( HBINKBUFFER buf); +RADEXPFUNC void RADEXPLINK BinkBufferSetResolution( S32 w, S32 h, S32 bits); +RADEXPFUNC void RADEXPLINK BinkBufferCheckWinPos( HBINKBUFFER buf, S32 * NewWindowX, S32 * NewWindowY); +RADEXPFUNC S32 RADEXPLINK BinkBufferSetOffset( HBINKBUFFER buf, S32 destx, S32 desty); +RADEXPFUNC void RADEXPLINK BinkBufferBlit( HBINKBUFFER buf, BINKRECT * rects, U32 numrects ); +RADEXPFUNC S32 RADEXPLINK BinkBufferSetScale( HBINKBUFFER buf, U32 w, U32 h); +RADEXPFUNC char * RADEXPLINK BinkBufferGetDescription( HBINKBUFFER buf); +RADEXPFUNC char * RADEXPLINK BinkBufferGetError( void ); +RADEXPFUNC void RADEXPLINK BinkRestoreCursor(S32 checkcount); +RADEXPFUNC S32 RADEXPLINK BinkBufferClear(HBINKBUFFER buf, U32 RGB); + +#endif + +typedef void * (RADLINK * BINKMEMALLOC) (U32 bytes); +typedef void (RADLINK * BINKMEMFREE) (void * ptr); + +RADEXPFUNC void RADEXPLINK BinkSetMemory(BINKMEMALLOC a,BINKMEMFREE f); + +RADEXPFUNC void RADEXPLINK BinkUseTelemetry( void * context ); +RADEXPFUNC void RADEXPLINK BinkUseTmLite( void * context ); + +RADDEFEND + +#endif + +// @cdep pre $set(INCs,$INCs -I$clipfilename($file)) $ignore(TakeCPP) + +#endif + diff --git a/code/video/bink2w64.lib b/code/video/bink2w64.lib new file mode 100644 index 0000000..45ed8dd Binary files /dev/null and b/code/video/bink2w64.lib differ diff --git a/code/video/rrCore.h b/code/video/rrCore.h new file mode 100644 index 0000000..0d830f0 --- /dev/null +++ b/code/video/rrCore.h @@ -0,0 +1,2322 @@ +/// ======================================================================== +// (C) Copyright 1994- 2014 RAD Game Tools, Inc. Global types header file +// ======================================================================== + +#ifndef __RADRR_COREH__ +#define __RADRR_COREH__ +#define RADCOPYRIGHT "Copyright (C) 1994-2014, RAD Game Tools, Inc." + +// __RAD16__ means 16 bit code (Win16) +// __RAD32__ means 32 bit code (DOS, Win386, Win32s, Mac AND Win64) +// __RAD64__ means 64 bit code (x64) + +// Note oddness - __RAD32__ essentially means "at *least* 32-bit code". +// So, on 64-bit systems, both __RAD32__ and __RAD64__ will be defined. + +// __RADDOS__ means DOS code (16 or 32 bit) +// __RADWIN__ means Windows API (Win16, Win386, Win32s, Win64, Xbox, Xenon) +// __RADWINEXT__ means Windows 386 extender (Win386) +// __RADNT__ means Win32 or Win64 code +// __RADWINRTAPI__ means Windows RT API (Win 8, Win Phone, ARM, Durango) +// __RADMAC__ means Macintosh +// __RADCARBON__ means Carbon +// __RADMACH__ means MachO +// __RADXBOX__ means the XBox console +// __RADXENON__ means the Xenon console +// __RADDURANGO__ or __RADXBOXONE__ means Xbox One +// __RADNGC__ means the Nintendo GameCube +// __RADWII__ means the Nintendo Wii +// __RADWIIU__ means the Nintendo Wii U +// __RADNDS__ means the Nintendo DS +// __RADTWL__ means the Nintendo DSi (__RADNDS__ also defined) +// __RAD3DS__ means the Nintendo 3DS +// __RADPS2__ means the Sony PlayStation 2 +// __RADPSP__ means the Sony PlayStation Portable +// __RADPS3__ means the Sony PlayStation 3 +// __RADPS4__ means the Sony PlayStation 4 +// __RADANDROID__ means Android NDK +// __RADNACL__ means Native Client SDK +// __RADNTBUILDLINUX__ means building Linux on NT +// __RADLINUX__ means actually building on Linux (most likely with GCC) +// __RADPSP2__ means NGP +// __RADBSD__ means a BSD-style UNIX (OS X, FreeBSD, OpenBSD, NetBSD) +// __RADPOSIX__ means POSIX-compliant +// __RADQNX__ means QNX +// __RADIPHONE__ means iphone +// __RADIPHONESIM__ means iphone simulator + +// __RADX86__ means Intel x86 +// __RADMMX__ means Intel x86 MMX instructions are allowed +// __RADX64__ means Intel/AMD x64 (NOT IA64=Itanium) +// __RAD68K__ means 68K +// __RADPPC__ means PowerPC +// __RADMIPS__ means Mips (only R5900 right now) +// __RADARM__ mean ARM processors + +// __RADLITTLEENDIAN__ means processor is little-endian (x86) +// __RADBIGENDIAN__ means processor is big-endian (680x0, PPC) + +// __RADNOVARARGMACROS__ means #defines can't use ... + + #ifdef WINAPI_FAMILY + // If this is #defined, we might be in a Windows Store App. But + // VC++ by default #defines this to a symbolic name, not an integer + // value, and those names are defined in "winapifamily.h". So if + // WINAPI_FAMILY is #defined, #include the header so we can parse it. + #include + #define RAD_WINAPI_IS_APP (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #else + #define RAD_WINAPI_IS_APP 0 + #endif + + #ifndef __RADRES__ + // Theoretically, this is to pad structs on platforms that don't support pragma pack or do it poorly. (PS3, PS2) + // In general it is assumed that your padding is set via pragma, so this is just a struct. + #define RADSTRUCT struct + + #ifdef __GNUC_MINOR__ + // make a combined GCC version for testing : + + #define __RAD_GCC_VERSION__ (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) + + /* Test for GCC > 3.2.0 */ + // #if GCC_VERSION > 30200 + #endif + + #if defined(__RADX32__) + + #define __RADX86__ + #define __RADMMX__ + #define __RAD32__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + // known platforms under the RAD generic build type + #if defined(_WIN32) || defined(_Windows) || defined(WIN32) || defined(__WINDOWS__) || defined(_WINDOWS) + #define __RADNT__ + #define __RADWIN__ + #elif (defined(__MWERKS__) && !defined(__INTEL__)) || defined(__MRC__) || defined(THINK_C) || defined(powerc) || defined(macintosh) || defined(__powerc) || defined(__APPLE__) || defined(__MACH__) + #define __RADMAC__ + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + #elif defined(linux) + #define __RADLINUX__ + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + #endif + +#elif defined(ANDROID) + #define __RADANDROID__ + #define __RAD32__ + #define __RADLITTLEENDIAN__ + #ifdef __i386__ + #define __RADX86__ + #else + #define __RADARM__ + #endif + #define RADINLINE inline + #define RADRESTRICT __restrict + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + +#elif defined(__QNX__) + #define __RAD32__ + #define __RADQNX__ + +#ifdef __arm__ + #define __RADARM__ +#elif defined __i386__ + #define __RADX86__ +#else + #error Unknown processor +#endif + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) +#elif defined(linux) && defined(__arm__) //This should pull in Raspberry Pi as well + + #define __RAD32__ + #define __RADLINUX__ + #define __RADARM__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + +#elif defined(__native_client__) + #define __RADNACL__ + #define __RAD32__ + #define __RADLITTLEENDIAN__ + #define __RADX86__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #elif defined(_DURANGO) || defined(_SEKRIT) || defined(_SEKRIT1) || defined(_XBOX_ONE) + + #define __RADDURANGO__ 1 + #define __RADXBOXONE__ 1 + #if !defined(__RADSEKRIT__) // keep sekrit around for a bit for compat + #define __RADSEKRIT__ 1 + #endif + + #define __RADWIN__ + #define __RAD32__ + #define __RAD64__ + #define __RADX64__ + #define __RADMMX__ + #define __RADX86__ + #define __RAD64REGS__ + #define __RADLITTLEENDIAN__ + #define RADINLINE __inline + #define RADRESTRICT __restrict + #define __RADWINRTAPI__ + + #elif defined(__ORBIS__) + + #define __RADPS4__ + #if !defined(__RADSEKRIT2__) // keep sekrit2 around for a bit for compat + #define __RADSEKRIT2__ 1 + #endif + #define __RAD32__ + #define __RAD64__ + #define __RADX64__ + #define __RADMMX__ + #define __RADX86__ + #define __RAD64REGS__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #elif defined(WINAPI_FAMILY) && RAD_WINAPI_IS_APP + + #define __RADWINRTAPI__ + #define __RADWIN__ + #define RADINLINE __inline + #define RADRESTRICT __restrict + + #if defined(_M_IX86) // WinRT on x86 + + #define __RAD32__ + #define __RADX86__ + #define __RADMMX__ + #define __RADLITTLEENDIAN__ + + #elif defined(_M_X64) // WinRT on x64 + #define __RAD32__ + #define __RAD64__ + #define __RADX86__ + #define __RADX64__ + #define __RADMMX__ + #define __RAD64REGS__ + #define __RADLITTLEENDIAN__ + + #elif defined(_M_ARM) // WinRT on ARM + + #define __RAD32__ + #define __RADARM__ + #define __RADLITTLEENDIAN__ + + #else + + #error Unrecognized WinRT platform! + + #endif + + #elif defined(_WIN64) + + #define __RADWIN__ + #define __RADNT__ + // See note at top for why both __RAD32__ and __RAD64__ are defined. + #define __RAD32__ + #define __RAD64__ + #define __RADX64__ + #define __RADMMX__ + #define __RADX86__ + #define __RAD64REGS__ + #define __RADLITTLEENDIAN__ + #define RADINLINE __inline + #define RADRESTRICT __restrict + + #elif defined(GENERIC_ARM) + + #define __RAD32__ + #define __RADARM__ + #define __RADLITTLEENDIAN__ + #define __RADFIXEDPOINT__ + #define RADINLINE inline + #if (defined(__GCC__) || defined(__GNUC__)) + #define RADRESTRICT __restrict + #else + #define RADRESTRICT // __restrict not supported on cw + #endif + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #elif defined(CAFE) // has to be before HOLLYWOOD_REV since it also defines it + + #define __RADWIIU__ + #define __RAD32__ + #define __RADPPC__ + #define __RADBIGENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + + #elif defined(HOLLYWOOD_REV) || defined(REVOLUTION) + + #define __RADWII__ + #define __RAD32__ + #define __RADPPC__ + #define __RADBIGENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + #elif defined(NN_PLATFORM_CTR) + + #define __RAD3DS__ + #define __RAD32__ + #define __RADARM__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #elif defined(GEKKO) + + #define __RADNGC__ + #define __RAD32__ + #define __RADPPC__ + #define __RADBIGENDIAN__ + #define RADINLINE inline + #define RADRESTRICT // __restrict not supported on cw + + #elif defined(SDK_ARM9) || defined(SDK_TWL) || (defined(__arm) && defined(__MWERKS__)) + + #define __RADNDS__ + #define __RAD32__ + #define __RADARM__ + #define __RADLITTLEENDIAN__ + #define __RADFIXEDPOINT__ + #define RADINLINE inline + #if (defined(__GCC__) || defined(__GNUC__)) + #define RADRESTRICT __restrict + #else + #define RADRESTRICT // __restrict not supported on cw + #endif + + #if defined(SDK_TWL) + #define __RADTWL__ + #endif + + #elif defined(R5900) + + #define __RADPS2__ + #define __RAD32__ + #define __RADMIPS__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + #define __RAD64REGS__ + #define U128 u_long128 + + #if !defined(__MWERKS__) + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + #endif + + #elif defined(__psp__) + + #define __RADPSP__ + #define __RAD32__ + #define __RADMIPS__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #elif defined(__psp2__) + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #define __RADPSP2__ + #define __RAD32__ + #define __RADARM__ + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + + // need packed attribute for struct with snc? + #elif defined(__CELLOS_LV2__) + + // CB change : 10-29-10 : RAD64REGS on PPU but NOT SPU + + #ifdef __SPU__ + #define __RADSPU__ + #define __RAD32__ + #define __RADCELL__ + #define __RADBIGENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + #else + #define __RAD64REGS__ + #define __RADPS3__ + #define __RADPPC__ + #define __RAD32__ + #define __RADCELL__ + #define __RADBIGENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + #define __RADALTIVEC__ + #endif + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #ifndef __LP32__ + #error "PS3 32bit ABI support only" + #endif + #elif (defined(__MWERKS__) && !defined(__INTEL__)) || defined(__MRC__) || defined(THINK_C) || defined(powerc) || defined(macintosh) || defined(__powerc) || defined(__APPLE__) || defined(__MACH__) + #ifdef __APPLE__ + #include "TargetConditionals.h" + #endif + + #if ((defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)) + + // iPhone/iPad/iOS + #define __RADIPHONE__ + #define __RADMACAPI__ + + #define __RAD32__ + #if defined(__x86_64__) + #define __RAD64__ + #endif + + #define __RADLITTLEENDIAN__ + #define RADINLINE inline + #define RADRESTRICT __restrict + #define __RADMACH__ + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR + #if defined( __x86_64__) + #define __RADX64__ + #else + #define __RADX86__ + #endif + #define __RADIPHONESIM__ + #elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + #define __RADARM__ + #endif + #else + + // An actual MacOSX machine + #define __RADMAC__ + #define __RADMACAPI__ + + #if defined(powerc) || defined(__powerc) || defined(__ppc__) + #define __RADPPC__ + #define __RADBIGENDIAN__ + #define __RADALTIVEC__ + #define RADRESTRICT + #elif defined(__i386__) + #define __RADX86__ + #define __RADMMX__ + #define __RADLITTLEENDIAN__ + #define RADRESTRICT __restrict + #elif defined(__x86_64__) + #define __RAD32__ + #define __RAD64__ + #define __RADX86__ + #define __RADX64__ + #define __RAD64REGS__ + #define __RADMMX__ + #define __RADLITTLEENDIAN__ + #define RADRESTRICT __restrict + #else + #define __RAD68K__ + #define __RADBIGENDIAN__ + #define __RADALTIVEC__ + #define RADRESTRICT + #endif + + #define __RAD32__ + + #if defined(__MWERKS__) + #if (defined(__cplusplus) || ! __option(only_std_keywords)) + #define RADINLINE inline + #endif + #ifdef __MACH__ + #define __RADMACH__ + #endif + #elif defined(__MRC__) + #if defined(__cplusplus) + #define RADINLINE inline + #endif + #elif defined(__GNUC__) || defined(__GNUG__) || defined(__MACH__) + #define RADINLINE inline + #define __RADMACH__ + + #undef RADRESTRICT /* could have been defined above... */ + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + #endif + + #ifdef __RADX86__ + #ifndef __RADCARBON__ + #define __RADCARBON__ + #endif + #endif + + #ifdef TARGET_API_MAC_CARBON + #if TARGET_API_MAC_CARBON + #ifndef __RADCARBON__ + #define __RADCARBON__ + #endif + #endif + #endif + #endif + #elif defined(linux) + + #define __RADLINUX__ + #define __RADMMX__ + #define __RADLITTLEENDIAN__ + #define __RADX86__ + #ifdef __x86_64 + #define __RAD32__ + #define __RAD64__ + #define __RADX64__ + #define __RAD64REGS__ + #else + #define __RAD32__ + #endif + #define RADINLINE inline + #define RADRESTRICT __restrict + + #undef RADSTRUCT + #define RADSTRUCT struct __attribute__((__packed__)) + + #else + + #if _MSC_VER >= 1400 + #undef RADRESTRICT + #define RADRESTRICT __restrict + #else + #define RADRESTRICT + #define __RADNOVARARGMACROS__ + #endif + + #if defined(_XENON) || ( defined(_XBOX_VER) && (_XBOX_VER == 200) ) + // Remember that Xenon also defines _XBOX + #define __RADPPC__ + #define __RADBIGENDIAN__ + #define __RADALTIVEC__ + #else + #define __RADX86__ + #define __RADMMX__ + #define __RADLITTLEENDIAN__ + #endif + + #ifdef __MWERKS__ + #define _WIN32 + #endif + + #ifdef __DOS__ + #define __RADDOS__ + #define S64_DEFINED // turn off these types + #define U64_DEFINED + #define S64 double //should error + #define U64 double //should error + #define __RADNOVARARGMACROS__ + #endif + + #ifdef __386__ + #define __RAD32__ + #endif + + #ifdef _Windows //For Borland + #ifdef __WIN32__ + #define WIN32 + #else + #define __WINDOWS__ + #endif + #endif + + #ifdef _WINDOWS //For MS + #ifndef _WIN32 + #define __WINDOWS__ + #endif + #endif + + #ifdef _WIN32 + #if defined(_XENON) || ( defined(_XBOX_VER) && (_XBOX_VER == 200) ) + // Remember that Xenon also defines _XBOX + #define __RADXENON__ + #define __RAD64REGS__ + #elif defined(_XBOX) + #define __RADXBOX__ + #elif !defined(__RADWINRTAPI__) + #define __RADNT__ + #endif + #define __RADWIN__ + #define __RAD32__ + #else + #ifdef __NT__ + #if defined(_XENON) || (_XBOX_VER == 200) + // Remember that Xenon also defines _XBOX + #define __RADXENON__ + #define __RAD64REGS__ + #elif defined(_XBOX) + #define __RADXBOX__ + #else + #define __RADNT__ + #endif + #define __RADWIN__ + #define __RAD32__ + #else + #ifdef __WINDOWS_386__ + #define __RADWIN__ + #define __RADWINEXT__ + #define __RAD32__ + #define S64_DEFINED // turn off these types + #define U64_DEFINED + #define S64 double //should error + #define U64 double //should error + #else + #ifdef __WINDOWS__ + #define __RADWIN__ + #define __RAD16__ + #else + #ifdef WIN32 + #if defined(_XENON) || (_XBOX_VER == 200) + // Remember that Xenon also defines _XBOX + #define __RADXENON__ + #elif defined(_XBOX) + #define __RADXBOX__ + #else + #define __RADNT__ + #endif + #define __RADWIN__ + #define __RAD32__ + #endif + #endif + #endif + #endif + #endif + + #ifdef __WATCOMC__ + #define RADINLINE + #else + #define RADINLINE __inline + #endif + #endif + + #if defined __RADMAC__ || defined __RADIPHONE__ + #define __RADBSD__ + #endif + + #if defined __RADBSD__ || defined __RADLINUX__ + #define __RADPOSIX__ + #endif + + #if (!defined(__RADDOS__) && !defined(__RADWIN__) && !defined(__RADMAC__) && \ + !defined(__RADNGC__) && !defined(__RADNDS__) && !defined(__RADXBOX__) && \ + !defined(__RADXENON__) && !defined(__RADDURANGO__) && !defined(__RADPS4__) && !defined(__RADLINUX__) && !defined(__RADPS2__) && \ + !defined(__RADPSP__) && !defined(__RADPSP2__) && !defined(__RADPS3__) && !defined(__RADSPU__) && \ + !defined(__RADWII__) && !defined(__RADIPHONE__) && !defined(__RADX32__) && !defined(__RADARM__) && \ + !defined(__RADWIIU__) && !defined(__RADANDROID__) && !defined(__RADNACL__) && !defined (__RADQNX__) ) + #error "RAD.H did not detect your platform. Define DOS, WINDOWS, WIN32, macintosh, powerpc, or appropriate console." + #endif + + + #ifdef __RADFINAL__ + #define RADTODO(str) { char __str[0]=str; } + #else + #define RADTODO(str) + #endif + + #ifdef __RADX32__ + #if defined(_MSC_VER) + #define RADLINK __stdcall + #define RADEXPLINK __stdcall + #else + #define RADLINK __attribute__((stdcall)) + #define RADEXPLINK __attribute__((stdcall)) + #endif + #define RADEXPFUNC RADDEFFUNC + + #elif (defined(__RADNGC__) || defined(__RADWII__) || defined( __RADPS2__) || \ + defined(__RADPSP__) || defined(__RADPSP2__) || defined(__RADPS3__) || \ + defined(__RADSPU__) || defined(__RADNDS__) || defined(__RADIPHONE__) || \ + (defined(__RADARM__) && !defined(__RADWINRTAPI__)) || defined(__RADWIIU__) || defined(__RADPS4__) ) + + #define RADLINK + #define RADEXPLINK + #define RADEXPFUNC RADDEFFUNC + #define RADASMLINK + + #elif defined(__RADANDROID__) + #define RADLINK + #define RADEXPLINK + #define RADEXPFUNC RADDEFFUNC + #define RADASMLINK + #elif defined(__RADNACL__) + #define RADLINK + #define RADEXPLINK + #define RADEXPFUNC RADDEFFUNC + #define RADASMLINK + #elif defined(__RADLINUX__) || defined (__RADQNX__) + + #ifdef __RAD64__ + #define RADLINK + #define RADEXPLINK + #else + #define RADLINK __attribute__((cdecl)) + #define RADEXPLINK __attribute__((cdecl)) + #endif + + #define RADEXPFUNC RADDEFFUNC + #define RADASMLINK + + #elif defined(__RADMAC__) + + // this define is for CodeWarrior 11's stupid new libs (even though + // we don't use longlong's). + + #define __MSL_LONGLONG_SUPPORT__ + + #define RADLINK + #define RADEXPLINK + + #if defined(__CFM68K__) || defined(__MWERKS__) + #ifdef __RADINDLL__ + #define RADEXPFUNC RADDEFFUNC __declspec(export) + #else + #define RADEXPFUNC RADDEFFUNC __declspec(import) + #endif + #else + #if defined(__RADMACH__) && !defined(__MWERKS__) + #ifdef __RADINDLL__ + #define RADEXPFUNC RADDEFFUNC __attribute__((visibility("default"))) + #else + #define RADEXPFUNC RADDEFFUNC + #endif + #else + #define RADEXPFUNC RADDEFFUNC + #endif + #endif + #define RADASMLINK + + #else + + #ifdef __RADNT__ + #ifndef _WIN32 + #define _WIN32 + #endif + #ifndef WIN32 + #define WIN32 + #endif + #endif + + #ifdef __RADWIN__ + #ifdef __RAD32__ + + #ifdef __RADXBOX__ + + #define RADLINK __stdcall + #define RADEXPLINK __stdcall + #define RADEXPFUNC RADDEFFUNC + + #elif defined(__RADXENON__) || defined(__RADDURANGO__) + + #define RADLINK __stdcall + #define RADEXPLINK __stdcall + + #define RADEXPFUNC RADDEFFUNC + + #elif defined(__RADWINRTAPI__) + + #define RADLINK __stdcall + #define RADEXPLINK __stdcall + + #if ( defined(__RADINSTATICLIB__) || defined(__RADNOEXPORTS__ ) || ( defined(__RADNOEXEEXPORTS__) && ( !defined(__RADINDLL__) ) && ( !defined(__RADINSTATICLIB__) ) ) ) + #define RADEXPFUNC RADDEFFUNC + #else + #ifndef __RADINDLL__ + #define RADEXPFUNC RADDEFFUNC __declspec(dllimport) + #else + #define RADEXPFUNC RADDEFFUNC __declspec(dllexport) + #endif + #endif + + #elif defined(__RADNTBUILDLINUX__) + + #define RADLINK __cdecl + #define RADEXPLINK __cdecl + #define RADEXPFUNC RADDEFFUNC + + #else + #ifdef __RADNT__ + + #define RADLINK __stdcall + #define RADEXPLINK __stdcall + + #if ( defined(__RADINSTATICLIB__) || defined(__RADNOEXPORTS__ ) || ( defined(__RADNOEXEEXPORTS__) && ( !defined(__RADINDLL__) ) && ( !defined(__RADINSTATICLIB__) ) ) ) + #define RADEXPFUNC RADDEFFUNC + #else + #ifndef __RADINDLL__ + #define RADEXPFUNC RADDEFFUNC __declspec(dllimport) + #ifdef __BORLANDC__ + #if __BORLANDC__<=0x460 + #undef RADEXPFUNC + #define RADEXPFUNC RADDEFFUNC + #endif + #endif + #else + #define RADEXPFUNC RADDEFFUNC __declspec(dllexport) + #endif + #endif + #else + #define RADLINK __pascal + #define RADEXPLINK __far __pascal + #define RADEXPFUNC RADDEFFUNC + #endif + #endif + #else + #define RADLINK __pascal + #define RADEXPLINK __far __pascal __export + #define RADEXPFUNC RADDEFFUNC + #endif + #else + #define RADLINK __pascal + #define RADEXPLINK __pascal + #define RADEXPFUNC RADDEFFUNC + #endif + + #define RADASMLINK __cdecl + + #endif + + #if !defined(__RADXBOX__) && !defined(__RADXENON__) && !defined(__RADDURANGO__) && !defined(__RADXBOXONE__) + #ifdef __RADWIN__ + #ifndef _WINDOWS + #define _WINDOWS + #endif + #endif + #endif + + #ifdef __RADLITTLEENDIAN__ + #ifdef __RADBIGENDIAN__ + #error both endians !? + #endif + #endif + + #if !defined(__RADLITTLEENDIAN__) && !defined(__RADBIGENDIAN__) + #error neither endian! + #endif + + + //----------------------------------------------------------------- + + #ifndef RADDEFFUNC + + #ifdef __cplusplus + #define RADDEFFUNC extern "C" + #define RADDEFSTART extern "C" { + #define RADDEFEND } + #define RADDEFINEDATA extern "C" + #define RADDECLAREDATA extern "C" + #define RADDEFAULT( val ) =val + + #define RR_NAMESPACE rr + #define RR_NAMESPACE_START namespace RR_NAMESPACE { + #define RR_NAMESPACE_END }; + #define RR_NAMESPACE_USE using namespace RR_NAMESPACE; + + #else + #define RADDEFFUNC + #define RADDEFSTART + #define RADDEFEND + #define RADDEFINEDATA + #define RADDECLAREDATA extern + #define RADDEFAULT( val ) + + #define RR_NAMESPACE + #define RR_NAMESPACE_START + #define RR_NAMESPACE_END + #define RR_NAMESPACE_USE + + #endif + + #endif + + // probably s.b: RAD_DECLARE_ALIGNED(type, name, alignment) + #if (defined(__RADWII__) || defined(__RADWIIU__) || defined(__RADPSP__) || defined(__RADPSP2__) || \ + defined(__RADPS3__) || defined(__RADSPU__) || defined(__RADPS4__) || \ + defined(__RADLINUX__) || defined(__RADMAC__)) || defined(__RADNDS__) || defined(__RAD3DS__) || \ + defined(__RADIPHONE__) || defined(__RADANDROID__) || defined (__RADQNX__) + #define RAD_ALIGN(type,var,num) type __attribute__ ((aligned (num))) var + #elif (defined(__RADNGC__) || defined(__RADPS2__)) + #define RAD_ALIGN(type,var,num) __attribute__ ((aligned (num))) type var + #elif (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__RADWINRTAPI__) + #define RAD_ALIGN(type,var,num) type __declspec(align(num)) var + #else + // NOTE: / / is a guaranteed parse error in C/C++. + #define RAD_ALIGN(type,var,num) RAD_ALIGN_USED_BUT_NOT_DEFINED / / + #endif + + // WARNING : RAD_TLS should really only be used for debug/tools stuff + // it's not reliable because even if we are built as a lib, our lib can + // be put into a DLL and then it doesn't work + #if defined(__RADNT__) || defined(__RADXENON__) + #ifndef __RADINDLL__ + // note that you can't use this in windows DLLs + #define RAD_TLS(type,var) __declspec(thread) type var + #endif + #elif defined(__RADPS3__) || defined(__RADLINUX__) || defined(__RADMAC__) + // works on PS3/gcc I believe : + #define RAD_TLS(type,var) __thread type var + #else + // RAD_TLS not defined + #endif + + // Note that __RAD16__/__RAD32__/__RAD64__ refers to the size of a pointer. + // The size of integers is specified explicitly in the code, i.e. u32 or whatever. + + #define RAD_S8 signed char + #define RAD_U8 unsigned char + + #if defined(__RAD64__) + // Remember that __RAD32__ will also be defined! + #if defined(__RADX64__) + // x64 still has 32-bit ints! + #define RAD_U32 unsigned int + #define RAD_S32 signed int + // But pointers are 64 bits. + #if (_MSC_VER >= 1300 && defined(_Wp64) && _Wp64 ) + #define RAD_SINTa __w64 signed __int64 + #define RAD_UINTa __w64 unsigned __int64 + #else // non-vc.net compiler or /Wp64 turned off + #define RAD_UINTa unsigned long long + #define RAD_SINTa signed long long + #endif + #else + #error Unknown 64-bit processor (see radbase.h) + #endif + #elif defined(__RAD32__) + #define RAD_U32 unsigned int + #define RAD_S32 signed int + // Pointers are 32 bits. + + #if ( ( defined(_MSC_VER) && (_MSC_VER >= 1300 ) ) && ( defined(_Wp64) && ( _Wp64 ) ) ) + #define RAD_SINTa __w64 signed long + #define RAD_UINTa __w64 unsigned long + #else // non-vc.net compiler or /Wp64 turned off + #ifdef _Wp64 + #define RAD_SINTa signed long + #define RAD_UINTa unsigned long + #else + #define RAD_SINTa signed int + #define RAD_UINTa unsigned int + #endif + #endif + #else + #define RAD_U32 unsigned long + #define RAD_S32 signed long + // Pointers in 16-bit land are still 32 bits. + #define RAD_UINTa unsigned long + #define RAD_SINTa signed long + #endif + + #define RAD_F32 float + #if defined(__RADPS2__) || defined(__RADPSP__) + typedef RADSTRUCT RAD_F64 // do this so that we don't accidentally use doubles + { // while using the same space + RAD_U32 vals[ 2 ]; + } RAD_F64; + #define RAD_F64_OR_32 float // type is F64 if available, otherwise F32 + #else + #define RAD_F64 double + #define RAD_F64_OR_32 double // type is F64 if available, otherwise F32 + #endif + + #if (defined(__RADMAC__) || defined(__MRC__) || defined( __RADNGC__ ) || \ + defined(__RADLINUX__) || defined( __RADWII__ ) || defined(__RADWIIU__) || \ + defined(__RADNDS__) || defined(__RADPSP__) || defined(__RADPS3__) || defined(__RADPS4__) || \ + defined(__RADSPU__) || defined(__RADIPHONE__) || defined(__RADNACL__) || defined( __RADANDROID__) || defined( __RADQNX__ ) ) + #define RAD_U64 unsigned long long + #define RAD_S64 signed long long + #elif defined(__RADPS2__) + #define RAD_U64 unsigned long + #define RAD_S64 signed long + #elif defined(__RADARM__) + #define RAD_U64 unsigned long long + #define RAD_S64 signed long long + #elif defined(__RADX64__) || defined(__RAD32__) + #define RAD_U64 unsigned __int64 + #define RAD_S64 signed __int64 + #else + // 16-bit + typedef RADSTRUCT RAD_U64 // do this so that we don't accidentally use U64s + { // while using the same space + RAD_U32 vals[ 2 ]; + } RAD_U64; + typedef RADSTRUCT RAD_S64 // do this so that we don't accidentally use S64s + { // while using the same space + RAD_S32 vals[ 2 ]; + } RAD_S64; + #endif + + #if defined(__RAD32__) + #define PTR4 + #define RAD_U16 unsigned short + #define RAD_S16 signed short + #else + #define PTR4 __far + #define RAD_U16 unsigned int + #define RAD_S16 signed int + #endif + + //------------------------------------------------- + // RAD_PTRBITS and such defined here without using sizeof() + // so that they can be used in align() and other macros + + #ifdef __RAD64__ + + #define RAD_PTRBITS 64 + #define RAD_PTRBYTES 8 + #define RAD_TWOPTRBYTES 16 + + #else + + #define RAD_PTRBITS 32 + #define RAD_PTRBYTES 4 + #define RAD_TWOPTRBYTES 8 + + #endif + + + //------------------------------------------------- + // UINTr = int the size of a register + + #ifdef __RAD64REGS__ + + #define RAD_UINTr RAD_U64 + #define RAD_SINTr RAD_S64 + + #else + + #define RAD_UINTr RAD_U32 + #define RAD_SINTr RAD_S32 + + #endif + + //=========================================================================== + + /* + // CB : meh this is enough of a mess that it's probably best to just let each + #if defined(__RADX86__) && defined(_MSC_VER) && _MSC_VER >= 1300 + #define __RADX86INTRIN2003__ + #endif + */ + + // RADASSUME(expr) tells the compiler that expr is always true + // RADUNREACHABLE must never be reachable - even in event of error + // eg. it's okay for compiler to generate completely invalid code after RADUNREACHABLE + + #ifdef _MSC_VER + #define RADFORCEINLINE __forceinline + #if _MSC_VER >= 1300 + #define RADNOINLINE __declspec(noinline) + #else + #define RADNOINLINE + #endif + #define RADUNREACHABLE __assume(0) + #define RADASSUME(exp) __assume(exp) + #elif defined(__clang__) + #ifdef _DEBUG + #define RADFORCEINLINE inline + #else + #define RADFORCEINLINE inline __attribute((always_inline)) + #endif + #define RADNOINLINE __attribute__((noinline)) + + #define RADUNREACHABLE __builtin_unreachable() + + #if __has_builtin(__builtin_assume) + #define RADASSUME(exp) __builtin_assume(exp) + #else + #define RADASSUME(exp) RAD_STATEMENT_WRAPPER( if ( ! (exp) ) __builtin_unreachable(); ) + #endif + #elif (defined(__GCC__) || defined(__GNUC__)) || defined(ANDROID) + #ifdef _DEBUG + #define RADFORCEINLINE inline + #else + #define RADFORCEINLINE inline __attribute((always_inline)) + #endif + #define RADNOINLINE __attribute__((noinline)) + + #if __RAD_GCC_VERSION__ >= 40500 + #define RADUNREACHABLE __builtin_unreachable() + #define RADASSUME(exp) RAD_STATEMENT_WRAPPER( if ( ! (exp) ) __builtin_unreachable(); ) + #else + #define RADUNREACHABLE RAD_INFINITE_LOOP( RR_BREAK(); ) + #define RADASSUME(exp) + #endif + #elif defined(__CWCC__) + #define RADFORCEINLINE inline + #define RADNOINLINE __attribute__((never_inline)) + #define RADUNREACHABLE + #define RADASSUME(x) (void)0 + #else + // ? #define RADFORCEINLINE ? + #define RADFORCEINLINE inline + #define RADNOINLINE + #define RADASSUME(x) (void)0 + #endif + + //=========================================================================== + + // RAD_ALIGN_HINT tells the compiler how a given pointer is aligned + // it *must* be true, but the compiler may or may not use that information + // it is not for cases where the pointer is to an inherently aligned data type, + // it's when the compiler cannot tell the alignment but you have extra information. + // eg : + // U8 * ptr = rrMallocAligned(256,16); + // RAD_ALIGN_HINT(ptr,16,0); + + #ifdef __RADSPU__ + #define RAD_ALIGN_HINT(ptr,alignment,offset) __align_hint(ptr,alignment,offset); RR_ASSERT( ((UINTa)(ptr) & ((alignment)-1)) == (UINTa)(offset) ) + #else + #define RAD_ALIGN_HINT(ptr,alignment,offset) RADASSUME( ((UINTa)(ptr) & ((alignment)-1)) == (UINTa)(offset) ) + #endif + + //=========================================================================== + + // RAD_EXPECT is to tell the compiler the *likely* value of an expression + // different than RADASSUME in that expr might not have that value + // it's use for branch code layout and static branch prediction + // condition can technically be a variable but should usually be 0 or 1 + + #if (defined(__GCC__) || defined(__GNUC__)) || defined(__clang__) + + // __builtin_expect returns value of expr + #define RAD_EXPECT(expr,cond) __builtin_expect(expr,cond) + + #else + + #define RAD_EXPECT(expr,cond) (expr) + + #endif + + // helpers for doing an if ( ) with expect : + // if ( RAD_LIKELY(expr) ) { ... } + + #define RAD_LIKELY(expr) RAD_EXPECT(expr,1) + #define RAD_UNLIKELY(expr) RAD_EXPECT(expr,0) + + //=========================================================================== + + // __RADX86ASM__ means you can use __asm {} style inline assembly + #if defined(__RADX86__) && !defined(__RADX64__) && defined(_MSC_VER) + #define __RADX86ASM__ + #endif + + //------------------------------------------------- + // typedefs : + + #ifndef RADNOTYPEDEFS + + #ifndef S8_DEFINED + #define S8_DEFINED + typedef RAD_S8 S8; + #endif + + #ifndef U8_DEFINED + #define U8_DEFINED + typedef RAD_U8 U8; + #endif + + #ifndef S16_DEFINED + #define S16_DEFINED + typedef RAD_S16 S16; + #endif + + #ifndef U16_DEFINED + #define U16_DEFINED + typedef RAD_U16 U16; + #endif + + #ifndef S32_DEFINED + #define S32_DEFINED + typedef RAD_S32 S32; + #endif + + #ifndef U32_DEFINED + #define U32_DEFINED + typedef RAD_U32 U32; + #endif + + #ifndef S64_DEFINED + #define S64_DEFINED + typedef RAD_S64 S64; + #endif + + #ifndef U64_DEFINED + #define U64_DEFINED + typedef RAD_U64 U64; + #endif + + #ifndef F32_DEFINED + #define F32_DEFINED + typedef RAD_F32 F32; + #endif + + #ifndef F64_DEFINED + #define F64_DEFINED + typedef RAD_F64 F64; + #endif + + #ifndef F64_OR_32_DEFINED + #define F64_OR_32_DEFINED + typedef RAD_F64_OR_32 F64_OR_32; + #endif + + // UINTa and SINTa are the ints big enough for an address + + #ifndef SINTa_DEFINED + #define SINTa_DEFINED + typedef RAD_SINTa SINTa; + #endif + + #ifndef UINTa_DEFINED + #define UINTa_DEFINED + typedef RAD_UINTa UINTa; + #endif + + #ifndef UINTr_DEFINED + #define UINTr_DEFINED + typedef RAD_UINTr UINTr; + #endif + + #ifndef SINTr_DEFINED + #define SINTr_DEFINED + typedef RAD_SINTr SINTr; + #endif + + #elif !defined(RADNOTYPEDEFINES) + + #ifndef S8_DEFINED + #define S8_DEFINED + #define S8 RAD_S8 + #endif + + #ifndef U8_DEFINED + #define U8_DEFINED + #define U8 RAD_U8 + #endif + + #ifndef S16_DEFINED + #define S16_DEFINED + #define S16 RAD_S16 + #endif + + #ifndef U16_DEFINED + #define U16_DEFINED + #define U16 RAD_U16 + #endif + + #ifndef S32_DEFINED + #define S32_DEFINED + #define S32 RAD_S32 + #endif + + #ifndef U32_DEFINED + #define U32_DEFINED + #define U32 RAD_U32 + #endif + + #ifndef S64_DEFINED + #define S64_DEFINED + #define S64 RAD_S64 + #endif + + #ifndef U64_DEFINED + #define U64_DEFINED + #define U64 RAD_U64 + #endif + + #ifndef F32_DEFINED + #define F32_DEFINED + #define F32 RAD_F32 + #endif + + #ifndef F64_DEFINED + #define F64_DEFINED + #define F64 RAD_F64 + #endif + + #ifndef F64_OR_32_DEFINED + #define F64_OR_32_DEFINED + #define F64_OR_32 RAD_F64_OR_32 + #endif + + // UINTa and SINTa are the ints big enough for an address (pointer) + #ifndef SINTa_DEFINED + #define SINTa_DEFINED + #define SINTa RAD_SINTa + #endif + + #ifndef UINTa_DEFINED + #define UINTa_DEFINED + #define UINTa RAD_UINTa + #endif + + #ifndef UINTr_DEFINED + #define UINTr_DEFINED + #define UINTr RAD_UINTr + #endif + + #ifndef SINTr_DEFINED + #define SINTr_DEFINED + #define SINTr RAD_SINTr + #endif + + #endif + + /// Some error-checking. + #if defined(__RAD64__) && !defined(__RAD32__) + // See top of file for why this is. + #error __RAD64__ must not be defined without __RAD32__ (see radbase.h) + #endif + +#ifdef _MSC_VER + // microsoft compilers + + #if _MSC_VER >= 1400 + #define RAD_STATEMENT_START \ + do { + + #define RAD_STATEMENT_END_FALSE \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + } while(0) \ + __pragma(warning(pop)) + + #define RAD_STATEMENT_END_TRUE \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + } while(1) \ + __pragma(warning(pop)) + + #else + #define RAD_USE_STANDARD_LOOP_CONSTRUCT + #endif +#else + #define RAD_USE_STANDARD_LOOP_CONSTRUCT +#endif + +#ifdef RAD_USE_STANDARD_LOOP_CONSTRUCT + #define RAD_STATEMENT_START \ + do { + + #define RAD_STATEMENT_END_FALSE \ + } while ( (void)0,0 ) + + #define RAD_STATEMENT_END_TRUE \ + } while ( (void)1,1 ) + +#endif + +#define RAD_STATEMENT_WRAPPER( code ) \ + RAD_STATEMENT_START \ + code \ + RAD_STATEMENT_END_FALSE + +#define RAD_INFINITE_LOOP( code ) \ + RAD_STATEMENT_START \ + code \ + RAD_STATEMENT_END_TRUE + + +// Must be placed after variable declarations for code compiled as .c +#if defined(_MSC_VER) && _MSC_VER >= 1700 // in 2012 aka 11.0 and later +# define RR_UNUSED_VARIABLE(x) (void) x +#else +# define RR_UNUSED_VARIABLE(x) (void)(sizeof(x)) +#endif + +//----------------------------------------------- +// RR_UINT3264 is a U64 in 64-bit code and a U32 in 32-bit code +// eg. it's pointer sized and the same type as a U32/U64 of the same size +// +// @@ CB 05/21/2012 : I think RR_UINT3264 may be deprecated +// it was useful back when UINTa was /Wp64 +// but since we removed that maybe it's not anymore ? +// + +#ifdef __RAD64__ +#define RR_UINT3264 U64 +#else +#define RR_UINT3264 U32 +#endif + +//RR_COMPILER_ASSERT( sizeof(RR_UINT3264) == sizeof(UINTa) ); + +//-------------------------------------------------- + +// RR_LINESTRING is the current line number as a string +#define RR_STRINGIZE( L ) #L +#define RR_DO_MACRO( M, X ) M(X) +#define RR_STRINGIZE_DELAY( X ) RR_DO_MACRO( RR_STRINGIZE, X ) +#define RR_LINESTRING RR_STRINGIZE_DELAY( __LINE__ ) + +#define RR_CAT(X,Y) X ## Y + +// RR_STRING_JOIN joins strings in the preprocessor and works with LINESTRING +#define RR_STRING_JOIN(arg1, arg2) RR_STRING_JOIN_DELAY(arg1, arg2) +#define RR_STRING_JOIN_DELAY(arg1, arg2) RR_STRING_JOIN_IMMEDIATE(arg1, arg2) +#define RR_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 + +// RR_NUMBERNAME is a macro to make a name unique, so that you can use it to declare +// variable names and they won't conflict with each other +// using __LINE__ is broken in MSVC with /ZI , but __COUNTER__ is an MSVC extension that works + +#ifdef _MSC_VER + #define RR_NUMBERNAME(name) RR_STRING_JOIN(name,__COUNTER__) +#else + #define RR_NUMBERNAME(name) RR_STRING_JOIN(name,__LINE__) +#endif + +//-------------------------------------------------- +// current plan is to use "rrbool" with plain old "true" and "false" +// if true and false give us trouble we might have to go to rrtrue and rrfalse +// BTW there's a danger for evil bugs here !! If you're checking == true +// then the rrbool must be set to exactly "1" not just "not zero" !! + +#ifndef RADNOTYPEDEFS + #ifndef RRBOOL_DEFINED + #define RRBOOL_DEFINED + typedef S32 rrbool; + typedef S32 RRBOOL; + #endif +#elif !defined(RADNOTYPEDEFINES) + #ifndef RRBOOL_DEFINED + #define RRBOOL_DEFINED + #define rrbool S32 + #define RRBOOL S32 + #endif +#endif + +//-------------------------------------------------- +// Range macros + + #ifndef RR_MIN + #define RR_MIN(a,b) ( (a) < (b) ? (a) : (b) ) + #endif + + #ifndef RR_MAX + #define RR_MAX(a,b) ( (a) > (b) ? (a) : (b) ) + #endif + + #ifndef RR_ABS + #define RR_ABS(a) ( ((a) < 0) ? -(a) : (a) ) + #endif + + #ifndef RR_CLAMP + #define RR_CLAMP(val,lo,hi) RR_MAX( RR_MIN(val,hi), lo ) + #endif + +//-------------------------------------------------- +// Data layout macros + + #define RR_ARRAY_SIZE(array) ( sizeof(array)/sizeof(array[0]) ) + + // MEMBER_OFFSET tells you the offset of a member in a type + #ifdef __RAD3DS__ + #define RR_MEMBER_OFFSET(type,member) (unsigned int)(( (char *) &(((type *)0)->member) - (char *) 0 )) + #elif defined(__RADANDROID__) || defined(__RADPSP__) || defined(__RADPS3__) || defined(__RADSPU__) + // offsetof() gets mucked with by system headers on android, making things dependent on #include order. + #define RR_MEMBER_OFFSET(type,member) __builtin_offsetof(type, member) + #elif defined(__RADLINUX__) + #define RR_MEMBER_OFFSET(type,member) (offsetof(type, member)) + #else + #define RR_MEMBER_OFFSET(type,member) ( (size_t) (UINTa) &(((type *)0)->member) ) + #endif + + // MEMBER_SIZE tells you the size of a member in a type + #define RR_MEMBER_SIZE(type,member) ( sizeof( ((type *) 0)->member) ) + + // just to make gcc shut up about derefing null : + #define RR_MEMBER_OFFSET_PTR(type,member,ptr) ( (SINTa) &(((type *)(ptr))->member) - (SINTa)(ptr) ) + #define RR_MEMBER_SIZE_PTR(type,member,ptr) ( sizeof( ((type *) (ptr))->member) ) + + // MEMBER_TO_OWNER takes a pointer to a member and gives you back the base of the object + // you should then RR_ASSERT( &(ret->member) == ptr ); + #define RR_MEMBER_TO_OWNER(type,member,ptr) (type *)( ((char *)(ptr)) - RR_MEMBER_OFFSET_PTR(type,member,ptr) ) + +//-------------------------------------------------- +// Cache / prefetch macros : + +// RR_PREFETCH for various platforms : +// +// RR_PREFETCH_SEQUENTIAL : prefetch memory for reading in a sequential scan +// platforms that automatically prefetch sequential (eg. PC) should be a no-op here +// RR_PREFETCH_WRITE_INVALIDATE : prefetch memory for writing - contents of memory are undefined +// (may be a no-op, may be a normal prefetch, may zero memory) +// warning : RR_PREFETCH_WRITE_INVALIDATE may write memory so don't do it past the end of buffers + +#ifdef __RADX86__ + +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) // nop +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) // nop + +#elif defined(__RADXENON__) + +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) __dcbt((int)(offset),(void *)(ptr)) +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) __dcbz128((int)(offset),(void *)(ptr)) + +#elif defined(__RADPS3__) + +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) __dcbt((char *)(ptr) + (int)(offset)) +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) __dcbz((char *)(ptr) + (int)(offset)) + +#elif defined(__RADSPU__) + +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) // intentional NOP +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) // nop + +#elif defined(__RADWII__) || defined(__RADWIIU__) + +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) // intentional NOP for now +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) // nop + +#elif defined(__RAD3DS__) + +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) __pld((char *)(ptr) + (int)(offset)) +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) __pldw((char *)(ptr) + (int)(offset)) + +#else + +// other platform +#define RR_PREFETCH_SEQUENTIAL(ptr,offset) // need_prefetch // compile error +#define RR_PREFETCH_WRITE_INVALIDATE(ptr,offset) // need_writezero // error + +#endif + +//-------------------------------------------------- +// LIGHTWEIGHT ASSERTS without rrAssert.h + +RADDEFSTART + +// set up RR_BREAK : + + #ifdef __RADNGC__ + + #define RR_BREAK() asm(" .long 0x00000001") + #define RR_CACHE_LINE_SIZE xxx + + #elif defined(__RADWII__) + + #define RR_BREAK() __asm__ volatile("trap") + #define RR_CACHE_LINE_SIZE 32 + + #elif defined(__RADWIIU__) + + #define RR_BREAK() asm("trap") + #define RR_CACHE_LINE_SIZE 32 + + #elif defined(__RAD3DS__) + + #define RR_BREAK() *((int volatile*)0)=0 + #define RR_CACHE_LINE_SIZE 32 + + #elif defined(__RADNDS__) + + #define RR_BREAK() asm("BKPT 0") + #define RR_CACHE_LINE_SIZE xxx + + #elif defined(__RADPS2__) + + #define RR_BREAK() __asm__ volatile("break") + #define RR_CACHE_LINE_SIZE 64 + + #elif defined(__RADPSP__) + + #define RR_BREAK() __asm__("break 0") + #define RR_CACHE_LINE_SIZE 64 + + #elif defined(__RADPSP2__) + + #define RR_BREAK() { __asm__ volatile("bkpt 0x0000"); } + #define RR_CACHE_LINE_SIZE 32 + + #elif defined (__RADQNX__) + #define RR_BREAK() __builtin_trap() + #define RR_CACHE_LINE_SIZE 32 + #elif defined (__RADARM__) && defined (__RADLINUX__) + #define RR_BREAK() __builtin_trap() + #define RR_CACHE_LINE_SIZE 32 + #elif defined(__RADSPU__) + + #define RR_BREAK() __asm volatile ("stopd 0,1,1") + #define RR_CACHE_LINE_SIZE 128 + + #elif defined(__RADPS3__) + + // #ifdef snPause // in LibSN.h + // snPause + // __asm__ volatile ( "tw 31,1,1" ) + + #define RR_BREAK() __asm__ volatile ( "tw 31,1,1" ) + //#define RR_BREAK() __asm__ volatile("trap"); + + #define RR_CACHE_LINE_SIZE 128 + + #elif defined(__RADMAC__) + + #if defined(__GNUG__) || defined(__GNUC__) + #ifdef __RADX86__ + #define RR_BREAK() __asm__ volatile ( "int $3" ) + #else + #define RR_BREAK() __builtin_trap() + #endif + #else + #ifdef __RADMACH__ + void DebugStr(unsigned char const *); + #else + void pascal DebugStr(unsigned char const *); + #endif + #define RR_BREAK() DebugStr("\pRR_BREAK() was called") + #endif + + #define RR_CACHE_LINE_SIZE 64 + + #elif defined(__RADIPHONE__) + #define RR_BREAK() __builtin_trap() + #define RR_CACHE_LINE_SIZE 32 + #elif defined(__RADXENON__) + #define RR_BREAK() __debugbreak() + #define RR_CACHE_LINE_SIZE 128 + #elif defined(__RADANDROID__) + #define RR_BREAK() __builtin_trap() + #define RR_CACHE_LINE_SIZE 32 + #elif defined(__RADPS4__) + #define RR_BREAK() __builtin_trap() + #define RR_CACHE_LINE_SIZE 64 + #elif defined(__RADNACL__) + #define RR_BREAK() __builtin_trap() + #define RR_CACHE_LINE_SIZE 64 + #else + // x86 : + #define RR_CACHE_LINE_SIZE 64 + + #ifdef __RADLINUX__ + #define RR_BREAK() __asm__ volatile ( "int $3" ) + #elif defined(__WATCOMC__) + + void RR_BREAK( void ); + #pragma aux RR_BREAK = "int 0x3"; + + #elif defined(__RADWIN__) && defined(_MSC_VER) && _MSC_VER >= 1300 + + #define RR_BREAK __debugbreak + + #else + + #define RR_BREAK() RAD_STATEMENT_WRAPPER( __asm {int 3} ) + + #endif + + #endif + +// simple RR_ASSERT : + +// CB 5-27-10 : use RR_DO_ASSERTS to toggle asserts on and off : +#if (defined(_DEBUG) && !defined(NDEBUG)) || defined(ASSERT_IN_RELEASE) + #define RR_DO_ASSERTS +#endif + +/********* + +rrAsserts : + +RR_ASSERT(exp) - the normal assert thing, toggled with RR_DO_ASSERTS +RR_ASSERT_ALWAYS(exp) - assert that you want to test even in ALL builds (including final!) +RR_ASSERT_RELEASE(exp) - assert that you want to test even in release builds (not for final!) +RR_ASSERT_LITE(exp) - normal assert is not safe from threads or inside malloc; use this instead +RR_DURING_ASSERT(exp) - wrap operations that compute stuff for assert in here +RR_DO_ASSERTS - toggle tells you if asserts are enabled or not + +RR_BREAK() - generate a debug break - always ! +RR_ASSERT_BREAK() - RR_BREAK for asserts ; disable with RAD_NO_BREAK + +RR_ASSERT_FAILURE(str) - just break with a messsage; like assert with no condition +RR_ASSERT_FAILURE_ALWAYS(str) - RR_ASSERT_FAILURE in release builds too +RR_CANT_GET_HERE() - put in spots execution should never go +RR_COMPILER_ASSERT(exp) - checks constant conditions at compile time + +RADTODO - note to search for nonfinal stuff +RR_PRAGMA_MESSAGE - message dealy, use with #pragma in MSVC + +*************/ + +//----------------------------------------------------------- + + +#if defined(__GNUG__) || defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER > 1200) + #define RR_FUNCTION_NAME __FUNCTION__ +#else + #define RR_FUNCTION_NAME 0 + + // __func__ is in the C99 standard +#endif + +//----------------------------------------------------------- + +// rrDisplayAssertion might just log, or it might pop a message box, depending on settings +// rrDisplayAssertion returns whether you should break or not +typedef rrbool (RADLINK fp_rrDisplayAssertion)(int * Ignored, const char * fileName,const int line,const char * function,const char * message); + +extern fp_rrDisplayAssertion * g_fp_rrDisplayAssertion; + +// if I have func pointer, call it, else true ; true = do int 3 +#define rrDisplayAssertion(i,n,l,f,m) ( ( g_fp_rrDisplayAssertion ) ? (*g_fp_rrDisplayAssertion)(i,n,l,f,m) : 1 ) + +//----------------------------------------------------------- + +// RAD_NO_BREAK : option if you don't like your assert to break +// CB : RR_BREAK is *always* a break ; RR_ASSERT_BREAK is optional +#ifdef RAD_NO_BREAK +#define RR_ASSERT_BREAK() 0 +#else +#define RR_ASSERT_BREAK() RR_BREAK() +#endif + +// assert_always is on FINAL ! +#define RR_ASSERT_ALWAYS(exp) RAD_STATEMENT_WRAPPER( static int Ignored=0; if ( ! (exp) ) { if ( rrDisplayAssertion(&Ignored,__FILE__,__LINE__,RR_FUNCTION_NAME,#exp) ) RR_ASSERT_BREAK(); } ) + +// RR_ASSERT_FAILURE is like an assert without a condition - if you hit it, you're bad +#define RR_ASSERT_FAILURE_ALWAYS(str) RAD_STATEMENT_WRAPPER( static int Ignored=0; if ( rrDisplayAssertion(&Ignored,__FILE__,__LINE__,RR_FUNCTION_NAME,str) ) RR_ASSERT_BREAK(); ) + +#define RR_ASSERT_LITE_ALWAYS(exp) RAD_STATEMENT_WRAPPER( if ( ! (exp) ) { RR_ASSERT_BREAK(); } ) + +//----------------------------------- +#ifdef RR_DO_ASSERTS + +#define RR_ASSERT(exp) RR_ASSERT_ALWAYS(exp) +#define RR_ASSERT_LITE(exp) RR_ASSERT_LITE_ALWAYS(exp) +#define RR_ASSERT_NO_ASSUME(exp) RR_ASSERT_ALWAYS(exp) +// RR_DURING_ASSERT is to set up expressions or declare variables that are only used in asserts +#define RR_DURING_ASSERT(exp) exp + +#define RR_ASSERT_FAILURE(str) RR_ASSERT_FAILURE_ALWAYS(str) + +// RR_CANT_GET_HERE is for like defaults in switches that should never be hit +#define RR_CANT_GET_HERE() RAD_STATEMENT_WRAPPER( RR_ASSERT_FAILURE("can't get here"); RADUNREACHABLE; ) + + +#else // RR_DO_ASSERTS //----------------------------------- + +#define RR_ASSERT(exp) (void)0 +#define RR_ASSERT_LITE(exp) (void)0 +#define RR_ASSERT_NO_ASSUME(exp) (void)0 + +#define RR_DURING_ASSERT(exp) (void)0 + +#define RR_ASSERT_FAILURE(str) (void)0 + +#define RR_CANT_GET_HERE() RADUNREACHABLE + +#endif // RR_DO_ASSERTS //----------------------------------- + +//================================================================= + +// RR_ASSERT_RELEASE is on in release build, but not final + +#ifndef __RADFINAL__ + +#define RR_ASSERT_RELEASE(exp) RR_ASSERT_ALWAYS(exp) +#define RR_ASSERT_LITE_RELEASE(exp) RR_ASSERT_LITE_ALWAYS(exp) + +#else + +#define RR_ASSERT_RELEASE(exp) (void)0 +#define RR_ASSERT_LITE_RELEASE(exp) (void)0 + +#endif + +// BH: This never gets compiled away except for __RADFINAL__ +#define RR_ASSERT_ALWAYS_NO_SHIP RR_ASSERT_RELEASE + +#define rrAssert RR_ASSERT +#define rrassert RR_ASSERT + +#ifdef _MSC_VER + // without this, our assert errors... + #if _MSC_VER >= 1300 + #pragma warning( disable : 4127) // conditional expression is constant + #endif +#endif + +//--------------------------------------- +// Get/Put from memory in little or big endian : +// +// val = RR_GET32_BE(ptr) +// RR_PUT32_BE(ptr,val) +// +// available here : +// RR_[GET/PUT][16/32]_[BE/LE][_UNALIGNED][_OFFSET] +// +// if you don't specify _UNALIGNED , then ptr & offset shoud both be aligned to type size +// _OFFSET is in *bytes* ! + +// you can #define RR_GET_RESTRICT to make all RR_GETs be RESTRICT +// if you set nothing they are not + +#ifdef RR_GET_RESTRICT +#define RR_GET_PTR_POST RADRESTRICT +#endif +#ifndef RR_GET_PTR_POST +#define RR_GET_PTR_POST +#endif + +// native version of get/put is always trivial : + +#define RR_GET16_NATIVE(ptr) *((const U16 * RR_GET_PTR_POST)(ptr)) +#define RR_PUT16_NATIVE(ptr,val) *((U16 * RR_GET_PTR_POST)(ptr)) = (val) + +// offset is in bytes +#define RR_U16_PTR_OFFSET(ptr,offset) ((U16 * RR_GET_PTR_POST)((char *)(ptr) + (offset))) +#define RR_GET16_NATIVE_OFFSET(ptr,offset) *( RR_U16_PTR_OFFSET((ptr),offset) ) +#define RR_PUT16_NATIVE_OFFSET(ptr,val,offset) *( RR_U16_PTR_OFFSET((ptr),offset)) = (val) + +#define RR_GET32_NATIVE(ptr) *((const U32 * RR_GET_PTR_POST)(ptr)) +#define RR_PUT32_NATIVE(ptr,val) *((U32 * RR_GET_PTR_POST)(ptr)) = (val) + +// offset is in bytes +#define RR_U32_PTR_OFFSET(ptr,offset) ((U32 * RR_GET_PTR_POST)((char *)(ptr) + (offset))) +#define RR_GET32_NATIVE_OFFSET(ptr,offset) *( RR_U32_PTR_OFFSET((ptr),offset) ) +#define RR_PUT32_NATIVE_OFFSET(ptr,val,offset) *( RR_U32_PTR_OFFSET((ptr),offset)) = (val) + +#define RR_GET64_NATIVE(ptr) *((const U64 * RR_GET_PTR_POST)(ptr)) +#define RR_PUT64_NATIVE(ptr,val) *((U64 * RR_GET_PTR_POST)(ptr)) = (val) + +// offset is in bytes +#define RR_U64_PTR_OFFSET(ptr,offset) ((U64 * RR_GET_PTR_POST)((char *)(ptr) + (offset))) +#define RR_GET64_NATIVE_OFFSET(ptr,offset) *( RR_U64_PTR_OFFSET((ptr),offset) ) +#define RR_PUT64_NATIVE_OFFSET(ptr,val,offset) *( RR_U64_PTR_OFFSET((ptr),offset)) = (val) + +//--------------------------------------------------- + +#ifdef __RADLITTLEENDIAN__ + +#define RR_GET16_LE RR_GET16_NATIVE +#define RR_PUT16_LE RR_PUT16_NATIVE +#define RR_GET16_LE_OFFSET RR_GET16_NATIVE_OFFSET +#define RR_PUT16_LE_OFFSET RR_PUT16_NATIVE_OFFSET + +#define RR_GET32_LE RR_GET32_NATIVE +#define RR_PUT32_LE RR_PUT32_NATIVE +#define RR_GET32_LE_OFFSET RR_GET32_NATIVE_OFFSET +#define RR_PUT32_LE_OFFSET RR_PUT32_NATIVE_OFFSET + +#define RR_GET64_LE RR_GET64_NATIVE +#define RR_PUT64_LE RR_PUT64_NATIVE +#define RR_GET64_LE_OFFSET RR_GET64_NATIVE_OFFSET +#define RR_PUT64_LE_OFFSET RR_PUT64_NATIVE_OFFSET + +#else + +#define RR_GET16_BE RR_GET16_NATIVE +#define RR_PUT16_BE RR_PUT16_NATIVE +#define RR_GET16_BE_OFFSET RR_GET16_NATIVE_OFFSET +#define RR_PUT16_BE_OFFSET RR_PUT16_NATIVE_OFFSET + +#define RR_GET32_BE RR_GET32_NATIVE +#define RR_PUT32_BE RR_PUT32_NATIVE +#define RR_GET32_BE_OFFSET RR_GET32_NATIVE_OFFSET +#define RR_PUT32_BE_OFFSET RR_PUT32_NATIVE_OFFSET + +#define RR_GET64_BE RR_GET64_NATIVE +#define RR_PUT64_BE RR_PUT64_NATIVE +#define RR_GET64_BE_OFFSET RR_GET64_NATIVE_OFFSET +#define RR_PUT64_BE_OFFSET RR_PUT64_NATIVE_OFFSET + +#endif + +//------------------------- +// non-native Get/Put implementations go here : + +#if defined(__RADX86__) +// good implementation for X86 : + +#if (_MSC_VER >= 1300) + +unsigned short __cdecl _byteswap_ushort (unsigned short _Short); +unsigned long __cdecl _byteswap_ulong (unsigned long _Long); +#pragma intrinsic(_byteswap_ushort, _byteswap_ulong) + +#define RR_BSWAP16 _byteswap_ushort +#define RR_BSWAP32 _byteswap_ulong + +unsigned __int64 __cdecl _byteswap_uint64 (unsigned __int64 val); +#pragma intrinsic(_byteswap_uint64) +#define RR_BSWAP64 _byteswap_uint64 + +#elif defined(_MSC_VER) // VC6 + +RADFORCEINLINE unsigned long RR_BSWAP16 (unsigned long _Long) +{ + __asm { + mov eax, [_Long] + rol ax, 8 + mov [_Long], eax; + } + return _Long; +} + +RADFORCEINLINE unsigned long RR_BSWAP32 (unsigned long _Long) +{ + __asm { + mov eax, [_Long] + bswap eax + mov [_Long], eax + } + return _Long; +} + +RADFORCEINLINE unsigned __int64 RR_BSWAP64 (unsigned __int64 _Long) +{ + __asm { + mov eax, DWORD PTR _Long + mov edx, DWORD PTR _Long+4 + bswap eax + bswap edx + mov DWORD PTR _Long, edx + mov DWORD PTR _Long+4, eax + } + return _Long; +} + +#elif defined(__GNUC__) || defined(__clang__) + +// GCC has __builtin_bswap16, but Clang only seems to have added it recently. +// We use __builtin_bswap32/64 but 16 just uses the macro version. (No big +// deal if that turns into shifts anyway) +#define RR_BSWAP16(u16) ( (U16) ( ((u16) >> 8) | ((u16) << 8) ) ) +#define RR_BSWAP32 __builtin_bswap32 +#define RR_BSWAP64 __builtin_bswap64 + +#endif + +#define RR_GET16_BE(ptr) RR_BSWAP16(*((U16 *)(ptr))) +#define RR_PUT16_BE(ptr,val) *((U16 *)(ptr)) = (U16) RR_BSWAP16(val) +#define RR_GET16_BE_OFFSET(ptr,offset) RR_BSWAP16(*RR_U16_PTR_OFFSET(ptr,offset)) +#define RR_PUT16_BE_OFFSET(ptr,val,offset) *RR_U16_PTR_OFFSET(ptr,offset) = RR_BSWAP16(val) + +#define RR_GET32_BE(ptr) RR_BSWAP32(*((U32 *)(ptr))) +#define RR_PUT32_BE(ptr,val) *((U32 *)(ptr)) = RR_BSWAP32(val) +#define RR_GET32_BE_OFFSET(ptr,offset) RR_BSWAP32(*RR_U32_PTR_OFFSET(ptr,offset)) +#define RR_PUT32_BE_OFFSET(ptr,val,offset) *RR_U32_PTR_OFFSET(ptr,offset) = RR_BSWAP32(val) + +#define RR_GET64_BE(ptr) RR_BSWAP64(*((U64 *)(ptr))) +#define RR_PUT64_BE(ptr,val) *((U64 *)(ptr)) = RR_BSWAP64(val) +#define RR_GET64_BE_OFFSET(ptr,offset) RR_BSWAP64(*RR_U64_PTR_OFFSET(ptr,offset)) +#define RR_PUT64_BE_OFFSET(ptr,val,offset) *RR_U64_PTR_OFFSET(ptr,offset) = RR_BSWAP64(val) + +// end _MSC_VER + +#elif defined(__RADXENON__) // Xenon has built-in funcs for this + +unsigned short __loadshortbytereverse(int offset, const void *base); +unsigned long __loadwordbytereverse (int offset, const void *base); + +void __storeshortbytereverse(unsigned short val, int offset, void *base); +void __storewordbytereverse (unsigned int val, int offset, void *base); + +#define RR_GET16_LE(ptr) __loadshortbytereverse(0, ptr) +#define RR_PUT16_LE(ptr,val) __storeshortbytereverse((U16) (val), 0, ptr) + +#define RR_GET16_LE_OFFSET(ptr,offset) __loadshortbytereverse(offset, ptr) +#define RR_PUT16_LE_OFFSET(ptr,val,offset) __storeshortbytereverse((U16) (val), offset, ptr) + +#define RR_GET32_LE(ptr) __loadwordbytereverse(0, ptr) +#define RR_PUT32_LE(ptr,val) __storewordbytereverse((U32) (val), 0, ptr) + +#define RR_GET32_LE_OFFSET(ptr,offset) __loadwordbytereverse(offset, ptr) +#define RR_PUT32_LE_OFFSET(ptr,val,offset) __storewordbytereverse((U32) (val), offset, ptr) + +#define RR_GET64_LE(ptr) ( ((U64)RR_GET32_OFFSET_LE(ptr,4)<<32) | RR_GET32_LE(ptr) ) +#define RR_PUT64_LE(ptr,val) RR_PUT32_LE(ptr, (U32) (val)), RR_PUT32_OFFSET_LE(ptr, (U32) ((val)>>32),4) + +#elif defined(__RADPS3__) + +#include + +#define RR_GET16_LE(ptr) __lhbrx(ptr) +#define RR_PUT16_LE(ptr,val) __sthbrx(ptr, (U16) (val)) + +#define RR_GET16_LE_OFFSET(ptr,offset) __lhbrx(RR_U16_PTR_OFFSET(ptr, offset)) +#define RR_PUT16_LE_OFFSET(ptr,val,offset) __sthbrx(RR_U16_PTR_OFFSET(ptr, offset), (U16) (val)) + +#define RR_GET32_LE(ptr) __lwbrx(ptr) +#define RR_PUT32_LE(ptr,val) __stwbrx(ptr, (U32) (val)) + +#define RR_GET64_LE(ptr) __ldbrx(ptr) +#define RR_PUT64_LE(ptr,val) __stdbrx(ptr, (U32) (val)) + +#define RR_GET32_LE_OFFSET(ptr,offset) __lwbrx(RR_U32_PTR_OFFSET(ptr, offset)) +#define RR_PUT32_LE_OFFSET(ptr,val,offset) __stwbrx(RR_U32_PTR_OFFSET(ptr, offset), (U32) (val)) + +#elif defined(__RADWII__) + +#define RR_GET16_LE(ptr) __lhbrx(ptr, 0) +#define RR_PUT16_LE(ptr,val) __sthbrx((U16) (val), ptr, 0) + +#define RR_GET16_LE_OFFSET(ptr,offset) __lhbrx(ptr, offset) +#define RR_PUT16_LE_OFFSET(ptr,val,offset) __sthbrx((U16) (val), ptr, offset) + +#define RR_GET32_LE(ptr) __lwbrx(ptr, 0) +#define RR_PUT32_LE(ptr,val) __stwbrx((U32) (val), ptr, 0) + +#define RR_GET32_LE_OFFSET(ptr,offset) __lwbrx(ptr, offset) +#define RR_PUT32_LE_OFFSET(ptr,val,offset) __stwbrx((U32) (val), ptr, offset) + +#elif defined(__RAD3DS__) + +#define RR_GET16_BE(ptr) __rev16(*(U16 *) (ptr)) +#define RR_PUT16_BE(ptr,val) *(U16 *) (ptr) = __rev16(val) + +#define RR_GET16_BE_OFFSET(ptr,offset) __rev16(*RR_U16_PTR_OFFSET(ptr,offset)) +#define RR_PUT16_BE_OFFSET(ptr,offset,val) *RR_U16_PTR_OFFSET(ptr,offset) = __rev16(val) + +#define RR_GET32_BE(ptr) __rev(*(U32 *) (ptr)) +#define RR_PUT32_BE(ptr,val) *(U32 *) (ptr) = __rev(val) + +#define RR_GET32_BE_OFFSET(ptr,offset) __rev(*RR_U32_PTR_OFFSET(ptr,offset)) +#define RR_PUT32_BE_OFFSET(ptr,offset,val) *RR_U32_PTR_OFFSET(ptr,offset) = __rev(val) + +#elif defined(__RADIPHONE__) + +// iPhone does not seem to have intrinsics for this, so use generic fallback! + +// Bswap is just here for use of implementing get/put +// caller should use Get/Put , not bswap +#define RR_BSWAP16(u16) ( (U16) ( ((u16) >> 8) | ((u16) << 8) ) ) +#define RR_BSWAP32(u32) ( (U32) ( ((u32) >> 24) | (((u32)<<8) & 0x00FF0000) | (((u32)>>8) & 0x0000FF00) | ((u32) << 24) ) ) + +#define RR_GET16_BE(ptr) RR_BSWAP16(*((U16 *)(ptr))) +#define RR_PUT16_BE(ptr,val) *((U16 *)(ptr)) = RR_BSWAP16(val) + +#define RR_GET32_BE(ptr) RR_BSWAP32(*((U32 *)(ptr))) +#define RR_PUT32_BE(ptr,val) *((U32 *)(ptr)) = RR_BSWAP32(val) + +#elif defined(__RADWIIU__) + +#include + +#define RR_GET16_LE(ptr) (*(__bytereversed U16 *) (ptr)) +#define RR_PUT16_LE(ptr,val) *(__bytereversed U16 *) (ptr) = val + +#define RR_GET16_LE_OFFSET(ptr,offset) (*(__bytereversed U16 *)RR_U16_PTR_OFFSET(ptr,offset)) +#define RR_PUT16_LE_OFFSET(ptr,val,offset) *(__bytereversed U16 *)RR_U16_PTR_OFFSET(ptr,offset) = val + +#define RR_GET32_LE(ptr) (*(__bytereversed U32 *) (ptr)) +#define RR_PUT32_LE(ptr,val) *(__bytereversed U32 *) (ptr) = val + +#define RR_GET32_LE_OFFSET(ptr,offset) (*(__bytereversed U32 *)RR_U32_PTR_OFFSET(ptr,offset)) +#define RR_PUT32_LE_OFFSET(ptr,val,offset) *(__bytereversed U32 *)RR_U32_PTR_OFFSET(ptr,offset) = val + +#define RR_GET64_LE(ptr) (*(__bytereversed U64 *) (ptr)) +#define RR_PUT64_LE(ptr,val) *(__bytereversed U64 *) (ptr) = val + +#define RR_GET64_LE_OFFSET(ptr,offset) (*(__bytereversed U64 *)RR_U32_PTR_OFFSET(ptr,offset)) +#define RR_PUT64_LE_OFFSET(ptr,val,offset) *(__bytereversed U64 *)RR_U32_PTR_OFFSET(ptr,offset) = val + +#elif defined(__RADWINRTAPI__) && defined(__RADARM__) + +#include + +#define RR_BSWAP16(u16) _arm_rev16(u16) +#define RR_BSWAP32(u32) _arm_rev(u32) + +#define RR_GET16_BE(ptr) RR_BSWAP16(*((U16 *)(ptr))) +#define RR_PUT16_BE(ptr,val) *((U16 *)(ptr)) = RR_BSWAP16(val) + +#define RR_GET32_BE(ptr) RR_BSWAP32(*((U32 *)(ptr))) +#define RR_PUT32_BE(ptr,val) *((U32 *)(ptr)) = RR_BSWAP32(val) + +#elif defined(__RADPSP2__) + +// no rev16 exposed +#define RR_BSWAP16(u16) ( (U16) ( ((u16) >> 8) | ((u16) << 8) ) ) +#define RR_BSWAP32(u32) __builtin_rev(u32) + +#define RR_GET16_BE(ptr) RR_BSWAP16(*((U16 *)(ptr))) +#define RR_PUT16_BE(ptr,val) *((U16 *)(ptr)) = RR_BSWAP16(val) + +#define RR_GET32_BE(ptr) RR_BSWAP32(*((U32 *)(ptr))) +#define RR_PUT32_BE(ptr,val) *((U32 *)(ptr)) = RR_BSWAP32(val) + +#else // other platforms ? + +// fall back : + +// Bswap is just here for use of implementing get/put +// caller should use Get/Put , not bswap +#define RR_BSWAP16(u16) ( (U16) ( ((u16) >> 8) | ((u16) << 8) ) ) +#define RR_BSWAP32(u32) ( (U32) ( ((u32) >> 24) | (((u32)<<8) & 0x00FF0000) | (((u32)>>8) & 0x0000FF00) | ((u32) << 24) ) ) +#define RR_BSWAP64(u64) ( ((U64) RR_BSWAP32((U32) (u64)) << 32) | (U64) RR_BSWAP32((U32) ((u64) >> 32)) ) + +#ifdef __RADLITTLEENDIAN__ + +// comment out fallbacks so users will get errors +//#define RR_GET16_BE(ptr) RR_BSWAP16(*((U16 *)(ptr))) +//#define RR_PUT16_BE(ptr,val) *((U16 *)(ptr)) = RR_BSWAP16(val) +//#define RR_GET32_BE(ptr) RR_BSWAP32(*((U32 *)(ptr))) +//#define RR_PUT32_BE(ptr,val) *((U32 *)(ptr)) = RR_BSWAP32(val) + +#else + +// comment out fallbacks so users will get errors +//#define RR_GET16_LE(ptr) RR_BSWAP16(*((U16 *)(ptr))) +//#define RR_PUT16_LE(ptr,val) *((U16 *)(ptr)) = RR_BSWAP16(val) +//#define RR_GET32_LE(ptr) RR_BSWAP32(*((U32 *)(ptr))) +//#define RR_PUT32_LE(ptr,val) *((U32 *)(ptr)) = RR_BSWAP32(val) + +#endif + +#endif + +//=================================================================== +// @@ TEMP : Aliases for old names : remove me when possible : + +#define RR_GET32_OFFSET_LE RR_GET32_LE_OFFSET +#define RR_GET32_OFFSET_BE RR_GET32_BE_OFFSET +#define RR_PUT32_OFFSET_LE RR_PUT32_LE_OFFSET +#define RR_PUT32_OFFSET_BE RR_PUT32_BE_OFFSET +#define RR_GET16_OFFSET_LE RR_GET16_LE_OFFSET +#define RR_GET16_OFFSET_BE RR_GET16_BE_OFFSET +#define RR_PUT16_OFFSET_LE RR_PUT16_LE_OFFSET +#define RR_PUT16_OFFSET_BE RR_PUT16_BE_OFFSET + + +//=================================================================== +// UNALIGNED VERSIONS : + +#if defined(__RADX86__) || defined(__RADPPC__) // platforms where unaligned is fast : + +#define RR_GET32_BE_UNALIGNED(ptr) RR_GET32_BE(ptr) +#define RR_GET32_BE_UNALIGNED_OFFSET(ptr,offset) RR_GET32_BE_OFFSET(ptr,offset) +#define RR_GET16_BE_UNALIGNED(ptr) RR_GET16_BE(ptr) +#define RR_GET16_BE_UNALIGNED_OFFSET(ptr,offset) RR_GET16_BE_OFFSET(ptr,offset) + +#define RR_GET32_LE_UNALIGNED(ptr) RR_GET32_LE(ptr) +#define RR_GET32_LE_UNALIGNED_OFFSET(ptr,offset) RR_GET32_LE_OFFSET(ptr,offset) +#define RR_GET16_LE_UNALIGNED(ptr) RR_GET16_LE(ptr) +#define RR_GET16_LE_UNALIGNED_OFFSET(ptr,offset) RR_GET16_LE_OFFSET(ptr,offset) + +#elif defined(__RAD3DS__) + +// arm has a "__packed" qualifier to tell the compiler to do unaligned accesses +#define RR_U16_PTR_OFFSET_UNALIGNED(ptr,offset) ((__packed U16 * RR_GET_PTR_POST)((char *)(ptr) + (offset))) +#define RR_U32_PTR_OFFSET_UNALIGNED(ptr,offset) ((__packed U32 * RR_GET_PTR_POST)((char *)(ptr) + (offset))) + +#define RR_GET32_BE_UNALIGNED(ptr) __rev(*RR_U32_PTR_OFFSET_UNALIGNED(ptr,0)) +#define RR_GET32_BE_UNALIGNED_OFFSET(ptr,offset) __rev(*RR_U32_PTR_OFFSET_UNALIGNED(ptr,offset)) +#define RR_GET16_BE_UNALIGNED(ptr) __rev16(*RR_U16_PTR_OFFSET_UNALIGNED(ptr,0)) +#define RR_GET16_BE_UNALIGNED_OFFSET(ptr,offset) __rev16(*RR_U16_PTR_OFFSET_UNALIGNED(ptr,offset)) + +#define RR_GET32_LE_UNALIGNED(ptr) *RR_U32_PTR_OFFSET_UNALIGNED(ptr,0) +#define RR_GET32_LE_UNALIGNED_OFFSET(ptr,offset) *RR_U32_PTR_OFFSET_UNALIGNED(ptr,offset) +#define RR_GET16_LE_UNALIGNED(ptr) *RR_U16_PTR_OFFSET_UNALIGNED(ptr,0) +#define RR_GET16_LE_UNALIGNED_OFFSET(ptr,offset) *RR_U16_PTR_OFFSET_UNALIGNED(ptr,offset) + +#elif defined(__RADPSP2__) + +#define RR_U16_PTR_OFFSET_UNALIGNED(ptr,offset) ((U16 __unaligned * RR_GET_PTR_POST)((char *)(ptr) + (offset))) +#define RR_U32_PTR_OFFSET_UNALIGNED(ptr,offset) ((U32 __unaligned * RR_GET_PTR_POST)((char *)(ptr) + (offset))) + +#define RR_GET32_BE_UNALIGNED(ptr) RR_BSWAP32(*RR_U32_PTR_OFFSET_UNALIGNED(ptr,0)) +#define RR_GET32_BE_UNALIGNED_OFFSET(ptr,offset) RR_BSWAP32(*RR_U32_PTR_OFFSET_UNALIGNED(ptr,offset)) +#define RR_GET16_BE_UNALIGNED(ptr) RR_BSWAP16(*RR_U16_PTR_OFFSET_UNALIGNED(ptr,0)) +#define RR_GET16_BE_UNALIGNED_OFFSET(ptr,offset) RR_BSWAP16(*RR_U16_PTR_OFFSET_UNALIGNED(ptr,offset)) + +#define RR_GET32_LE_UNALIGNED(ptr) *RR_U32_PTR_OFFSET_UNALIGNED(ptr,0) +#define RR_GET32_LE_UNALIGNED_OFFSET(ptr,offset) *RR_U32_PTR_OFFSET_UNALIGNED(ptr,offset) +#define RR_GET16_LE_UNALIGNED(ptr) *RR_U16_PTR_OFFSET_UNALIGNED(ptr,0) +#define RR_GET16_LE_UNALIGNED_OFFSET(ptr,offset) *RR_U16_PTR_OFFSET_UNALIGNED(ptr,offset) + +#else +// Unaligned via bytes : + +#define RR_GET32_BE_UNALIGNED(ptr) ( \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[0] << 24 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[1] << 16 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[2] << 8 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[3] << 0 ) ) + +#define RR_GET32_BE_UNALIGNED_OFFSET(ptr,offset) ( \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[0] << 24 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[1] << 16 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[2] << 8 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[3] << 0 ) ) + +#define RR_GET16_BE_UNALIGNED(ptr) ( \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr)))[0] << 8 ) | \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr)))[1] << 0 ) ) + +#define RR_GET16_BE_UNALIGNED_OFFSET(ptr,offset) ( \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[0] << 8 ) | \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[1] << 0 ) ) + +#define RR_GET32_LE_UNALIGNED(ptr) ( \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[3] << 24 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[2] << 16 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[1] << 8 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr)))[0] << 0 ) ) + +#define RR_GET32_LE_UNALIGNED_OFFSET(ptr,offset) ( \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[3] << 24 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[2] << 16 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[1] << 8 ) | \ + ( (U32)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[0] << 0 ) ) + +#define RR_GET16_LE_UNALIGNED(ptr) ( \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr)))[1] << 8 ) | \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr)))[0] << 0 ) ) + +#define RR_GET16_LE_UNALIGNED_OFFSET(ptr,offset) ( \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[1] << 8 ) | \ + ( (U16)(((const U8 * RR_GET_PTR_POST)(ptr))+(offset))[0] << 0 ) ) + +#endif + +//=================================================================== +// RR_ROTL32 : 32-bit rotate +// + +#ifdef _MSC_VER + + unsigned long __cdecl _lrotl(unsigned long, int); + #pragma intrinsic(_lrotl) + + #define RR_ROTL32(x,k) _lrotl((unsigned long)(x),(int)(k)) + +#elif defined(__RADCELL__) || defined(__RADLINUX__) || defined(__RADWII__) || defined(__RADMACAPI__) || defined(__RADWIIU__) || defined(__RADPS4__) || defined(__RADPSP2__) + + // Compiler turns this into rotate correctly : + #define RR_ROTL32(u32,num) ( ( (u32) << (num) ) | ( (u32) >> (32 - (num))) ) + +#elif defined(__RAD3DS__) + + #define RR_ROTL32(u32,num) __ror(u32, (-(num))&31) + +#else + +// comment out fallbacks so users will get errors +// fallback implementation using shift and or : +//#define RR_ROTL32(u32,num) ( ( (u32) << (num) ) | ( (u32) >> (32 - (num))) ) + +#endif + + +//=================================================================== +// RR_ROTL64 : 64-bit rotate + +#if ( defined(_MSC_VER) && _MSC_VER >= 1300) + +unsigned __int64 __cdecl _rotl64(unsigned __int64 _Val, int _Shift); +#pragma intrinsic(_rotl64) + +#define RR_ROTL64(x,k) _rotl64((unsigned __int64)(x),(int)(k)) + +#elif defined(__RADCELL__) + +// PS3 GCC turns this into rotate correctly : +#define RR_ROTL64(u64,num) ( ( (u64) << (num) ) | ( (u64) >> (64 - (num))) ) + +#elif defined(__RADLINUX__) || defined(__RADMACAPI__) + +//APTODO: Just to compile linux. Should we be doing better than this? If not, combine with above. +#define RR_ROTL64(u64,num) ( ( (u64) << (num) ) | ( (u64) >> (64 - (num))) ) + +#else + +// comment out fallbacks so users will get errors +// fallback implementation using shift and or : +//#define RR_ROTL64(u64,num) ( ( (u64) << (num) ) | ( (u64) >> (64 - (num))) ) + +#endif + +//=================================================================== + +RADDEFEND + +//=================================================================== + +// RR_COMPILER_ASSERT +#if defined(__cplusplus) && !defined(RR_COMPILER_ASSERT) + #if defined(_MSC_VER) && (_MSC_VER >=1400) + + // better version of COMPILER_ASSERT using boost technique + template struct RR_COMPILER_ASSERT_FAILURE; + + template <> struct RR_COMPILER_ASSERT_FAILURE<1> { enum { value = 1 }; }; + + template struct rr_compiler_assert_test{}; + + // __LINE__ macro broken when -ZI is used see Q199057 + #define RR_COMPILER_ASSERT( B ) \ + typedef rr_compiler_assert_test<\ + sizeof(RR_COMPILER_ASSERT_FAILURE< (B) ? 1 : 0 >)\ + > rr_compiler_assert_typedef_ + + #endif +#endif + +#ifndef RR_COMPILER_ASSERT + // this happens at declaration time, so if it's inside a function in a C file, drop {} around it + #define RR_COMPILER_ASSERT(exp) typedef char RR_STRING_JOIN(_dummy_array, __LINE__) [ (exp) ? 1 : -1 ] +#endif + +//=================================================================== +// some error checks : + + RR_COMPILER_ASSERT( sizeof(RAD_UINTa) == sizeof( RR_STRING_JOIN(RAD_U,RAD_PTRBITS) ) ); + RR_COMPILER_ASSERT( sizeof(RAD_UINTa) == RAD_PTRBYTES ); + RR_COMPILER_ASSERT( RAD_TWOPTRBYTES == 2* RAD_PTRBYTES ); + +//=================================================================== + + #endif // __RADRES__ + +//include "testconstant.inl" // uncomment and include to test statement constants + +#endif // __RADRR_COREH__ + + diff --git a/code/video/video.c b/code/video/video.c new file mode 100644 index 0000000..5c2d88a --- /dev/null +++ b/code/video/video.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#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); +} diff --git a/code/win32/kernel32.c b/code/win32/kernel32.c index 0a56911..10b4a51 100644 --- a/code/win32/kernel32.c +++ b/code/win32/kernel32.c @@ -1,131 +1,1032 @@ -// kernel32.c - Win32 personality: process, thread, memory, sync -// Thin wrappers over syscall.dll stubs. Provides a familiar API for -// user-mode code that was originally written for Windows. +// kernel32.c - Win32 personality layer +// +// Thin wrappers around syscall.dll stubs that expose a familiar Win32 API +// surface to user-mode code originally written for Windows. +// +// Design notes: +// - HANDLE is void*; we store the uint32_t kernel handle as a pointer value +// so Win32 code can pass it around opaquely without knowing its size. +// HANDLE_TO_KH / KH_TO_HANDLE do that conversion in one place. +// - INVALID_HANDLE_VALUE maps to kernel handle 0 (the always-invalid slot). +// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and +// always use the single kernel user-heap. This is fine: we don't expose +// multiple heaps, and NULL is a legal (no-op) heap argument under Win32. +// - GetLastError / SetLastError are per-thread on Windows. We keep a single +// global for now; enough for Bink and similar single-threaded guests. +// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE / +// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate +// SysCloseFile). +// - WriteFile / CreateFileA in write-mode / SetEndOfFile are stubs because +// the VFS has no write path yet. + #include #include #include +#include -// Forward declarations of syscall stubs (from syscall.dll). -extern uint64_t SysAlloc(size_t size); +// --------------------------------------------------------------------------- +// Syscall stubs (from syscall.dll, assembled from syscall_stubs.asm) +// --------------------------------------------------------------------------- +extern void* SysAlloc(size_t size); extern void SysFree(void* ptr, size_t size); -extern void SysYield(void); -extern void SysSleep(uint64_t ms); -extern void SysExitProcess(int code); -extern void SysExitThread(int code); -extern uint64_t SysCreateThread(uintptr_t entry, uintptr_t arg, uint32_t priority); +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 SysSeekFile(uint32_t handle, uint64_t offset); +extern uint64_t SysGetFileSize(uint32_t handle); extern void SysCloseHandle(uint32_t handle); -extern void SysDbgPrint(const char* s); -extern uint64_t SysMutexCreate(void); -extern void SysMutexAcquire(uint32_t handle); -extern void SysMutexRelease(uint32_t handle); -extern uint64_t SysEventCreate(uint32_t manualReset, uint32_t initial); -extern void SysEventSet(uint32_t handle); -extern void SysEventWait(uint32_t handle); + +extern void SysExitProcess(int exitCode); +extern void SysExitThread(int exitCode); +extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg, + uint32_t priority, uint32_t* tidOut); +extern uint32_t SysGetCurrentTid(void); +extern uint32_t SysGetCurrentPid(void); +extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut); +extern uint32_t SysWaitForProcess(uint32_t handle); +extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut); +extern char SysKbReadChar(void); +extern uint32_t SysKbPoll(void); +extern void SysYield(void); +extern void SysSleep(uint32_t ms); + extern uint64_t SysGetTick(void); - -#define INVALID_HANDLE_VALUE ((void *)(intptr_t)-1) -#define WAIT_OBJECT_0 0 +extern void SysDebugPrint(const char* str); // --------------------------------------------------------------------------- -// Heap +// Types // --------------------------------------------------------------------------- -void* HeapAlloc(void* heap, unsigned long flags, size_t size) { +typedef void* HANDLE; +typedef unsigned long DWORD; +typedef int BOOL; + +#define TRUE 1 +#define FALSE 0 + +#define INVALID_HANDLE_VALUE ((HANDLE)(uintptr_t)0) + +// CreateFileA disposition / access flags (subset used by Bink and similar) +#define GENERIC_READ 0x80000000UL +#define GENERIC_WRITE 0x40000000UL +#define OPEN_EXISTING 3 +#define CREATE_ALWAYS 2 +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 + +// HeapAlloc flags +#define HEAP_ZERO_MEMORY 0x00000008UL + +// --------------------------------------------------------------------------- +// Handle conversion helpers +// uint32_t kernel handle <-> opaque Win32 HANDLE +// Kernel handle 0 == invalid; we map that to INVALID_HANDLE_VALUE (NULL). +// --------------------------------------------------------------------------- +static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; } +static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; } + +// --------------------------------------------------------------------------- +// GetLastError / SetLastError (single-threaded stub) +// --------------------------------------------------------------------------- +static DWORD gLastError = 0; + +__declspec(dllexport) DWORD GetLastError(void) { + return gLastError; +} +__declspec(dllexport) void SetLastError(DWORD err) { + gLastError = err; +} + +// --------------------------------------------------------------------------- +// Process termination +// --------------------------------------------------------------------------- +__declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) { + SysExitProcess((int)code); + for (;;) ; // unreachable +} + +// --------------------------------------------------------------------------- +// Debug output +// --------------------------------------------------------------------------- +__declspec(dllexport) void OutputDebugStringA(const char* str) { + if (str) SysDebugPrint(str); +} + +// --------------------------------------------------------------------------- +// Timer +// --------------------------------------------------------------------------- +__declspec(dllexport) DWORD GetTickCount(void) { + return (DWORD)SysGetTick(); +} + +// --------------------------------------------------------------------------- +// Memory (heap argument is ignored - single kernel user-heap) +// --------------------------------------------------------------------------- +__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) { + (void)heap; + if (!size) return NULL; + void* ptr = SysAlloc(size); + if (ptr && (flags & HEAP_ZERO_MEMORY)) + memset(ptr, 0, size); + if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY + return ptr; +} + +__declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) { (void)heap; (void)flags; - return (void *)SysAlloc(size); + // Win32's HeapFree doesn't need the size; we pass 0 and the kernel's + // allocator handles it (the block header carries the size). + if (ptr) SysFree(ptr, 0); + return TRUE; } -void* HeapReAlloc(void* heap, unsigned long flags, void* mem, size_t size) { + +__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags, + void* ptr, size_t size) { (void)heap; (void)flags; - // No in-place resize; alloc + copy + free. - void* n = (void *)SysAlloc(size); - if (!n) return (void *)0; - // We don't track sizes, so copy a conservative max. - // In practice callers should use a custom heap. - SysFree(mem, 0); - return n; + void* newptr = SysRealloc(ptr, size); + if (!newptr && size) gLastError = 8; + return newptr; } -int HeapFree(void* heap, unsigned long flags, void* mem) { - (void)heap; (void)flags; - SysFree(mem, 0); - return 1; + +// GetProcessHeap - return a sentinel non-NULL value so callers can pass it +// back into HeapAlloc/HeapFree without crashing (we ignore it anyway). +__declspec(dllexport) void* GetProcessHeap(void) { + return (void*)1; } // --------------------------------------------------------------------------- -// Process / Thread +// Locale / code-page stubs // --------------------------------------------------------------------------- -void ExitProcess(unsigned int code) { SysExitProcess((int)code); } -void ExitThread(unsigned long code) { SysExitThread((int)code); } - -typedef unsigned long (*LPTHREAD_START_ROUTINE)(void *); - -void* CreateThread(void* attr, size_t stackSize, - LPTHREAD_START_ROUTINE fn, void* arg, - unsigned long flags, unsigned long* tid) { - (void)attr; (void)stackSize; (void)flags; (void)tid; - uint64_t h = SysCreateThread((uintptr_t)fn, (uintptr_t)arg, 1 /* Normal */); - if (!h) return INVALID_HANDLE_VALUE; - return (void *)(uintptr_t)h; +__declspec(dllexport) int MultiByteToWideChar(unsigned int cp, DWORD flags, + const char* src, int srcLen, + unsigned short* dst, int dstLen) { + (void)cp; (void)flags; + // Trivial ASCII -> UTF-16LE expansion, good enough for English strings. + if (!src || !dstLen) return 0; + if (srcLen < 0) srcLen = (int)strlen(src) + 1; + int out = 0; + while (out < dstLen && out < srcLen) { + if (dst) dst[out] = (unsigned short)(unsigned char)src[out]; + out++; + } + return out; } -void Sleep(unsigned long ms) { SysSleep((uint64_t)ms); } -void SwitchToThread(void) { SysYield(); } - -// --------------------------------------------------------------------------- -// Synchronisation -// --------------------------------------------------------------------------- -void* CreateMutex(void* attr, int initialOwner, const char* name) { - (void)attr; (void)initialOwner; (void)name; - uint64_t h = SysMutexCreate(); - if (!h) return INVALID_HANDLE_VALUE; - return (void *)(uintptr_t)h; +__declspec(dllexport) int WideCharToMultiByte(unsigned int cp, DWORD flags, + const unsigned short* src, int srcLen, + char* dst, int dstLen, + const char* defChar, int* usedDefault) { + (void)cp; (void)flags; (void)defChar; + if (usedDefault) *usedDefault = FALSE; + if (!src || !dstLen) return 0; + if (srcLen < 0) { + srcLen = 0; + while (src[srcLen]) srcLen++; + srcLen++; + } + int out = 0; + while (out < dstLen && out < srcLen) { + if (dst) dst[out] = (char)(src[out] < 128 ? src[out] : '?'); + out++; + } + return out; } -unsigned long WaitForSingleObject(void* handle, unsigned long ms) { - (void)ms; // Timeout not supported - always blocking for now. - uint32_t h = (uint32_t)(uintptr_t)handle; - // We need to know if this is a mutex, event, etc. - // For simplicity, try event wait; syscall layer validates the type. - SysEventWait(h); +__declspec(dllexport) DWORD GetACP(void) { return 1252; } // Windows-1252 +__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. +__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; + } + + 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; + } + return KH_TO_HANDLE(kh); +} + +// 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 + 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; + if (!hFile || !buf) { gLastError = 6; return FALSE; } + 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 + return TRUE; +} + +// SetFilePointer - seek to an absolute or 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; } + + uint64_t offset = 0; + 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; + } else { + gLastError = 87; // ERROR_INVALID_PARAMETER + return 0xFFFFFFFFUL; + } + + uint32_t s = SysSeekFile(HANDLE_TO_KH(hFile), offset); + if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; } + if (distHigh) *distHigh = (long)(offset >> 32); + return (DWORD)(offset & 0xFFFFFFFFULL); +} + +// CloseHandle - unified close for files, mutexes, semaphores, events, queues. +__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) { + if (!hObject) { gLastError = 6; return FALSE; } + SysCloseHandle(HANDLE_TO_KH(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 +// unless a matching syscall stub is available. +// =========================================================================== + +// --------------------------------------------------------------------------- +// Additional Heap APIs +// We have one kernel heap; the HANDLE is always ignored. +// --------------------------------------------------------------------------- + +typedef int HEAP_INFORMATION_CLASS; // opaque + +__declspec(dllexport) HANDLE HeapCreate(DWORD flOptions, + size_t dwInitialSize, + size_t dwMaximumSize) { + (void)flOptions; (void)dwInitialSize; (void)dwMaximumSize; + return (HANDLE)(uintptr_t)1; // non-NULL sentinel +} + +__declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) { + (void)hHeap; + return TRUE; +} + +__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) { + (void)heap; (void)flags; (void)ptr; + return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported +} + +__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle, + HEAP_INFORMATION_CLASS cls, + void* info, size_t infoLen) { + (void)HeapHandle; (void)cls; (void)info; (void)infoLen; + return TRUE; +} + +// --------------------------------------------------------------------------- +// Process & Module pseudo-handles / queries +// --------------------------------------------------------------------------- + +// Pseudo-handles matching Windows convention: -1 = process, -2 = thread. +__declspec(dllexport) HANDLE GetCurrentProcess(void) { + return (HANDLE)(uintptr_t)(~(uintptr_t)0); +} + +__declspec(dllexport) HANDLE GetCurrentThread(void) { + return (HANDLE)(uintptr_t)(~(uintptr_t)1); +} + +__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); } +__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); } + +// GetModuleHandleA/W – no PE map; return NULL so callers that check can fail +// gracefully. GetModuleHandleA(NULL) on Windows returns the exe base; we +// return a recognisable non-NULL sentinel so code that just passes it to +// GetProcAddress doesn't immediately crash. +__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) { + return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL; +} + +__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) { + return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL; +} + +// GetProcAddress – no dynamic linker; always fail. +__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) { + (void)hModule; (void)lpProcName; + gLastError = 127; // ERROR_PROC_NOT_FOUND + return NULL; +} + +// LoadLibraryA – no dynamic loader; always fail. +__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) { + (void)lpLibFileName; + gLastError = 2; // ERROR_FILE_NOT_FOUND + return NULL; +} + +__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) { + (void)hLibModule; + return TRUE; +} + +__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule, + char* lpFilename, DWORD nSize) { + (void)hModule; + if (lpFilename && nSize) lpFilename[0] = '\0'; + return 0; +} + +__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) { + (void)hProcess; + SysExitProcess((int)uExitCode); + for (;;) ; +} + +// STARTUPINFOA – only cb matters for our stub. +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; + +__declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) { + if (!lpStartupInfo) return; + // Zero every field, then set the mandatory cb. + unsigned char* p = (unsigned char*)lpStartupInfo; + for (size_t i = 0; i < sizeof(*lpStartupInfo); i++) p[i] = 0; + lpStartupInfo->cb = (DWORD)sizeof(*lpStartupInfo); +} + +__declspec(dllexport) char* GetCommandLineA(void) { + // Return a valid but empty-ish command line. + static char cmdline[4] = ""; + return cmdline; +} + +// --------------------------------------------------------------------------- +// Threading +// --------------------------------------------------------------------------- + +#define WAIT_OBJECT_0 0x00000000UL +#define WAIT_TIMEOUT 0x00000102UL +#define INFINITE 0xFFFFFFFFUL + +__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes, + size_t dwStackSize, + void* lpStartAddress, + void* lpParameter, + DWORD dwCreationFlags, + DWORD* lpThreadId) { + (void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags; + uint32_t tid = 0; + uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress, + (uint64_t)(uintptr_t)lpParameter, + 0 /* normal priority */, &tid); + if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; } + if (lpThreadId) *lpThreadId = (DWORD)tid; + return KH_TO_HANDLE(tid); +} + +// WaitForSingleObject – blocks on process handles via SysWaitForProcess; +// thread and other handle types are not yet waitable so return immediately. +__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { + (void)dwMilliseconds; + uint32_t kh = HANDLE_TO_KH(hHandle); + if (kh) SysWaitForProcess(kh); // no-op KSTATUS if not a process handle return WAIT_OBJECT_0; } -int ReleaseMutex(void* handle) { - extern void SysMutexRelease(uint32_t); - SysMutexRelease((uint32_t)(uintptr_t)handle); - return 1; +// --------------------------------------------------------------------------- +// Process creation +// --------------------------------------------------------------------------- + +typedef struct { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; +} PROCESS_INFORMATION; + +#define STILL_ACTIVE 259UL + +__declspec(dllexport) BOOL CreateProcessA( + const char* lpApplicationName, + char* lpCommandLine, + void* lpProcessAttributes, + void* lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + void* lpEnvironment, + const char* lpCurrentDirectory, + STARTUPINFOA* lpStartupInfo, + PROCESS_INFORMATION* lpProcessInformation) { + (void)lpCommandLine; (void)lpProcessAttributes; (void)lpThreadAttributes; + (void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment; + (void)lpCurrentDirectory; (void)lpStartupInfo; + + if (!lpApplicationName || !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 + return FALSE; + } + + lpProcessInformation->hProcess = KH_TO_HANDLE(kh); + lpProcessInformation->hThread = INVALID_HANDLE_VALUE; + lpProcessInformation->dwProcessId = 0; + lpProcessInformation->dwThreadId = 0; + return TRUE; } -void* CreateEvent(void* attr, int manualReset, int initialState, const char* name) { - (void)attr; (void)name; - uint64_t h = SysEventCreate((uint32_t)manualReset, (uint32_t)initialState); - if (!h) return INVALID_HANDLE_VALUE; - return (void *)(uintptr_t)h; +__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) { + if (!lpExitCode) { gLastError = 87; return FALSE; } + uint32_t kh = HANDLE_TO_KH(hProcess); + if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE + int code = 0; + uint32_t s = SysGetProcessExitCode(kh, &code); + if (s != 0) { gLastError = 6; return FALSE; } + *lpExitCode = (DWORD)code; + return TRUE; } -int SetEvent(void* handle) { - SysEventSet((uint32_t)(uintptr_t)handle); - return 1; +__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) { + (void)hThread; + return 0; // previous suspend count } -int CloseHandle(void* handle) { - SysCloseHandle((uint32_t)(uintptr_t)handle); - return 1; +#define THREAD_PRIORITY_NORMAL 0 + +__declspec(dllexport) BOOL SetThreadPriority(HANDLE hThread, int nPriority) { + (void)hThread; (void)nPriority; + return TRUE; } -void OutputDebugStringA(const char* s) { - SysDbgPrint(s); +__declspec(dllexport) int GetThreadPriority(HANDLE hThread) { + (void)hThread; + return THREAD_PRIORITY_NORMAL; +} + +__declspec(dllexport) uintptr_t SetThreadAffinityMask(HANDLE hThread, + uintptr_t dwThreadAffinityMask) { + (void)hThread; (void)dwThreadAffinityMask; + return 1; // previous mask: CPU 0 only +} + +__declspec(dllexport) void Sleep(DWORD dwMilliseconds) { + SysSleep(dwMilliseconds); } // --------------------------------------------------------------------------- -// Time +// Console I/O // --------------------------------------------------------------------------- -unsigned long long GetTickCount64(void) { return SysGetTick(); } -unsigned long GetTickCount(void) { return (unsigned long)SysGetTick(); } + +#define STD_INPUT_HANDLE ((DWORD)-10) +#define STD_OUTPUT_HANDLE ((DWORD)-11) +#define STD_ERROR_HANDLE ((DWORD)-12) + +// We have no real console handles; return a sentinel the other APIs recognise. +__declspec(dllexport) HANDLE GetStdHandle(DWORD nStdHandle) { + return (HANDLE)(uintptr_t)nStdHandle; +} + +// ReadConsoleA: blocking read of up to nNumberOfCharsToRead characters. +// Each character is read individually via SysKbReadChar so the caller can +// do its own line-editing (echo, backspace, etc.) one char at a time. +__declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput, + void* lpBuffer, + DWORD nNumberOfCharsToRead, + DWORD* lpNumberOfCharsRead, + void* pInputControl) { + (void)hConsoleInput; (void)pInputControl; + if (!lpBuffer || nNumberOfCharsToRead == 0) return FALSE; + + char* buf = (char*)lpBuffer; + DWORD i = 0; // Notice we switched to a while loop + + while (i < nNumberOfCharsToRead) { + char c = SysKbReadChar(); + + // Ignore unmapped keys (Shift, Ctrl, etc.) + if (c == 0) continue; + + // Deliver the raw character; the caller handles echo and line editing. + buf[i] = c; + i++; + + // Stop reading when the user presses Enter + if (c == '\n' || c == '\r') { + // If your shell expects strings to be \n terminated, leave this. + // If your shell expects \0 termination without a newline, + // you might want to replace buf[i-1] with \0 here! + break; + } + } + + if (lpNumberOfCharsRead) *lpNumberOfCharsRead = i; + return TRUE; +} // --------------------------------------------------------------------------- -// String helpers (no CRT dependency) -// --------------------------------------------------------------------------- -int lstrlenA(const char* s) { - int n = 0; - while (*s++) n++; - return n; + +__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner, + const char* lpName) { + (void)lpMutexAttributes; (void)bInitialOwner; (void)lpName; + return (HANDLE)(uintptr_t)0x4D555458UL; // 'MUTX' sentinel } + +__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) { + (void)hMutex; + return TRUE; +} + +__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes, + long lInitialCount, long lMaximumCount, + const char* lpName) { + (void)lpSemaphoreAttributes; (void)lInitialCount; + (void)lMaximumCount; (void)lpName; + return (HANDLE)(uintptr_t)0x53454D41UL; // 'SEMA' sentinel +} + +__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount, + long* lpPreviousCount) { + (void)hSemaphore; (void)lReleaseCount; + if (lpPreviousCount) *lpPreviousCount = 0; + return TRUE; +} + +// --------------------------------------------------------------------------- +// Critical Sections & Condition Variables +// Single-threaded: all locks are no-ops. +// --------------------------------------------------------------------------- + +// Minimal opaque types; callers allocate these from their Windows headers – +// we just need the pointer to be accepted by our stubs. +typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION; +typedef struct { void* _ptr; } CONDITION_VARIABLE; + +__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) { + (void)lpCS; +} +__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS, + DWORD dwSpinCount) { + (void)lpCS; (void)dwSpinCount; + return TRUE; +} +__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; } +__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; } +__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; } +__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) { + (void)lpCS; return TRUE; +} + +__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; } + +__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv, + CRITICAL_SECTION* cs, + DWORD dwMilliseconds) { + (void)cv; (void)cs; (void)dwMilliseconds; + return TRUE; +} +__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; } +__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; } + +// --------------------------------------------------------------------------- +// Timing & Performance Counters +// We report a 1 MHz counter so QPC / QPF is consistent with GetTickCount. +// --------------------------------------------------------------------------- + +typedef union { + struct { DWORD LowPart; long HighPart; } u; + long long QuadPart; +} LARGE_INTEGER; + +#define QPC_FREQUENCY 1000000LL // 1 MHz + +__declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) { + if (lpFrequency) lpFrequency->QuadPart = QPC_FREQUENCY; + return TRUE; +} + +__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) { + // SysGetTick returns milliseconds; scale to microseconds for 1 MHz QPC. + if (lpPerformanceCount) + lpPerformanceCount->QuadPart = (long long)SysGetTick() * 1000LL; + return TRUE; +} + +__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) { + // Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC. + // FILETIME = 100-nanosecond intervals since 1601-01-01. + // 2024-01-01 offset from 1601 in 100ns units: 133186080000000000 + if (!lpSystemTimeAsFileTime) return; + unsigned long long ft = 133186080000000000ULL + + (unsigned long long)SysGetTick() * 10000ULL; + unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime; + for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8)); +} + +// --------------------------------------------------------------------------- +// System Information +// --------------------------------------------------------------------------- + +typedef struct { + union { + DWORD dwOemId; + struct { unsigned short wProcessorArchitecture; + unsigned short wReserved; }; + }; + DWORD dwPageSize; + void* lpMinimumApplicationAddress; + void* lpMaximumApplicationAddress; + uintptr_t dwActiveProcessorMask; + DWORD dwNumberOfProcessors; + DWORD dwProcessorType; + DWORD dwAllocationGranularity; + unsigned short wProcessorLevel; + unsigned short wProcessorRevision; +} SYSTEM_INFO; + +__declspec(dllexport) void GetSystemInfo(SYSTEM_INFO* lpSystemInfo) { + if (!lpSystemInfo) return; + unsigned char* p = (unsigned char*)lpSystemInfo; + for (size_t i = 0; i < sizeof(*lpSystemInfo); i++) p[i] = 0; + lpSystemInfo->wProcessorArchitecture = 9; // PROCESSOR_ARCHITECTURE_AMD64 + lpSystemInfo->dwPageSize = 4096; + lpSystemInfo->lpMinimumApplicationAddress = (void*)0x10000; + lpSystemInfo->lpMaximumApplicationAddress = (void*)0x7FFFFFFEFFFFull; + lpSystemInfo->dwActiveProcessorMask = 1; + lpSystemInfo->dwNumberOfProcessors = 1; + lpSystemInfo->dwProcessorType = 8664; // PROCESSOR_AMD_X8664 + lpSystemInfo->dwAllocationGranularity= 65536; + lpSystemInfo->wProcessorLevel = 6; + lpSystemInfo->wProcessorRevision = 0; +} + +typedef struct { + DWORD dwLength; + DWORD dwMemoryLoad; + unsigned long long ullTotalPhys; + unsigned long long ullAvailPhys; + unsigned long long ullTotalPageFile; + unsigned long long ullAvailPageFile; + unsigned long long ullTotalVirtual; + unsigned long long ullAvailVirtual; + unsigned long long ullAvailExtendedVirtual; +} MEMORYSTATUSEX; + +__declspec(dllexport) BOOL GetLogicalProcessorInformation(void* Buffer, + DWORD* ReturnedLength) { + (void)Buffer; + // Signal that the buffer is too small so callers know to handle failure. + if (ReturnedLength) *ReturnedLength = 0; + gLastError = 122; // ERROR_INSUFFICIENT_BUFFER + return FALSE; +} + +#define MAX_PATH 260 + +__declspec(dllexport) unsigned int GetSystemDirectoryA(char* lpBuffer, unsigned int uSize) { + static const char sysdir[] = "C:\\Windows\\System32"; + unsigned int needed = sizeof(sysdir); // includes NUL + if (lpBuffer && uSize >= needed) { + for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = sysdir[i]; + } + return needed - 1; // return length without NUL, matching Win32 +} + +__declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned int uSize) { + static const char windir[] = "C:\\Windows"; + unsigned int needed = sizeof(windir); + if (lpBuffer && uSize >= needed) { + for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = windir[i]; + } + return needed - 1; +} + +__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer, + DWORD nSize) { + (void)lpName; (void)lpBuffer; (void)nSize; + gLastError = 203; // ERROR_ENVVAR_NOT_FOUND + return 0; +} + +__declspec(dllexport) char* GetEnvironmentStrings(void) { + // Return a double-NUL-terminated empty environment block. + static char emptyEnv[2] = { '\0', '\0' }; + return emptyEnv; +} + +__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) { + (void)penv; return TRUE; +} + +__declspec(dllexport) unsigned short* GetEnvironmentStringsW(void) { + static unsigned short emptyEnvW[2] = { 0, 0 }; + return emptyEnvW; +} + +__declspec(dllexport) BOOL FreeEnvironmentStringsW(unsigned short* penv) { + (void)penv; return TRUE; +} + +// --------------------------------------------------------------------------- +// Handle / Error helpers +// --------------------------------------------------------------------------- + +__declspec(dllexport) unsigned int SetErrorMode(unsigned int uMode) { + (void)uMode; + return 0; // previous mode +} + +__declspec(dllexport) unsigned int SetHandleCount(unsigned int uNumber) { + return uNumber < 256 ? 256 : uNumber; +} + +// --------------------------------------------------------------------------- +// File helpers +// --------------------------------------------------------------------------- + +#define FILE_TYPE_UNKNOWN 0x0000 +#define FILE_TYPE_DISK 0x0001 +#define FILE_TYPE_CHAR 0x0002 +#define FILE_TYPE_PIPE 0x0003 + +#define STD_INPUT_HANDLE ((DWORD)-10) +#define STD_OUTPUT_HANDLE ((DWORD)-11) +#define STD_ERROR_HANDLE ((DWORD)-12) + +__declspec(dllexport) DWORD GetFileType(HANDLE hFile) { + if (!hFile || hFile == INVALID_HANDLE_VALUE) return FILE_TYPE_UNKNOWN; + return FILE_TYPE_DISK; +} + +// --------------------------------------------------------------------------- +// Exception Handling & Debugging +// --------------------------------------------------------------------------- + +#define EXCEPTION_CONTINUE_SEARCH 0 +#define EXCEPTION_EXECUTE_HANDLER 1 + +__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) { + (void)First; (void)Handler; + // Return a non-NULL sentinel so callers can pass it to RemoveVectoredExceptionHandler. + return (void*)(uintptr_t)0x56455843UL; // 'VEXC' +} + +__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) { + (void)Handle; + return 1; // success +} + +__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, + DWORD nNumberOfArguments, + const uintptr_t* lpArguments) { + (void)dwExceptionCode; (void)dwExceptionFlags; + (void)nNumberOfArguments; (void)lpArguments; + // Best-effort: nothing to unwind; just halt. + for (;;) ; +} + +typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS; + +__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) { + (void)ExceptionInfo; + return EXCEPTION_CONTINUE_SEARCH; +} + +typedef long (*LPTOP_LEVEL_EXCEPTION_FILTER)(EXCEPTION_POINTERS*); + +__declspec(dllexport) LPTOP_LEVEL_EXCEPTION_FILTER +SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) { + (void)lpFilter; + return NULL; // previous filter: none +} + +__declspec(dllexport) BOOL IsDebuggerPresent(void) { + return FALSE; +} + +// --------------------------------------------------------------------------- +// RTL Exception / Unwind (SEH infrastructure – all stubs) +// --------------------------------------------------------------------------- + +typedef struct _RUNTIME_FUNCTION { + unsigned int BeginAddress; + unsigned int EndAddress; + unsigned int UnwindInfoAddress; +} RUNTIME_FUNCTION; + +// We have no runtime function table, so RtlLookupFunctionEntry always fails. +__declspec(dllexport) RUNTIME_FUNCTION* +RtlLookupFunctionEntry(unsigned long long ControlPc, + unsigned long long* ImageBase, + void* HistoryTable) { + (void)ControlPc; (void)HistoryTable; + if (ImageBase) *ImageBase = 0; + return NULL; +} + +__declspec(dllexport) void* RtlVirtualUnwind(unsigned long HandlerType, + unsigned long long ImageBase, + unsigned long long ControlPc, + RUNTIME_FUNCTION* FunctionEntry, + void* ContextRecord, + void** HandlerData, + unsigned long long* EstablisherFrame, + void* ContextPointers) { + (void)HandlerType; (void)ImageBase; (void)ControlPc; (void)FunctionEntry; + (void)ContextRecord; (void)ContextPointers; + if (HandlerData) *HandlerData = NULL; + if (EstablisherFrame)*EstablisherFrame = 0; + return NULL; +} + +__declspec(dllexport) void RtlCaptureContext(void* ContextRecord) { + // Zero out the context record so callers don't read garbage. + if (!ContextRecord) return; + // CONTEXT is ~1232 bytes on x64; zero a conservative upper bound. + unsigned char* p = (unsigned char*)ContextRecord; + for (int i = 0; i < 1232; i++) p[i] = 0; +} + +__declspec(dllexport) void RtlUnwindEx(void* TargetFrame, void* TargetIp, + void* ExceptionRecord, void* ReturnValue, + void* ContextRecord, void* HistoryTable) { + (void)TargetFrame; (void)TargetIp; (void)ExceptionRecord; + (void)ReturnValue; (void)ContextRecord; (void)HistoryTable; + // No real unwind possible; halt rather than corrupt the stack silently. + for (;;) ; +} + +// --------------------------------------------------------------------------- +// Code Pages & Locale helpers +// --------------------------------------------------------------------------- + +typedef struct { unsigned int MaxCharSize; unsigned char DefaultChar[2]; unsigned char LeadByte[12]; } CPINFO; + +__declspec(dllexport) BOOL GetCPInfo(unsigned int CodePage, CPINFO* lpCPInfo) { + (void)CodePage; + if (lpCPInfo) { + lpCPInfo->MaxCharSize = 1; + lpCPInfo->DefaultChar[0] = '?'; + lpCPInfo->DefaultChar[1] = 0; + for (int i = 0; i < 12; i++) lpCPInfo->LeadByte[i] = 0; + } + return TRUE; +} + +__declspec(dllexport) BOOL IsValidCodePage(unsigned int CodePage) { + // Acknowledge the two code pages we claim to support. + return (CodePage == 1252 || CodePage == 437) ? TRUE : FALSE; +} + +__declspec(dllexport) int LCMapStringA(unsigned int Locale, DWORD dwMapFlags, + const char* lpSrcStr, int cchSrc, + char* lpDestStr, int cchDest) { + (void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc; + (void)lpDestStr; (void)cchDest; + return 0; +} + +__declspec(dllexport) int LCMapStringW(unsigned int Locale, DWORD dwMapFlags, + const unsigned short* lpSrcStr, int cchSrc, + unsigned short* lpDestStr, int cchDest) { + (void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc; + (void)lpDestStr; (void)cchDest; + return 0; +} + +__declspec(dllexport) BOOL GetStringTypeA(unsigned int Locale, DWORD dwInfoType, + const char* lpSrcStr, int cchSrc, + unsigned short* lpCharType) { + (void)Locale; (void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType; + return FALSE; +} + +__declspec(dllexport) BOOL GetStringTypeW(DWORD dwInfoType, + const unsigned short* lpSrcStr, int cchSrc, + unsigned short* lpCharType) { + (void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType; + return FALSE; +} + +__declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType, + char* lpLCData, int cchData) { + (void)Locale; (void)LCType; (void)lpLCData; (void)cchData; + return 0; +} + +// --------------------------------------------------------------------------- +// Fiber Local Storage (FLS) +// We implement a minimal 64-slot static table so callers that use FLS for +// thread-local-ish state don't immediately fail. +// --------------------------------------------------------------------------- + +#define FLS_MAX_SLOTS 64 +#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL + +static void* gFlsSlots[FLS_MAX_SLOTS]; +static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated + +__declspec(dllexport) DWORD FlsAlloc(void* lpCallback) { + (void)lpCallback; + for (int i = 0; i < FLS_MAX_SLOTS; i++) { + if (!gFlsUsed[i]) { + gFlsUsed[i] = 1; + gFlsSlots[i] = NULL; + return (DWORD)i; + } + } + gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY + return (DWORD)FLS_OUT_OF_INDEXES; +} + +__declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) { + if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) { + gLastError = 87; // ERROR_INVALID_PARAMETER + return NULL; + } + return gFlsSlots[dwFlsIndex]; +} + +__declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) { + if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) { + gLastError = 87; + return FALSE; + } + gFlsSlots[dwFlsIndex] = lpFlsData; + return TRUE; +} + +__declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) { + if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) { + gLastError = 87; + return FALSE; + } + gFlsUsed[dwFlsIndex] = 0; + gFlsSlots[dwFlsIndex] = NULL; + return TRUE; +} + +// --------------------------------------------------------------------------- +// Pointer encoding (security cookie obfuscation) +// We have no security cookie; return the pointer unchanged. +// --------------------------------------------------------------------------- + +__declspec(dllexport) void* EncodePointer(void* Ptr) { return Ptr; } +__declspec(dllexport) void* DecodePointer(void* Ptr) { return Ptr; } diff --git a/code/win32/ole32.c b/code/win32/ole32.c index b008b5c..c86d62a 100644 --- a/code/win32/ole32.c +++ b/code/win32/ole32.c @@ -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 +#include + +// --------------------------------------------------------------------------- +// 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; +} diff --git a/code/win32/user32.c b/code/win32/user32.c index 25089ba..2eafcb1 100644 --- a/code/win32/user32.c +++ b/code/win32/user32.c @@ -1,20 +1,91 @@ -// user32.c - minimal user32 stubs (no window manager yet) +// user32.c - Win32 user interface stubs (no window manager yet) #include +#include -// 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; +} diff --git a/code/win32/winmm.c b/code/win32/winmm.c index 5155621..394a1da 100644 --- a/code/win32/winmm.c +++ b/code/win32/winmm.c @@ -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 +#include + +// --------------------------------------------------------------------------- +// 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; +} diff --git a/image.png b/image.png new file mode 100644 index 0000000..b6a6d87 Binary files /dev/null and b/image.png differ diff --git a/readme.md b/readme.md index 9a2fc42..193e09f 100644 --- a/readme.md +++ b/readme.md @@ -1,26 +1,26 @@ -# an x86-64 kernel +# NT-Lite -- an x86-64 kernel -boots. runs processes. doesn't crash (usually). +Boots. Runs processes. Mimics Windows to some extent. ## what is it? -a 64-bit operating system kernel written from scratch. +A 64-bit operating system kernel written from scratch. UEFI bootloader, physical and virtual memory managers, preemptive scheduler, HAL, VFS, PE loader, partial Win32 layer. -it boots into a shell. -~20k lines of code. +It boots into a shell. +~20k lines of C and assembly. ## features -- UEFI bootloader with PE loading and base relocation -- physical memory manager (frame stack, O(1) alloc/free) +- UEFI bootloader +- physical memory manager - virtual memory manager with kernel pool - HAL (screen, keyboard, timer, disk, PCI, APIC) - preemptive scheduler with priority levels - processes and threads -- VFS with FAT32 driver (read-only) +- VFS - drive-letter paths (`0:\apps\shell.exe`) - INI-based boot and driver configuration -- partial Win32 layer (kernel32 and user32, far from finished) +- partial Win32 layer (kernel32 and user32, far from finished but capable of running simple apps) - PE import resolution and dynamic driver loading ## how 2 build @@ -62,10 +62,14 @@ a filesystem driver fills in an `IO_FILESYSTEM` vtable and calls `IoRegisterFile drives 0–7. a drive only works if a filesystem registered on it. +## screenshots + +![shell.exe playing a video with bink 2](image.png) + ## faq Q: Why? -A: Why not. +A: Why not? Q: Does it run Doom? A: Not yet. @@ -74,7 +78,7 @@ Q: Is the Win32 layer real? A: Real enough to load binaries that expect it. Not real enough to run Notepad. Q: Is the scheduler preemptive? -A: Yes. +A: As much as it will ever be. Q: Will it corrupt my data? A: How would it? It's read-only.