353 lines
16 KiB
C
353 lines
16 KiB
C
// 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 <stddef.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
// 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);
|
|
|
|
// Set the mickey-to-pixel ratio. Controls base cursor speed.
|
|
// horizMickeys/vertMickeys: mickeys per 8 pixels of cursor movement.
|
|
// Higher values = slower cursor. Default is typically 8 horiz, 16 vert.
|
|
void platformMouseSetMickeys(int32_t horizMickeys, int32_t vertMickeys);
|
|
|
|
// 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
|