// dvxPlat.h -- Platform abstraction layer for DVX GUI // // All OS-specific and CPU-specific code is isolated behind this // interface. To port DVX to a new platform, implement a new // dvxPlatformXxx.c against this header. // // Currently one implementation exists: // dvxPlatformDos.c -- DJGPP/DPMI: real VESA VBE, INT 33h mouse, // INT 16h keyboard, rep movsd/stosl asm spans // // The abstraction covers five areas: video mode setup, framebuffer // flushing, optimized memory spans, mouse input, and keyboard input. // File system operations are minimal (just filename validation) because // the C standard library handles most file I/O portably. // // Design rule: functions in this header must be stateless or manage their // own internal state. They must not reference AppContextT or any layer // above dvxTypes.h. This ensures the platform layer can be compiled and // tested independently. #ifndef DVX_PLAT_H #define DVX_PLAT_H #include "dvxTypes.h" #include #include // Maximum file path length. 260 matches DOS MAX_PATH. #define DVX_MAX_PATH 260 // ============================================================ // Keyboard event // ============================================================ // // Separating ASCII value from scancode handles the DOS keyboard model // where extended keys (arrows, F-keys) produce a zero ASCII byte followed // by a scancode. The platform layer normalizes this into a single struct. typedef struct { int32_t ascii; // ASCII value, 0 for extended/function keys int32_t scancode; // PC scan code (0x48=Up, 0x50=Down, etc.) } PlatformKeyEventT; // ============================================================ // Logging // ============================================================ // Append a line to dvx.log. Lives in dvx.exe, exported to all modules. void dvxLog(const char *fmt, ...); // ============================================================ // System lifecycle // ============================================================ // One-time platform initialisation. On DOS this installs signal handlers // for clean shutdown on Ctrl+C/Ctrl+Break. void platformInit(void); // Cooperative yield -- give up the CPU timeslice when the event loop has // nothing to do. On DOS this calls __dpmi_yield() to be friendly to // multitaskers (Windows 3.x, OS/2). void platformYield(void); // ============================================================ // Video // ============================================================ // Probe for a suitable video mode, enable it, map the framebuffer, and // allocate the system RAM backbuffer. On DOS this involves VBE BIOS calls // and DPMI physical memory mapping. Fills in all DisplayT fields on success. int32_t platformVideoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t preferredBpp); // Restore the previous video mode and free all video resources. On DOS // this restores VGA text mode (mode 3) and frees the DPMI memory mapping. void platformVideoShutdown(DisplayT *d); // Enumerate LFB-capable graphics modes. The callback is invoked for each // available mode. Used by videoInit() to find the best match for the // requested resolution and depth. void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData); // Program the VGA/VESA DAC palette registers (8-bit mode only). pal // points to RGB triplets (3 bytes per entry). void platformVideoSetPalette(const uint8_t *pal, int32_t firstEntry, int32_t count); // ============================================================ // Framebuffer flush // ============================================================ // Copy a rectangle from the system RAM backbuffer (d->backBuf) to the // display surface (d->lfb). On DOS this copies to real video memory via // the LFB mapping -- the critical path where PCI bus write speed matters. // Each scanline is copied as a contiguous block; rep movsd on DOS gives // near-optimal bus utilization for aligned 32-bit writes. void platformFlushRect(const DisplayT *d, const RectT *r); // ============================================================ // Optimised memory operations (span fill / copy) // ============================================================ // // These are the innermost loops of the renderer -- called once per // scanline of every rectangle fill, blit, and text draw. On DOS they // use inline assembly: rep stosl for fills (one instruction fills an // entire scanline) and rep movsd for copies. // // Three variants per operation (8/16/32 bpp) because the fill semantics // differ by depth: 8-bit fills a byte per pixel, 16-bit fills a word // (must handle odd counts), and 32-bit fills a dword. The copy variants // differ only in the byte count computation (count * bytesPerPixel). // drawInit() selects the right function pointers into BlitOpsT at startup. void platformSpanFill8(uint8_t *dst, uint32_t color, int32_t count); void platformSpanFill16(uint8_t *dst, uint32_t color, int32_t count); void platformSpanFill32(uint8_t *dst, uint32_t color, int32_t count); void platformSpanCopy8(uint8_t *dst, const uint8_t *src, int32_t count); void platformSpanCopy16(uint8_t *dst, const uint8_t *src, int32_t count); void platformSpanCopy32(uint8_t *dst, const uint8_t *src, int32_t count); // ============================================================ // Input -- Mouse // ============================================================ // Initialize the mouse driver and constrain movement to the screen bounds. // On DOS this calls INT 33h functions to detect the mouse, set the X/Y // range, and center the cursor. void platformMouseInit(int32_t screenW, int32_t screenH); // Poll the current mouse state. Buttons is a bitmask: bit 0 = left, // bit 1 = right, bit 2 = middle. Polling (rather than event-driven // callbacks) is the natural model for a cooperative event loop -- the // main loop polls once per frame and compares with the previous state // to detect press/release edges. void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons); // Detect and activate mouse wheel support. Returns true if the mouse // driver supports the CuteMouse Wheel API (INT 33h AX=0011h). This // call also activates wheel reporting -- after it returns true, function // 03h will return wheel delta in BH. Must be called after platformMouseInit. bool platformMouseWheelInit(void); // Read the accumulated wheel delta since the last call. Positive = scroll // down, negative = scroll up. Returns 0 if no wheel movement or if wheel // is not supported. The delta is cleared on each read (accumulated by the // driver between polls). int32_t platformMouseWheelPoll(void); // Set the double-speed threshold in mickeys/second. When the mouse // moves faster than this, cursor movement is doubled by the driver. // A very high value (e.g. 10000) effectively disables acceleration. void platformMouseSetAccel(int32_t threshold); // Move the mouse cursor to an absolute screen position. Uses INT 33h // function 04h on DOS. Used to clamp the cursor to window edges during // resize operations. void platformMouseWarp(int32_t x, int32_t y); // ============================================================ // Input -- Keyboard // ============================================================ // Return the current modifier key state in BIOS shift-state format: // bits 0-1 = either shift, bit 2 = ctrl, bit 3 = alt. On DOS this // reads the BIOS data area at 0040:0017. int32_t platformKeyboardGetModifiers(void); // Non-blocking read of the next key from the keyboard buffer. Returns // true if a key was available. On DOS this uses INT 16h AH=11h (check) // + AH=10h (read). Extended keys (0xE0 prefix from enhanced keyboard) // are normalized by zeroing the ASCII byte so the scancode identifies // them unambiguously. bool platformKeyboardRead(PlatformKeyEventT *evt); // Non-blocking read of the next key-up event. Returns true if a // key release was detected. On DOS this requires an INT 9 hook to // detect break codes (scan code with bit 7 set). bool platformKeyUpRead(PlatformKeyEventT *evt); // Install/remove the INT 9 hook for key-up detection. On DOS this // chains the hardware keyboard interrupt. Call Init before using // platformKeyUpRead, and Shutdown before exit. void platformKeyUpInit(void); void platformKeyUpShutdown(void); // Translate an Alt+key scancode to its corresponding ASCII character. // When Alt is held, DOS doesn't provide the ASCII value -- only the // scancode. This function contains a lookup table mapping scancodes // to their unshifted letter/digit. Returns 0 for scancodes that don't // map to a printable character (e.g. Alt+F1). char platformAltScanToChar(int32_t scancode); // ============================================================ // System information // ============================================================ // Maximum size of the formatted system information text #define PLATFORM_SYSINFO_MAX 4096 // Gather hardware information (CPU, clock, memory, DOS/DPMI version, // video, mouse, disk drives) and return as a pre-formatted text string. // The display pointer provides the current video mode info. Returns a // pointer to a static buffer valid for the lifetime of the process. // On DOS this uses CPUID, RDTSC, DPMI, INT 21h, INT 33h, and VBE. const char *platformGetSystemInfo(const DisplayT *display); // ============================================================ // File system // ============================================================ // Validate a filename against platform-specific rules. On DOS this // enforces 8.3 naming (no long filenames), checks for reserved device // names (CON, PRN, etc.), and rejects characters illegal in FAT filenames. // Returns NULL if the filename is valid, or a human-readable error string // describing why it's invalid. Used by the file dialog's save-as validation. const char *platformValidateFilename(const char *name); // Query current system memory. Sets totalKb and freeKb to the total // and free physical memory in kilobytes. Returns false if unavailable. bool platformGetMemoryInfo(uint32_t *totalKb, uint32_t *freeKb); // ============================================================ // Per-app memory tracking // ============================================================ // // Wraps malloc/free/calloc/realloc with a small header per allocation // that records the owning app ID and size. This lets DVX report per-app // memory usage in the Task Manager and detect leaks at app termination. // // The allocator reads *dvxMemAppIdPtr to determine which app to charge. // The shell sets this pointer to &ctx->currentAppId during init. // Tracked allocations carry a 16-byte header (magic + appId + size + pad). // Calls to dvxFree on non-tracked pointers (magic mismatch) fall through // to the real free() safely. // Per-app memory tracking (header-based). // The DXE export table maps malloc/free/calloc/realloc/strdup to // these wrappers. DXE code is tracked transparently. extern int32_t *dvxMemAppIdPtr; void *dvxMalloc(size_t size); void *dvxCalloc(size_t nmemb, size_t size); void *dvxRealloc(void *ptr, size_t size); void dvxFree(void *ptr); char *dvxStrdup(const char *s); void dvxMemSnapshotLoad(int32_t appId); uint32_t dvxMemGetAppUsage(int32_t appId); void dvxMemResetApp(int32_t appId); // Create a directory and all parent directories (like mkdir -p). // Returns 0 on success, -1 on failure. Existing directories are not // an error. int32_t platformMkdirRecursive(const char *path); // Change the working directory, including drive letter on DOS. Standard // chdir() does not switch drives under DJGPP; this wrapper calls setdisk() // first when the path contains a drive prefix (e.g. "A:\DVX"). void platformChdir(const char *path); // Free the backbuffer and palette without restoring text mode. Used // when switching between graphics modes live. void platformVideoFreeBuffers(DisplayT *d); // Return a pointer to the last directory separator in path, or NULL if // none is found. On DOS this checks both '/' and '\\' since DJGPP // accepts either. char *platformPathDirEnd(const char *path); // The platform's native directory separator character. // '/' on DJGPP (which accepts both '/' and '\\'). #define DVX_PATH_SEP '/' // Simple glob pattern matching for filenames. Case-insensitive. // Supports * (zero or more chars) and ? (one char). bool platformGlobMatch(const char *pattern, const char *name); // Return the platform's native line ending string. // "\r\n" on DOS. const char *platformLineEnding(void); // Strip platform-specific line ending characters from a buffer in place. // On DOS this removes '\r' from CR+LF pairs. Returns the new length. int32_t platformStripLineEndings(char *buf, int32_t len); // ============================================================ // DXE module support // ============================================================ // Register platform and C runtime symbols with the dynamic module // loader so that DXE modules can resolve them at load time. On DOS // this calls dlregsym() with the full DJGPP libc/libm/libgcc/platform // export table. void platformRegisterDxeExports(void); // ============================================================ // Crash recovery // ============================================================ // Log function signature for crash diagnostics. typedef void (*PlatformLogFnT)(const char *fmt, ...); // Install signal handlers for fatal exceptions (SIGSEGV, SIGFPE, SIGILL). // On crash, logs platform-specific diagnostics (register dump on DJGPP) // via logFn, stores the signal number in *crashSignal, then longjmps to // recoveryBuf. The caller is responsible for app-level diagnostics in // the setjmp recovery block. void platformInstallCrashHandler(jmp_buf *recoveryBuf, volatile int *crashSignal, PlatformLogFnT logFn); // Log platform-specific crash diagnostics (signal name and register dump // on DJGPP). Called internally by the crash handler before longjmp; // also available for manual invocation if needed. void platformLogCrashDetail(int sig, PlatformLogFnT logFn); // ============================================================ // VGA splash screen (mode 13h, 320x200, 256-color) // ============================================================ // Enter VGA mode 13h (320x200x256). void platformSplashInit(void); // Return to text mode 03h. void platformSplashShutdown(void); // Load and display a raw splash file (768 bytes palette + 64000 bytes pixels). // Returns true on success, false if the file could not be loaded. bool platformSplashLoadRaw(const char *path); // Fill a rectangle on the VGA mode 13h screen. void platformSplashFillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t color); // ============================================================ // DXE symbol overrides // ============================================================ // A name/function-pointer pair for registering symbol overrides with // the dynamic module loader. On DJGPP this maps directly to the // dxe_symbol_table struct used by dlregsym(). typedef struct { const char *name; void *func; } PlatformSymOverrideT; // Register function pointer overrides for subsequently loaded modules. // entries is an array of {name, funcPtr} pairs terminated by {NULL, NULL}. // On DJGPP this wraps dlregsym(). On platforms without DXE, this is a // no-op (RTLD_GLOBAL or equivalent handles symbol resolution). void platformRegisterSymOverrides(const PlatformSymOverrideT *entries); #endif // DVX_PLAT_H