From d876bc1c5516ff908b1d5a4ea3fe9d27f96a8244 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Wed, 18 Mar 2026 17:26:49 -0500 Subject: [PATCH] Now reports system information for troubleshooting. --- apps/Makefile | 2 +- apps/progman/progman.c | 40 +++ dvx/platform/dvxPlatform.h | 15 ++ dvx/platform/dvxPlatformDos.c | 474 +++++++++++++++++++++++++++++++++- dvxshell/Makefile | 5 +- dvxshell/shellExport.c | 2 + dvxshell/shellInfo.c | 63 +++++ dvxshell/shellInfo.h | 20 ++ dvxshell/shellMain.c | 4 + 9 files changed, 610 insertions(+), 15 deletions(-) create mode 100644 dvxshell/shellInfo.c create mode 100644 dvxshell/shellInfo.h diff --git a/apps/Makefile b/apps/Makefile index f440fb5..f0953ae 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -64,7 +64,7 @@ $(BINDIR)/dvxdemo: mkdir -p $(BINDIR)/dvxdemo # Dependencies -$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h +$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h ../dvxshell/shellInfo.h $(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h $(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h $(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h diff --git a/apps/progman/progman.c b/apps/progman/progman.c index be6b866..80415c5 100644 --- a/apps/progman/progman.c +++ b/apps/progman/progman.c @@ -23,6 +23,7 @@ #include "dvxWidget.h" #include "dvxWm.h" #include "shellApp.h" +#include "shellInfo.h" #include #include @@ -55,6 +56,7 @@ #define CMD_MIN_ON_RUN 104 #define CMD_ABOUT 300 #define CMD_TASK_MGR 301 +#define CMD_SYSINFO 302 // Task Manager column count #define TM_COL_COUNT 4 @@ -94,6 +96,7 @@ static void onPmMenu(WindowT *win, int32_t menuId); static void scanAppsDir(void); static void scanAppsDirRecurse(const char *dirPath); static void showAboutDialog(void); +static void showSystemInfo(void); static void updateStatusText(void); // Task Manager @@ -162,6 +165,7 @@ static void buildPmWindow(void) { MenuT *helpMenu = wmAddMenu(menuBar, "&Help"); wmAddMenuItem(helpMenu, "&About DVX Shell...", CMD_ABOUT); + wmAddMenuItem(helpMenu, "&System Information...", CMD_SYSINFO); wmAddMenuSeparator(helpMenu); wmAddMenuItem(helpMenu, "&Task Manager\tCtrl+Esc", CMD_TASK_MGR); @@ -380,6 +384,10 @@ static void onPmMenu(WindowT *win, int32_t menuId) { showAboutDialog(); break; + case CMD_SYSINFO: + showSystemInfo(); + break; + case CMD_TASK_MGR: buildTaskManager(); break; @@ -634,6 +642,38 @@ static void showAboutDialog(void) { } +static void showSystemInfo(void) { + const char *info = shellGetSystemInfo(); + + if (!info || !info[0]) { + dvxMessageBox(sAc, "System Information", "No system information available.", MB_OK | MB_ICONINFO); + return; + } + + // Create a window with a read-only text area + int32_t screenW = sAc->display.width; + int32_t screenH = sAc->display.height; + int32_t winW = 400; + int32_t winH = 360; + int32_t winX = (screenW - winW) / 2; + int32_t winY = (screenH - winH) / 4; + + WindowT *win = dvxCreateWindow(sAc, "System Information", winX, winY, winW, winH, true); + + if (!win) { + return; + } + + WidgetT *root = wgtInitWindow(sAc, win); + WidgetT *ta = wgtTextArea(root, 4096); + ta->weight = 100; + wgtSetText(ta, info); + + // Mark read-only by disabling the text area + wgtSetEnabled(ta, false); +} + + static void updateStatusText(void) { if (!sStatusLabel) { return; diff --git a/dvx/platform/dvxPlatform.h b/dvx/platform/dvxPlatform.h index c6369f3..06d2063 100644 --- a/dvx/platform/dvxPlatform.h +++ b/dvx/platform/dvxPlatform.h @@ -163,6 +163,21 @@ bool platformKeyboardRead(PlatformKeyEventT *evt); // 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. +// On other platforms it returns whatever the OS can report. +const char *platformGetSystemInfo(const DisplayT *display); + // ============================================================ // File system // ============================================================ diff --git a/dvx/platform/dvxPlatformDos.c b/dvx/platform/dvxPlatformDos.c index ce0c779..4f9ef58 100644 --- a/dvx/platform/dvxPlatformDos.c +++ b/dvx/platform/dvxPlatformDos.c @@ -29,6 +29,7 @@ #include "dvxPlatform.h" #include "../dvxPalette.h" +#include #include #include #include @@ -46,11 +47,24 @@ // Prototypes // ============================================================ -static int32_t findBestMode(int32_t requestedW, int32_t requestedH, int32_t preferredBpp, uint16_t *outMode, DisplayT *d); -static void getModeInfo(uint16_t mode, DisplayT *d, int32_t *score, int32_t requestedW, int32_t requestedH, int32_t preferredBpp); -static int32_t mapLfb(DisplayT *d, uint32_t physAddr); -void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData); -static int32_t setVesaMode(uint16_t mode); +static uint32_t estimateClockMhz(void); +static int32_t findBestMode(int32_t requestedW, int32_t requestedH, int32_t preferredBpp, uint16_t *outMode, DisplayT *d); +static void getModeInfo(uint16_t mode, DisplayT *d, int32_t *score, int32_t requestedW, int32_t requestedH, int32_t preferredBpp); +static bool hasCpuid(void); +static int32_t mapLfb(DisplayT *d, uint32_t physAddr); +void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData); +static int32_t setVesaMode(uint16_t mode); +static void sysInfoAppend(const char *fmt, ...); + +// ============================================================ +// Module state +// ============================================================ + +// Wheel state: set by platformMouseWheelInit, read by platformMousePoll +// and platformGetSystemInfo. Declared here (above all functions) so +// every function in the file can see them. +static bool sHasMouseWheel = false; +static int32_t sLastWheelDelta = 0; // Alt+key scan code to ASCII lookup table (indexed by BIOS scan code). // INT 16h returns these scan codes with ascii=0 for Alt+key combos. @@ -543,6 +557,449 @@ void platformFlushRect(const DisplayT *d, const RectT *r) { } +// ============================================================ +// System information — static buffer and helpers +// ============================================================ + +static char sSysInfoBuf[PLATFORM_SYSINFO_MAX]; +static int32_t sSysInfoPos = 0; + +// Formatted append to the system info buffer (newline-terminated). +static void sysInfoAppend(const char *fmt, ...) { + if (sSysInfoPos >= PLATFORM_SYSINFO_MAX - 1) { + return; + } + + va_list ap; + va_start(ap, fmt); + int32_t written = vsnprintf(sSysInfoBuf + sSysInfoPos, PLATFORM_SYSINFO_MAX - sSysInfoPos, fmt, ap); + va_end(ap); + + if (written > 0) { + sSysInfoPos += written; + } + + if (sSysInfoPos < PLATFORM_SYSINFO_MAX - 1) { + sSysInfoBuf[sSysInfoPos] = '\n'; + sSysInfoPos++; + sSysInfoBuf[sSysInfoPos] = '\0'; + } +} + + +// ============================================================ +// estimateClockMhz — RDTSC calibration via BIOS timer +// ============================================================ +// +// Measures TSC ticks over 3 BIOS timer ticks (~165 ms). The BIOS timer +// at 0040:006C increments at 18.2065 Hz (1193182 / 65536 Hz per tick). +// Using 3 ticks instead of 1 reduces jitter from interrupt latency and +// gives a more stable reading. + +#define CLOCK_MEAS_TICKS 3 + +static uint32_t estimateClockMhz(void) { + uint32_t biosTimerAddr = 0x46C; // linear address 0000:046C = 0040:006C + + // Wait for tick boundary to synchronize + uint32_t tick0 = _farpeekl(_dos_ds, biosTimerAddr); + + while (_farpeekl(_dos_ds, biosTimerAddr) == tick0) { + // spin + } + + // Read TSC at tick boundary + uint32_t lo1; + uint32_t hi1; + + __asm__ __volatile__ ("rdtsc" : "=a"(lo1), "=d"(hi1)); + + uint32_t tickStart = _farpeekl(_dos_ds, biosTimerAddr); + + // Wait for CLOCK_MEAS_TICKS more ticks + while ((_farpeekl(_dos_ds, biosTimerAddr) - tickStart) < CLOCK_MEAS_TICKS) { + // spin + } + + // Read TSC at end + uint32_t lo2; + uint32_t hi2; + + __asm__ __volatile__ ("rdtsc" : "=a"(lo2), "=d"(hi2)); + + uint64_t tsc1 = ((uint64_t)hi1 << 32) | lo1; + uint64_t tsc2 = ((uint64_t)hi2 << 32) | lo2; + uint64_t delta = tsc2 - tsc1; + + // Each BIOS tick = 65536 / 1193182 seconds = 54925.4 microseconds + uint32_t mhz = (uint32_t)(delta / (CLOCK_MEAS_TICKS * 54925ULL)); + + return mhz; +} + + +// ============================================================ +// hasCpuid — check if CPUID instruction is available +// ============================================================ +// +// The CPUID instruction exists if bit 21 (ID flag) of EFLAGS can be +// toggled. On a 386 this bit is hardwired to 0. On a 486 without +// CPUID it's also hardwired. Only if we can flip it does CPUID exist. + +static bool hasCpuid(void) { + uint32_t before; + uint32_t after; + + __asm__ __volatile__ ( + "pushfl\n\t" + "popl %%eax\n\t" + "movl %%eax, %0\n\t" + "xorl $0x200000, %%eax\n\t" + "pushl %%eax\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %%eax\n\t" + "movl %%eax, %1\n\t" + : "=r"(before), "=r"(after) + : + : "eax" + ); + + __asm__ __volatile__ ( + "pushl %0\n\t" + "popfl" + : + : "r"(before) + ); + + return (before ^ after) & 0x200000; +} + + +// ============================================================ +// platformGetSystemInfo +// ============================================================ +// +// Gathers all available hardware information and formats it as a +// human-readable text string. Each section is separated by a blank +// line and headed with === Section ===. + +const char *platformGetSystemInfo(const DisplayT *display) { + __dpmi_regs r; + + sSysInfoPos = 0; + sSysInfoBuf[0] = '\0'; + + sysInfoAppend("DVX System Information"); + sysInfoAppend(""); + + // ---- CPU ---- + sysInfoAppend("=== CPU ==="); + + if (!hasCpuid()) { + sysInfoAppend("Processor: 386/486 (no CPUID support)"); + } else { + // Vendor string (CPUID leaf 0) + uint32_t maxFunc; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + char vendor[13]; + + __asm__ __volatile__ ( + "cpuid" + : "=a"(maxFunc), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(0) + ); + + memcpy(vendor + 0, &ebx, 4); + memcpy(vendor + 4, &edx, 4); + memcpy(vendor + 8, &ecx, 4); + vendor[12] = '\0'; + + sysInfoAppend("CPU Vendor: %s", vendor); + + // Family/model/stepping/features (CPUID leaf 1) + if (maxFunc >= 1) { + uint32_t eax; + uint32_t features; + + __asm__ __volatile__ ( + "cpuid" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(features) + : "a"(1) + ); + + int32_t stepping = eax & 0x0F; + int32_t model = (eax >> 4) & 0x0F; + int32_t family = (eax >> 8) & 0x0F; + + if (family == 0x0F) { + family += (eax >> 20) & 0xFF; + model += ((eax >> 16) & 0x0F) << 4; + } else if (family == 6) { + model += ((eax >> 16) & 0x0F) << 4; + } + + sysInfoAppend("Family: %ld Model: %ld Stepping: %ld", (long)family, (long)model, (long)stepping); + + // Feature flags + bool hasFpu = (features & (1U << 0)) != 0; + bool hasTsc = (features & (1U << 4)) != 0; + bool hasMmx = (features & (1U << 23)) != 0; + bool hasSse = (features & (1U << 25)) != 0; + bool hasSse2 = (features & (1U << 26)) != 0; + + char featureStr[128]; + int32_t fpos = 0; + + featureStr[0] = '\0'; + + if (hasFpu) { + fpos += snprintf(featureStr + fpos, sizeof(featureStr) - fpos, "FPU "); + } + if (hasTsc) { + fpos += snprintf(featureStr + fpos, sizeof(featureStr) - fpos, "TSC "); + } + if (hasMmx) { + fpos += snprintf(featureStr + fpos, sizeof(featureStr) - fpos, "MMX "); + } + if (hasSse) { + fpos += snprintf(featureStr + fpos, sizeof(featureStr) - fpos, "SSE "); + } + if (hasSse2) { + fpos += snprintf(featureStr + fpos, sizeof(featureStr) - fpos, "SSE2 "); + } + if (fpos > 0) { + featureStr[fpos - 1] = '\0'; + } + + sysInfoAppend("Features: %s", featureStr[0] ? featureStr : "(none)"); + + // Clock speed via RDTSC (Pentium+ only) + if (hasTsc) { + uint32_t mhz = estimateClockMhz(); + + if (mhz > 0) { + sysInfoAppend("Clock: ~%lu MHz", (unsigned long)mhz); + } + } + } + + // Brand string (CPUID extended leaves 0x80000002-0x80000004) + uint32_t maxExtFunc; + + __asm__ __volatile__ ( + "cpuid" + : "=a"(maxExtFunc), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(0x80000000U) + ); + + if (maxExtFunc >= 0x80000004U) { + char brand[49]; + uint32_t *b = (uint32_t *)brand; + + for (uint32_t func = 0x80000002U; func <= 0x80000004U; func++) { + __asm__ __volatile__ ( + "cpuid" + : "=a"(b[0]), "=b"(b[1]), "=c"(b[2]), "=d"(b[3]) + : "a"(func) + ); + + b += 4; + } + + brand[48] = '\0'; + + const char *p = brand; + + while (*p == ' ') { + p++; + } + + if (*p) { + sysInfoAppend("Brand: %s", p); + } + } + } + + // ---- Memory ---- + sysInfoAppend(""); + sysInfoAppend("=== Memory ==="); + + __dpmi_free_mem_info memInfo; + + if (__dpmi_get_free_memory_information(&memInfo) == 0) { + if (memInfo.largest_available_free_block_in_bytes != 0xFFFFFFFFUL) { + sysInfoAppend("Largest free block: %lu KB", (unsigned long)(memInfo.largest_available_free_block_in_bytes / 1024)); + } + if (memInfo.total_number_of_physical_pages != 0xFFFFFFFFUL) { + uint32_t totalKb = memInfo.total_number_of_physical_pages * 4; + sysInfoAppend("Total physical: %lu KB (%lu MB)", (unsigned long)totalKb, (unsigned long)(totalKb / 1024)); + } + if (memInfo.total_number_of_free_pages != 0xFFFFFFFFUL) { + uint32_t freeKb = memInfo.total_number_of_free_pages * 4; + sysInfoAppend("Free physical: %lu KB (%lu MB)", (unsigned long)freeKb, (unsigned long)(freeKb / 1024)); + } + if (memInfo.linear_address_space_size_in_pages != 0xFFFFFFFFUL) { + uint32_t linearKb = memInfo.linear_address_space_size_in_pages * 4; + sysInfoAppend("Linear address space: %lu KB (%lu MB)", (unsigned long)linearKb, (unsigned long)(linearKb / 1024)); + } + if (memInfo.size_of_paging_file_partition_in_pages != 0xFFFFFFFFUL) { + uint32_t pagingKb = memInfo.size_of_paging_file_partition_in_pages * 4; + sysInfoAppend("Paging file: %lu KB (%lu MB)", (unsigned long)pagingKb, (unsigned long)(pagingKb / 1024)); + } + if (memInfo.free_linear_address_space_in_pages != 0xFFFFFFFFUL) { + uint32_t freeLinearKb = memInfo.free_linear_address_space_in_pages * 4; + sysInfoAppend("Free linear space: %lu KB", (unsigned long)freeLinearKb); + } + } else { + sysInfoAppend("DPMI memory info unavailable"); + } + + // ---- DOS / DPMI ---- + sysInfoAppend(""); + sysInfoAppend("=== DOS ==="); + + memset(&r, 0, sizeof(r)); + r.x.ax = 0x3000; + __dpmi_int(0x21, &r); + sysInfoAppend("DOS Version: %ld.%02ld", (long)(r.x.ax & 0xFF), (long)((r.x.ax >> 8) & 0xFF)); + + __dpmi_version_ret ver; + + if (__dpmi_get_version(&ver) == 0) { + sysInfoAppend("DPMI Version: %d.%02d", ver.major, ver.minor); + sysInfoAppend("DPMI Flags: %s%s%s", + (ver.flags & 0x01) ? "32-bit " : "16-bit ", + (ver.flags & 0x02) ? "V86 " : "", + (ver.flags & 0x04) ? "VirtMem " : ""); + sysInfoAppend("CPU Type: %d86", ver.cpu); + } + + // ---- Video ---- + sysInfoAppend(""); + sysInfoAppend("=== Video ==="); + + // VBE controller info for version, video RAM, OEM string + _farpokeb(_dos_ds, __tb + 0, 'V'); + _farpokeb(_dos_ds, __tb + 1, 'B'); + _farpokeb(_dos_ds, __tb + 2, 'E'); + _farpokeb(_dos_ds, __tb + 3, '2'); + + memset(&r, 0, sizeof(r)); + r.x.ax = 0x4F00; + r.x.es = __tb >> 4; + r.x.di = __tb & 0x0F; + __dpmi_int(0x10, &r); + + if (r.x.ax == 0x004F) { + uint16_t vbeVersion = _farpeekw(_dos_ds, __tb + 4); + uint16_t totalMem64k = _farpeekw(_dos_ds, __tb + 18); + + sysInfoAppend("VBE Version: %d.%d", vbeVersion >> 8, vbeVersion & 0xFF); + sysInfoAppend("Video memory: %lu KB", (unsigned long)(totalMem64k * 64)); + + // OEM string (real-mode far pointer at offset 6) + uint16_t oemOff = _farpeekw(_dos_ds, __tb + 6); + uint16_t oemSeg = _farpeekw(_dos_ds, __tb + 8); + uint32_t oemAddr = ((uint32_t)oemSeg << 4) + oemOff; + + char oemStr[80]; + int32_t oemLen = 0; + + while (oemLen < 79) { + char c = _farpeekb(_dos_ds, oemAddr + oemLen); + + if (c == 0) { + break; + } + + oemStr[oemLen++] = c; + } + + oemStr[oemLen] = '\0'; + + if (oemLen > 0) { + sysInfoAppend("VBE OEM: %s", oemStr); + } + } + + if (display) { + sysInfoAppend("Resolution: %ldx%ld", (long)display->width, (long)display->height); + sysInfoAppend("Color depth: %ld bpp", (long)display->format.bitsPerPixel); + sysInfoAppend("Pitch: %ld bytes", (long)display->pitch); + + uint32_t fbSize = (uint32_t)display->pitch * (uint32_t)display->height; + sysInfoAppend("Framebuffer: %lu KB", (unsigned long)(fbSize / 1024)); + + if (display->format.bitsPerPixel >= 15) { + sysInfoAppend("Red: %ld bits @ bit %ld", (long)display->format.redBits, (long)display->format.redShift); + sysInfoAppend("Green: %ld bits @ bit %ld", (long)display->format.greenBits, (long)display->format.greenShift); + sysInfoAppend("Blue: %ld bits @ bit %ld", (long)display->format.blueBits, (long)display->format.blueShift); + } + } + + // ---- Mouse ---- + sysInfoAppend(""); + sysInfoAppend("=== Mouse ==="); + + memset(&r, 0, sizeof(r)); + r.x.ax = 0x0000; + __dpmi_int(0x33, &r); + + if (r.x.ax == 0xFFFF) { + sysInfoAppend("Mouse: Detected (%ld buttons)", (long)r.x.bx); + } else { + sysInfoAppend("Mouse: Not detected"); + } + + sysInfoAppend("Wheel: %s", sHasMouseWheel ? "Yes (CuteMouse Wheel API)" : "No"); + + // ---- Disk Drives ---- + sysInfoAppend(""); + sysInfoAppend("=== Disk Drives ==="); + + for (int32_t drv = 3; drv <= 26; drv++) { + // INT 21h AH=36h: Get disk free space + memset(&r, 0, sizeof(r)); + r.x.ax = 0x3600; + r.x.dx = drv; + __dpmi_int(0x21, &r); + + if (r.x.ax == 0xFFFF) { + continue; + } + + uint32_t sectPerClust = r.x.ax; + uint32_t freeClusters = r.x.bx; + uint32_t bytesPerSect = r.x.cx; + uint32_t totalClusters = r.x.dx; + + uint32_t clusterSize = sectPerClust * bytesPerSect; + uint32_t totalMb = (uint32_t)((uint64_t)totalClusters * clusterSize / (1024 * 1024)); + uint32_t freeMb = (uint32_t)((uint64_t)freeClusters * clusterSize / (1024 * 1024)); + + // INT 21h AX=4408h: Check if drive is removable + memset(&r, 0, sizeof(r)); + r.x.ax = 0x4408; + r.x.bx = drv; + __dpmi_int(0x21, &r); + + const char *driveType = "Unknown"; + + if (!(r.x.flags & 0x01)) { + driveType = (r.x.ax == 0) ? "Removable" : "Fixed"; + } + + char letter = 'A' + (drv - 1); + sysInfoAppend("%c: %s %lu MB total %lu MB free", letter, driveType, (unsigned long)totalMb, (unsigned long)freeMb); + } + + return sSysInfoBuf; +} + + // ============================================================ // platformInit // ============================================================ @@ -686,13 +1143,6 @@ void platformMouseInit(int32_t screenW, int32_t screenH) { // on every mickeyed movement, which is wasteful when we only sample // once per frame anyway. -// Wheel state: detected by platformMouseWheelInit, read by platformMousePoll. -// The wheel delta is extracted from function 03h BH in the same INT call that -// reads position and buttons, avoiding a double call that would clear the -// accumulated counter. -static bool sHasMouseWheel = false; -static int32_t sLastWheelDelta = 0; - void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons) { __dpmi_regs r; diff --git a/dvxshell/Makefile b/dvxshell/Makefile index d905232..b3c5017 100644 --- a/dvxshell/Makefile +++ b/dvxshell/Makefile @@ -12,7 +12,7 @@ OBJDIR = ../obj/dvxshell BINDIR = ../bin LIBDIR = ../lib -SRCS = shellMain.c shellApp.c shellExport.c +SRCS = shellMain.c shellApp.c shellExport.c shellInfo.c OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) TARGET = $(BINDIR)/dvx.exe @@ -42,6 +42,7 @@ $(BINDIR): # Dependencies $(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h $(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h -$(OBJDIR)/shellExport.o: shellExport.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h +$(OBJDIR)/shellExport.o: shellExport.c shellApp.h shellInfo.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h +$(OBJDIR)/shellInfo.o: shellInfo.c shellInfo.h shellApp.h ../dvx/dvxApp.h ../dvx/platform/dvxPlatform.h clean: rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log diff --git a/dvxshell/shellExport.c b/dvxshell/shellExport.c index 382075b..3aff038 100644 --- a/dvxshell/shellExport.c +++ b/dvxshell/shellExport.c @@ -28,6 +28,7 @@ // design limitation — there's no automatic fallback to the host's libc. #include "shellApp.h" +#include "shellInfo.h" #include "dvxApp.h" #include "dvxDialog.h" #include "dvxWidget.h" @@ -394,6 +395,7 @@ DXE_EXPORT_TABLE(shellExportTable) DXE_EXPORT(shellForceKillApp) DXE_EXPORT(shellRunningAppCount) DXE_EXPORT(shellRegisterDesktopUpdate) + DXE_EXPORT(shellGetSystemInfo) // libc exports below. DXE3 modules are compiled as relocatable objects, // not fully linked executables. Any libc function the DXE calls must be diff --git a/dvxshell/shellInfo.c b/dvxshell/shellInfo.c new file mode 100644 index 0000000..74f4637 --- /dev/null +++ b/dvxshell/shellInfo.c @@ -0,0 +1,63 @@ +// shellInfo.c — System information wrapper for DVX Shell +// +// Delegates hardware detection to the platform layer via +// platformGetSystemInfo(), then logs the result line-by-line +// to DVX.LOG. The result pointer is cached so subsequent calls +// to shellGetSystemInfo() return instantly without re-probing. + +#include "shellInfo.h" +#include "shellApp.h" +#include "platform/dvxPlatform.h" + +#include + +// ============================================================ +// Module state +// ============================================================ + +static const char *sCachedInfo = NULL; + +// ============================================================ +// shellGetSystemInfo — return the cached info text +// ============================================================ + +const char *shellGetSystemInfo(void) { + return sCachedInfo ? sCachedInfo : ""; +} + + +// ============================================================ +// shellInfoInit — gather info and log it +// ============================================================ + +void shellInfoInit(AppContextT *ctx) { + sCachedInfo = platformGetSystemInfo(&ctx->display); + + // Log each line individually so the log file is readable + shellLog("=== System Information ==="); + + const char *line = sCachedInfo; + + while (*line) { + const char *eol = strchr(line, '\n'); + + if (!eol) { + shellLog("%s", line); + break; + } + + int32_t len = (int32_t)(eol - line); + char tmp[256]; + + if (len >= (int32_t)sizeof(tmp)) { + len = sizeof(tmp) - 1; + } + + memcpy(tmp, line, len); + tmp[len] = '\0'; + shellLog("%s", tmp); + line = eol + 1; + } + + shellLog("=== End System Information ==="); +} diff --git a/dvxshell/shellInfo.h b/dvxshell/shellInfo.h new file mode 100644 index 0000000..ae6382e --- /dev/null +++ b/dvxshell/shellInfo.h @@ -0,0 +1,20 @@ +// shellInfo.h — System information display for DVX Shell +// +// Thin wrapper around platformGetSystemInfo(). Calls the platform +// layer to gather hardware info, logs each line to DVX.LOG, and +// caches the result for the Program Manager's "System Information" +// dialog. +#ifndef SHELL_INFO_H +#define SHELL_INFO_H + +#include "dvxApp.h" + +// Gather all hardware information via the platform layer, log it, +// and store for later retrieval. Call once after dvxInit. +void shellInfoInit(AppContextT *ctx); + +// Return the formatted system information text. The pointer is valid +// for the lifetime of the process (static buffer in the platform layer). +const char *shellGetSystemInfo(void); + +#endif // SHELL_INFO_H diff --git a/dvxshell/shellMain.c b/dvxshell/shellMain.c index f4a0011..7906c16 100644 --- a/dvxshell/shellMain.c +++ b/dvxshell/shellMain.c @@ -25,6 +25,7 @@ // doesn't take down the whole system. #include "shellApp.h" +#include "shellInfo.h" #include "dvxDialog.h" #include "platform/dvxPlatform.h" @@ -259,6 +260,9 @@ int main(void) { // roughly twice as many scheduling turns as any single app. tsSetPriority(0, TS_PRIORITY_HIGH); + // Gather system information (CPU, memory, drives, etc.) + shellInfoInit(&sCtx); + // Register DXE export table shellExportInit();