Now reports system information for troubleshooting.
This commit is contained in:
parent
5cf6db01e1
commit
d876bc1c55
9 changed files with 610 additions and 15 deletions
|
|
@ -64,7 +64,7 @@ $(BINDIR)/dvxdemo:
|
||||||
mkdir -p $(BINDIR)/dvxdemo
|
mkdir -p $(BINDIR)/dvxdemo
|
||||||
|
|
||||||
# Dependencies
|
# 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)/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)/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
|
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "dvxWidget.h"
|
#include "dvxWidget.h"
|
||||||
#include "dvxWm.h"
|
#include "dvxWm.h"
|
||||||
#include "shellApp.h"
|
#include "shellApp.h"
|
||||||
|
#include "shellInfo.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
@ -55,6 +56,7 @@
|
||||||
#define CMD_MIN_ON_RUN 104
|
#define CMD_MIN_ON_RUN 104
|
||||||
#define CMD_ABOUT 300
|
#define CMD_ABOUT 300
|
||||||
#define CMD_TASK_MGR 301
|
#define CMD_TASK_MGR 301
|
||||||
|
#define CMD_SYSINFO 302
|
||||||
|
|
||||||
// Task Manager column count
|
// Task Manager column count
|
||||||
#define TM_COL_COUNT 4
|
#define TM_COL_COUNT 4
|
||||||
|
|
@ -94,6 +96,7 @@ static void onPmMenu(WindowT *win, int32_t menuId);
|
||||||
static void scanAppsDir(void);
|
static void scanAppsDir(void);
|
||||||
static void scanAppsDirRecurse(const char *dirPath);
|
static void scanAppsDirRecurse(const char *dirPath);
|
||||||
static void showAboutDialog(void);
|
static void showAboutDialog(void);
|
||||||
|
static void showSystemInfo(void);
|
||||||
static void updateStatusText(void);
|
static void updateStatusText(void);
|
||||||
|
|
||||||
// Task Manager
|
// Task Manager
|
||||||
|
|
@ -162,6 +165,7 @@ static void buildPmWindow(void) {
|
||||||
|
|
||||||
MenuT *helpMenu = wmAddMenu(menuBar, "&Help");
|
MenuT *helpMenu = wmAddMenu(menuBar, "&Help");
|
||||||
wmAddMenuItem(helpMenu, "&About DVX Shell...", CMD_ABOUT);
|
wmAddMenuItem(helpMenu, "&About DVX Shell...", CMD_ABOUT);
|
||||||
|
wmAddMenuItem(helpMenu, "&System Information...", CMD_SYSINFO);
|
||||||
wmAddMenuSeparator(helpMenu);
|
wmAddMenuSeparator(helpMenu);
|
||||||
wmAddMenuItem(helpMenu, "&Task Manager\tCtrl+Esc", CMD_TASK_MGR);
|
wmAddMenuItem(helpMenu, "&Task Manager\tCtrl+Esc", CMD_TASK_MGR);
|
||||||
|
|
||||||
|
|
@ -380,6 +384,10 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
|
||||||
showAboutDialog();
|
showAboutDialog();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CMD_SYSINFO:
|
||||||
|
showSystemInfo();
|
||||||
|
break;
|
||||||
|
|
||||||
case CMD_TASK_MGR:
|
case CMD_TASK_MGR:
|
||||||
buildTaskManager();
|
buildTaskManager();
|
||||||
break;
|
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) {
|
static void updateStatusText(void) {
|
||||||
if (!sStatusLabel) {
|
if (!sStatusLabel) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,21 @@ bool platformKeyboardRead(PlatformKeyEventT *evt);
|
||||||
// map to a printable character (e.g. Alt+F1).
|
// map to a printable character (e.g. Alt+F1).
|
||||||
char platformAltScanToChar(int32_t scancode);
|
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
|
// File system
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "dvxPlatform.h"
|
#include "dvxPlatform.h"
|
||||||
#include "../dvxPalette.h"
|
#include "../dvxPalette.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -46,11 +47,24 @@
|
||||||
// Prototypes
|
// 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 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 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);
|
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);
|
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 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).
|
// 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.
|
// 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
|
// platformInit
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -686,13 +1143,6 @@ void platformMouseInit(int32_t screenW, int32_t screenH) {
|
||||||
// on every mickeyed movement, which is wasteful when we only sample
|
// on every mickeyed movement, which is wasteful when we only sample
|
||||||
// once per frame anyway.
|
// 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) {
|
void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons) {
|
||||||
__dpmi_regs r;
|
__dpmi_regs r;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ OBJDIR = ../obj/dvxshell
|
||||||
BINDIR = ../bin
|
BINDIR = ../bin
|
||||||
LIBDIR = ../lib
|
LIBDIR = ../lib
|
||||||
|
|
||||||
SRCS = shellMain.c shellApp.c shellExport.c
|
SRCS = shellMain.c shellApp.c shellExport.c shellInfo.c
|
||||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||||
TARGET = $(BINDIR)/dvx.exe
|
TARGET = $(BINDIR)/dvx.exe
|
||||||
|
|
||||||
|
|
@ -42,6 +42,7 @@ $(BINDIR):
|
||||||
# Dependencies
|
# Dependencies
|
||||||
$(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
|
$(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)/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:
|
clean:
|
||||||
rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log
|
rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
// design limitation — there's no automatic fallback to the host's libc.
|
// design limitation — there's no automatic fallback to the host's libc.
|
||||||
|
|
||||||
#include "shellApp.h"
|
#include "shellApp.h"
|
||||||
|
#include "shellInfo.h"
|
||||||
#include "dvxApp.h"
|
#include "dvxApp.h"
|
||||||
#include "dvxDialog.h"
|
#include "dvxDialog.h"
|
||||||
#include "dvxWidget.h"
|
#include "dvxWidget.h"
|
||||||
|
|
@ -394,6 +395,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
DXE_EXPORT(shellForceKillApp)
|
DXE_EXPORT(shellForceKillApp)
|
||||||
DXE_EXPORT(shellRunningAppCount)
|
DXE_EXPORT(shellRunningAppCount)
|
||||||
DXE_EXPORT(shellRegisterDesktopUpdate)
|
DXE_EXPORT(shellRegisterDesktopUpdate)
|
||||||
|
DXE_EXPORT(shellGetSystemInfo)
|
||||||
|
|
||||||
// libc exports below. DXE3 modules are compiled as relocatable objects,
|
// libc exports below. DXE3 modules are compiled as relocatable objects,
|
||||||
// not fully linked executables. Any libc function the DXE calls must be
|
// not fully linked executables. Any libc function the DXE calls must be
|
||||||
|
|
|
||||||
63
dvxshell/shellInfo.c
Normal file
63
dvxshell/shellInfo.c
Normal 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
20
dvxshell/shellInfo.h
Normal 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
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
// doesn't take down the whole system.
|
// doesn't take down the whole system.
|
||||||
|
|
||||||
#include "shellApp.h"
|
#include "shellApp.h"
|
||||||
|
#include "shellInfo.h"
|
||||||
#include "dvxDialog.h"
|
#include "dvxDialog.h"
|
||||||
#include "platform/dvxPlatform.h"
|
#include "platform/dvxPlatform.h"
|
||||||
|
|
||||||
|
|
@ -259,6 +260,9 @@ int main(void) {
|
||||||
// roughly twice as many scheduling turns as any single app.
|
// roughly twice as many scheduling turns as any single app.
|
||||||
tsSetPriority(0, TS_PRIORITY_HIGH);
|
tsSetPriority(0, TS_PRIORITY_HIGH);
|
||||||
|
|
||||||
|
// Gather system information (CPU, memory, drives, etc.)
|
||||||
|
shellInfoInit(&sCtx);
|
||||||
|
|
||||||
// Register DXE export table
|
// Register DXE export table
|
||||||
shellExportInit();
|
shellExportInit();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue