Now reports system information for troubleshooting.

This commit is contained in:
Scott Duensing 2026-03-18 17:26:49 -05:00
parent 5cf6db01e1
commit d876bc1c55
9 changed files with 610 additions and 15 deletions

View file

@ -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

View file

@ -23,6 +23,7 @@
#include "dvxWidget.h"
#include "dvxWm.h"
#include "shellApp.h"
#include "shellInfo.h"
#include <stdint.h>
#include <stdbool.h>
@ -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;

View file

@ -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
// ============================================================

View file

@ -29,6 +29,7 @@
#include "dvxPlatform.h"
#include "../dvxPalette.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -46,11 +47,24 @@
// Prototypes
// ============================================================
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;

View file

@ -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

View file

@ -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

63
dvxshell/shellInfo.c Normal file
View file

@ -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 <string.h>
// ============================================================
// 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 ===");
}

20
dvxshell/shellInfo.h Normal file
View file

@ -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

View file

@ -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();