Initial commit.
This commit is contained in:
commit
628ef231b9
30 changed files with 8836 additions and 0 deletions
106
.claude/settings.local.json
Normal file
106
.claude/settings.local.json
Normal file
File diff suppressed because one or more lines are too long
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.CUR filter=lfs diff=lfs merge=lfs -text
|
||||
*.cur filter=lfs diff=lfs merge=lfs -text
|
||||
*.ico filter=lfs diff=lfs merge=lfs -text
|
||||
*.DRV filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.so.* filter=lfs diff=lfs merge=lfs -text
|
||||
*.BMP filter=lfs diff=lfs merge=lfs -text
|
||||
*.ICO filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Build artifacts
|
||||
obj/
|
||||
win31drv/obj/
|
||||
win31drv/libwindrv.a
|
||||
bin/
|
||||
|
||||
# Runtime logs
|
||||
OUTPUT.LOG
|
||||
|
||||
# Editor backups
|
||||
*~
|
||||
*.swp
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "vbesvga.drv"]
|
||||
path = vbesvga.drv
|
||||
url = https://github.com/PluMGMK/vbesvga.drv.git
|
||||
53
Makefile
Normal file
53
Makefile
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# ============================================================================
|
||||
# Makefile for windrv demo - Windows 3.x display driver loader for DJGPP/DOS
|
||||
# ============================================================================
|
||||
|
||||
DJGPP_PREFIX ?= $(HOME)/djgpp/djgpp
|
||||
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
CFLAGS = -Wall -Wextra -O2 -std=gnu99 -Iwin31drv
|
||||
LDFLAGS =
|
||||
|
||||
# DJGPP binutils need libfl.so.2 which may not be installed system-wide
|
||||
export LD_LIBRARY_PATH := $(realpath tools/lib):$(LD_LIBRARY_PATH)
|
||||
|
||||
OBJDIR = obj
|
||||
BINDIR = bin
|
||||
|
||||
LIBDIR = win31drv
|
||||
LIB = $(LIBDIR)/libwindrv.a
|
||||
|
||||
DEMO_SRC = demo.c
|
||||
DEMO_OBJ = $(OBJDIR)/demo.o
|
||||
DEMO_EXE = $(BINDIR)/demo.exe
|
||||
|
||||
.PHONY: all clean lib demo
|
||||
|
||||
all: lib demo
|
||||
|
||||
lib:
|
||||
$(MAKE) -C $(LIBDIR)
|
||||
|
||||
demo: $(DEMO_EXE)
|
||||
|
||||
$(DEMO_EXE): $(DEMO_OBJ) lib | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ $(DEMO_OBJ) -L$(LIBDIR) -lwindrv $(LDFLAGS)
|
||||
unzip -oj tools/cwsdpmi.zip bin/CWSDPMI.EXE -d $(BINDIR) 2>/dev/null; true
|
||||
cp tools/TEST.BAT $(BINDIR)/
|
||||
-cp -n drivers/*.DRV $(BINDIR)/ 2>/dev/null; true
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/demo.o: demo.c win31drv/windrv.h win31drv/wintypes.h
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(LIBDIR) clean
|
||||
-rm -rf $(OBJDIR) $(BINDIR)
|
||||
323
demo.c
Normal file
323
demo.c
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
// ============================================================================
|
||||
// demo.c - Demonstration of using Windows 3.x display drivers from DOS
|
||||
//
|
||||
// This program loads a Windows 3.x accelerated display driver (.DRV file)
|
||||
// and uses its hardware-accelerated functions to draw on screen.
|
||||
//
|
||||
// Usage: demo [-d] <driver.drv>
|
||||
// -d Enable debug output
|
||||
//
|
||||
// Example:
|
||||
// demo vga.drv
|
||||
// demo -d s3trio.drv
|
||||
// ============================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <pc.h>
|
||||
#include <dpmi.h>
|
||||
|
||||
#include "win31drv/windrv.h"
|
||||
#include "win31drv/wintypes.h"
|
||||
#include "win31drv/log.h"
|
||||
|
||||
__attribute__((noinline))
|
||||
static void demoDrawing(WdrvHandleT drv);
|
||||
static void printDriverInfo(WdrvHandleT drv);
|
||||
static void printUsage(const char *progName);
|
||||
static void setupPalette(WdrvHandleT drv);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *driverPath = NULL;
|
||||
bool debug = false;
|
||||
|
||||
// Parse arguments
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-d") == 0) {
|
||||
debug = true;
|
||||
} else if (argv[i][0] != '-') {
|
||||
driverPath = argv[i];
|
||||
} else {
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!driverPath) {
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
logInit("OUTPUT.LOG");
|
||||
|
||||
// Initialize the library
|
||||
logMsg("Initializing windrv library...\n");
|
||||
int32_t err = wdrvInit();
|
||||
if (err != WDRV_OK) {
|
||||
logErr("Failed to initialize: %s\n", wdrvGetLastErrorString());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
wdrvSetDebug(true);
|
||||
}
|
||||
|
||||
// Load the driver
|
||||
logMsg("Loading driver: %s\n", driverPath);
|
||||
WdrvHandleT drv = wdrvLoadDriver(driverPath);
|
||||
if (!drv) {
|
||||
logErr("Failed to load driver: %s\n", wdrvGetLastErrorString());
|
||||
wdrvShutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print driver info
|
||||
printDriverInfo(drv);
|
||||
|
||||
// Redirect stdout to the log file before entering SVGA mode.
|
||||
// In graphics mode, console output through INT 21h can crash
|
||||
// because the console handler tries to access VGA text memory
|
||||
// that's now part of the SVGA framebuffer.
|
||||
freopen("OUTPUT.LOG", "a", stdout);
|
||||
|
||||
// Enable the driver (set video mode)
|
||||
logMsg("Enabling driver...\n");
|
||||
err = wdrvEnable(drv, 0, 0, 0); // Use driver defaults
|
||||
if (err != WDRV_OK) {
|
||||
logErr("Failed to enable driver: %s\n", wdrvGetLastErrorString());
|
||||
wdrvUnloadDriver(drv);
|
||||
wdrvShutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set up a 256-color palette via the driver's SetPalette DDI.
|
||||
// This updates both the driver's internal color table (used by
|
||||
// RealizeObject for color matching) and the VGA DAC hardware.
|
||||
setupPalette(drv);
|
||||
|
||||
// Run drawing demos
|
||||
demoDrawing(drv);
|
||||
|
||||
logMsg("Drawing complete. Press any key...\n");
|
||||
|
||||
// Wait for a keypress so we can see the output
|
||||
while (!kbhit()) {
|
||||
// Busy-wait; kbhit() does keyboard I/O which keeps DOSBox-X responsive
|
||||
}
|
||||
(void)getkey(); // consume the key
|
||||
|
||||
// Disable the driver (restore text mode)
|
||||
logMsg("Calling wdrvDisable...\n");
|
||||
err = wdrvDisable(drv);
|
||||
if (err != WDRV_OK) {
|
||||
logMsg("wdrvDisable failed (err=%d), forcing text mode via INT 10h\n", (int)err);
|
||||
__dpmi_regs dr;
|
||||
memset(&dr, 0, sizeof(dr));
|
||||
dr.x.ax = 0x0003; // INT 10h AH=00 AL=03: set 80x25 text mode
|
||||
__dpmi_int(0x10, &dr);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
logMsg("Unloading driver...\n");
|
||||
wdrvUnloadDriver(drv);
|
||||
wdrvShutdown();
|
||||
|
||||
logMsg("Done.\n");
|
||||
logShutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void printUsage(const char *progName)
|
||||
{
|
||||
logErr("Usage: %s [-d] <driver.drv>\n", progName);
|
||||
logErr(" -d Enable debug output\n");
|
||||
logErr("\nLoads a Windows 3.x display driver and demonstrates\n");
|
||||
logErr("its accelerated drawing functions from DOS.\n");
|
||||
}
|
||||
|
||||
|
||||
static void printDriverInfo(WdrvHandleT drv)
|
||||
{
|
||||
WdrvInfoT info;
|
||||
wdrvGetInfo(drv, &info);
|
||||
|
||||
logMsg("\n=== Driver Information ===\n");
|
||||
logMsg(" Name: %s\n", info.driverName);
|
||||
logMsg(" Version: %u.%u\n", info.driverVersion >> 8,
|
||||
info.driverVersion & 0xFF);
|
||||
logMsg(" Resolution: %" PRId32 "x%" PRId32 "\n", info.maxWidth, info.maxHeight);
|
||||
logMsg(" Color depth: %" PRId32 " bpp\n", info.maxBpp);
|
||||
logMsg(" Colors: %" PRId32 "\n", info.numColors);
|
||||
logMsg(" Raster caps: 0x%04" PRIX32 "\n", info.rasterCaps);
|
||||
logMsg(" Capabilities:\n");
|
||||
logMsg(" BitBlt: %s\n", info.hasBitBlt ? "yes" : "no");
|
||||
logMsg(" Output: %s\n", info.hasOutput ? "yes" : "no");
|
||||
logMsg(" Pixel: %s\n", info.hasPixel ? "yes" : "no");
|
||||
logMsg(" StretchBlt: %s\n", info.hasStretchBlt ? "yes" : "no");
|
||||
logMsg(" ExtTextOut: %s\n", info.hasExtTextOut ? "yes" : "no");
|
||||
logMsg(" SetPalette: %s\n", info.hasSetPalette ? "yes" : "no");
|
||||
logMsg(" SetCursor: %s\n", info.hasSetCursor ? "yes" : "no");
|
||||
}
|
||||
|
||||
|
||||
// noinline: when inlined into main with -O2, the optimizer mishandles
|
||||
// callee-saved registers across the thunk calls in the Demo 2 → Demo 3
|
||||
// transition, causing the handle pointer to be corrupted.
|
||||
__attribute__((noinline))
|
||||
static void demoDrawing(WdrvHandleT drv)
|
||||
{
|
||||
WdrvInfoT info;
|
||||
wdrvGetInfo(drv, &info);
|
||||
|
||||
int16_t screenW = (int16_t)info.maxWidth;
|
||||
int16_t screenH = (int16_t)info.maxHeight;
|
||||
|
||||
if (screenW == 0) {
|
||||
screenW = 640;
|
||||
}
|
||||
if (screenH == 0) {
|
||||
screenH = 480;
|
||||
}
|
||||
|
||||
logMsg("demoDrawing: screenW=%d screenH=%d hasBitBlt=%d hasOutput=%d hasPixel=%d\n",
|
||||
screenW, screenH, info.hasBitBlt, info.hasOutput, info.hasPixel);
|
||||
|
||||
// Demo 1: Fill rectangles
|
||||
if (info.hasBitBlt) {
|
||||
logMsg("Demo 1: Fill rectangles\n");
|
||||
|
||||
// Clear screen to white
|
||||
wdrvFillRect(drv, 0, 0, screenW, screenH, MAKE_RGB(255, 255, 255));
|
||||
|
||||
static const uint32_t vgaColors[] = {
|
||||
MAKE_RGB( 0, 0, 0), // black
|
||||
MAKE_RGB( 0, 0, 255), // blue
|
||||
MAKE_RGB( 0, 255, 0), // green
|
||||
MAKE_RGB( 0, 255, 255), // cyan
|
||||
MAKE_RGB(255, 0, 0), // red
|
||||
MAKE_RGB(255, 0, 255), // magenta
|
||||
MAKE_RGB(255, 255, 0), // yellow
|
||||
MAKE_RGB(170, 170, 170), // light gray
|
||||
MAKE_RGB( 85, 85, 85), // dark gray
|
||||
MAKE_RGB( 85, 85, 255), // light blue
|
||||
MAKE_RGB( 85, 255, 85), // light green
|
||||
MAKE_RGB( 85, 255, 255), // light cyan
|
||||
MAKE_RGB(255, 85, 85), // light red
|
||||
MAKE_RGB(255, 85, 255), // light magenta
|
||||
MAKE_RGB(255, 255, 85), // yellow-ish
|
||||
MAKE_RGB(128, 128, 128), // mid gray
|
||||
};
|
||||
|
||||
int16_t boxW = screenW / 4;
|
||||
int16_t boxH = screenH / 4;
|
||||
|
||||
for (int16_t row = 0; row < 4; row++) {
|
||||
for (int16_t col = 0; col < 4; col++) {
|
||||
int idx = row * 4 + col;
|
||||
wdrvFillRect(drv, col * boxW + 4, row * boxH + 4,
|
||||
boxW - 8, boxH - 8, vgaColors[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
logMsg(" Drew %d colored rectangles\n", 16);
|
||||
}
|
||||
|
||||
// Demo 2: Draw pixel patterns
|
||||
if (info.hasPixel) {
|
||||
logMsg("Demo 2: Pixel patterns (8x8)\n");
|
||||
int pixCount = 0;
|
||||
for (int16_t y = 0; y < 8 && y < screenH; y++) {
|
||||
for (int16_t x = 0; x < 8 && x < screenW; x++) {
|
||||
uint8_t r = (uint8_t)(x * 32);
|
||||
uint8_t g = (uint8_t)(y * 32);
|
||||
uint8_t b = (uint8_t)((x + y) * 16);
|
||||
wdrvSetPixel(drv, x + screenW - 18, y + 10, MAKE_RGB(r, g, b));
|
||||
pixCount++;
|
||||
}
|
||||
}
|
||||
logMsg(" Drew %d pixels\n", pixCount);
|
||||
}
|
||||
|
||||
// Demo 3: Draw lines using Output (polyline with realized pen)
|
||||
if (info.hasOutput) {
|
||||
logMsg("Demo 3: Lines (starburst)\n");
|
||||
|
||||
int16_t cx = screenW / 2;
|
||||
int16_t cy = screenH / 2;
|
||||
int16_t radius = (screenH < screenW ? screenH : screenW) / 3;
|
||||
int lineCount = 0;
|
||||
|
||||
for (int angle = 0; angle < 360; angle += 15) {
|
||||
int32_t dx = 0;
|
||||
int32_t dy = 0;
|
||||
|
||||
int a = angle % 360;
|
||||
int qa = a % 90;
|
||||
int32_t s = (int32_t)qa * radius / 90;
|
||||
int32_t c = (int32_t)(90 - qa) * radius / 90;
|
||||
|
||||
if (a < 90) {
|
||||
dx = s;
|
||||
dy = -c;
|
||||
} else if (a < 180) {
|
||||
dx = c;
|
||||
dy = s;
|
||||
} else if (a < 270) {
|
||||
dx = -s;
|
||||
dy = c;
|
||||
} else {
|
||||
dx = -c;
|
||||
dy = -s;
|
||||
}
|
||||
|
||||
Point16T pts[2];
|
||||
pts[0].x = cx;
|
||||
pts[0].y = cy;
|
||||
pts[1].x = (int16_t)(cx + dx);
|
||||
pts[1].y = (int16_t)(cy + dy);
|
||||
|
||||
uint32_t lineColor = MAKE_RGB(
|
||||
(uint8_t)(angle * 255 / 360),
|
||||
(uint8_t)(255 - angle * 255 / 360),
|
||||
128);
|
||||
|
||||
wdrvPolyline(drv, pts, 2, lineColor);
|
||||
lineCount++;
|
||||
}
|
||||
logMsg(" Drew %d lines\n", lineCount);
|
||||
}
|
||||
|
||||
// Demo 4: Screen-to-screen blit test
|
||||
if (info.hasBitBlt) {
|
||||
logMsg("Demo 4: Screen-to-screen blit\n");
|
||||
|
||||
WdrvBitBltParamsT bp;
|
||||
memset(&bp, 0, sizeof(bp));
|
||||
bp.srcX = 0;
|
||||
bp.srcY = 0;
|
||||
bp.dstX = screenW / 2;
|
||||
bp.dstY = screenH / 2;
|
||||
bp.width = screenW / 4;
|
||||
bp.height = screenH / 4;
|
||||
bp.rop3 = SRCCOPY;
|
||||
wdrvBitBlt(drv, &bp);
|
||||
logMsg(" Screen blit done\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void setupPalette(WdrvHandleT drv)
|
||||
{
|
||||
// The driver sets up its own palette during Enable (via VBE 4F09)
|
||||
// and stores an internal color table that RealizeObject uses for
|
||||
// color matching. We leave the palette as-is so the DAC and the
|
||||
// internal table stay in sync. RealizeObject will find the best
|
||||
// matching index, and the DAC will display the correct color.
|
||||
(void)drv;
|
||||
}
|
||||
|
||||
40
dosbox-x.conf
Normal file
40
dosbox-x.conf
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# DOSBox-X configuration for win31drv development
|
||||
# S3 Trio64 SVGA with VESA support
|
||||
|
||||
[sdl]
|
||||
output = opengl
|
||||
windowresolution = 1024x768
|
||||
|
||||
[dosbox]
|
||||
machine = svga_s3trio64
|
||||
memsize = 64
|
||||
quit warning = false
|
||||
|
||||
[cpu]
|
||||
core = normal
|
||||
cputype = pentium
|
||||
cycles = max
|
||||
|
||||
[render]
|
||||
aspect = true
|
||||
scaler = none
|
||||
|
||||
[video]
|
||||
vmemsize = 8
|
||||
vmemsizekb = 0
|
||||
vesa oldvbe = false
|
||||
vesa oldvbe10 = false
|
||||
|
||||
[dos]
|
||||
umb = true
|
||||
xms = true
|
||||
ems = true
|
||||
|
||||
[autoexec]
|
||||
@echo off
|
||||
mount c /home/scott/claude/windriver
|
||||
c:
|
||||
cd bin
|
||||
DEMO.EXE -d VGA.DRV
|
||||
rem exit
|
||||
|
||||
BIN
drivers/ET4000.DRV
(Stored with Git LFS)
Normal file
BIN
drivers/ET4000.DRV
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
drivers/S3TRIO.DRV
(Stored with Git LFS)
Normal file
BIN
drivers/S3TRIO.DRV
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
drivers/VBESVGA.DRV
(Stored with Git LFS)
Normal file
BIN
drivers/VBESVGA.DRV
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
drivers/VGA.DRV
(Stored with Git LFS)
Normal file
BIN
drivers/VGA.DRV
(Stored with Git LFS)
Normal file
Binary file not shown.
1
tools/TEST.BAT
Normal file
1
tools/TEST.BAT
Normal file
|
|
@ -0,0 +1 @@
|
|||
demo.exe -d VBESVGA.DRV
|
||||
BIN
tools/cwsdpmi.zip
(Stored with Git LFS)
Normal file
BIN
tools/cwsdpmi.zip
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tools/lib/libfl.so.2
(Stored with Git LFS)
Normal file
BIN
tools/lib/libfl.so.2
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
tools/lib/libfl.so.2.0.0
(Stored with Git LFS)
Normal file
BIN
tools/lib/libfl.so.2.0.0
(Stored with Git LFS)
Normal file
Binary file not shown.
1
vbesvga.drv
Submodule
1
vbesvga.drv
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 2da3782d3f17531f32bbd9d67a0c242656bb7b07
|
||||
51
win31drv/Makefile
Normal file
51
win31drv/Makefile
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# ============================================================================
|
||||
# Makefile for win31drv - Windows 3.x display driver library for DJGPP/DOS
|
||||
# ============================================================================
|
||||
|
||||
DJGPP_PREFIX ?= $(HOME)/djgpp/djgpp
|
||||
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
AR = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ar
|
||||
CFLAGS = -Wall -Wextra -O2 -std=gnu99 -I.
|
||||
|
||||
# DOSBox-X's S3 Trio64 emulation corrupts specific memory addresses
|
||||
# during 16-bit driver calls via thunkCall16. With -O2 GCSE enabled,
|
||||
# the code layout places stack locals and register spills at addresses
|
||||
# that overlap the corruption targets, causing wrong values in drawing
|
||||
# parameters. Disabling GCSE for windrv.c changes the layout enough
|
||||
# to avoid the overlap. Only windrv.c is affected (it has the drawing
|
||||
# functions that call thunkCall16 with interleaved parameter setup).
|
||||
WINDRV_CFLAGS = $(CFLAGS) -fno-gcse
|
||||
|
||||
# DJGPP binutils need libfl.so.2 which may not be installed system-wide
|
||||
export LD_LIBRARY_PATH := $(realpath ../tools/lib):$(LD_LIBRARY_PATH)
|
||||
|
||||
OBJDIR = obj
|
||||
|
||||
SRCS = log.c neload.c thunk.c winstub.c windrv.c
|
||||
OBJS = $(addprefix $(OBJDIR)/,$(SRCS:.c=.o))
|
||||
LIB = libwindrv.a
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(LIB)
|
||||
|
||||
$(LIB): $(OBJS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/log.o: log.c log.h
|
||||
$(OBJDIR)/neload.o: neload.c neload.h neformat.h wintypes.h log.h
|
||||
$(OBJDIR)/thunk.o: thunk.c thunk.h wintypes.h log.h
|
||||
$(OBJDIR)/winstub.o: winstub.c winstub.h thunk.h wintypes.h log.h
|
||||
$(OBJDIR)/windrv.o: windrv.c windrv.h wintypes.h winddi.h neformat.h neload.h thunk.h winstub.h log.h
|
||||
$(CC) $(WINDRV_CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
-rm -rf $(OBJDIR) $(LIB)
|
||||
83
win31drv/log.c
Normal file
83
win31drv/log.c
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// ============================================================================
|
||||
// log.c - Logging to file
|
||||
// ============================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static FILE *gLogFile = NULL;
|
||||
static LogPreIoFuncT gPreIoHook = NULL;
|
||||
|
||||
|
||||
void logInit(const char *filename)
|
||||
{
|
||||
if (filename) {
|
||||
gLogFile = fopen(filename, "w");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void logErr(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
logErrV(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
void logErrV(const char *fmt, va_list ap)
|
||||
{
|
||||
if (gLogFile) {
|
||||
if (gPreIoHook) {
|
||||
gPreIoHook();
|
||||
}
|
||||
vfprintf(gLogFile, fmt, ap);
|
||||
fflush(gLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FILE *logGetFile(void)
|
||||
{
|
||||
return gLogFile;
|
||||
}
|
||||
|
||||
|
||||
void logMsg(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
logMsgV(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
void logMsgV(const char *fmt, va_list ap)
|
||||
{
|
||||
if (gLogFile) {
|
||||
if (gPreIoHook) {
|
||||
gPreIoHook();
|
||||
}
|
||||
vfprintf(gLogFile, fmt, ap);
|
||||
fflush(gLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void logSetPreIoHook(LogPreIoFuncT func)
|
||||
{
|
||||
gPreIoHook = func;
|
||||
}
|
||||
|
||||
|
||||
void logShutdown(void)
|
||||
{
|
||||
if (gLogFile) {
|
||||
fflush(gLogFile);
|
||||
fclose(gLogFile);
|
||||
gLogFile = NULL;
|
||||
}
|
||||
}
|
||||
37
win31drv/log.h
Normal file
37
win31drv/log.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
// ============================================================================
|
||||
// Logging layer - writes to a log file
|
||||
//
|
||||
// Call logInit() with a filename to enable logging.
|
||||
// ============================================================================
|
||||
|
||||
// Initialize logging. If filename is non-NULL, opens it for writing.
|
||||
// Can be called before any other log function. If not called, output
|
||||
// goes only to stdout/stderr.
|
||||
void logInit(const char *filename);
|
||||
|
||||
// Shut down logging, flush and close the log file.
|
||||
void logShutdown(void);
|
||||
|
||||
// Write to stdout and the log file (like printf).
|
||||
void logMsg(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
// Write to stderr and the log file (like fprintf(stderr, ...)).
|
||||
void logErr(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
// Varargs versions for use by other wrappers.
|
||||
void logMsgV(const char *fmt, va_list ap);
|
||||
void logErrV(const char *fmt, va_list ap);
|
||||
|
||||
// Get the underlying log file handle (or NULL if not initialized).
|
||||
FILE *logGetFile(void);
|
||||
|
||||
// Register a pre-I/O hook called before every file write.
|
||||
typedef void (*LogPreIoFuncT)(void);
|
||||
void logSetPreIoHook(LogPreIoFuncT func);
|
||||
|
||||
#endif // LOG_H
|
||||
214
win31drv/neformat.h
Normal file
214
win31drv/neformat.h
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#ifndef NEFORMAT_H
|
||||
#define NEFORMAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ============================================================================
|
||||
// MZ (DOS) executable header - precedes the NE header
|
||||
// ============================================================================
|
||||
|
||||
#define MZ_SIGNATURE 0x5A4D // 'MZ'
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t signature; // 0x00: 'MZ'
|
||||
uint16_t lastPageBytes; // 0x02: bytes on last page
|
||||
uint16_t pageCount; // 0x04: pages in file (512 bytes each)
|
||||
uint16_t relocationCount; // 0x06: relocation entries
|
||||
uint16_t headerParagraphs;// 0x08: header size in paragraphs
|
||||
uint16_t minAlloc; // 0x0A: minimum extra paragraphs
|
||||
uint16_t maxAlloc; // 0x0C: maximum extra paragraphs
|
||||
uint16_t initSS; // 0x0E: initial SS
|
||||
uint16_t initSP; // 0x10: initial SP
|
||||
uint16_t checksum; // 0x12: checksum
|
||||
uint16_t initIP; // 0x14: initial IP
|
||||
uint16_t initCS; // 0x16: initial CS
|
||||
uint16_t relocationOff; // 0x18: relocation table offset
|
||||
uint16_t overlayNum; // 0x1A: overlay number
|
||||
uint16_t reserved1[4]; // 0x1C: reserved
|
||||
uint16_t oemId; // 0x24: OEM identifier
|
||||
uint16_t oemInfo; // 0x26: OEM information
|
||||
uint16_t reserved2[10]; // 0x28: reserved
|
||||
uint32_t neHeaderOffset; // 0x3C: offset to NE header
|
||||
} MzHeaderT;
|
||||
|
||||
// ============================================================================
|
||||
// NE (New Executable) header
|
||||
// ============================================================================
|
||||
|
||||
#define NE_SIGNATURE 0x454E // 'NE'
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t signature; // 0x00: 'NE'
|
||||
uint8_t linkerMajor; // 0x02: linker version
|
||||
uint8_t linkerMinor; // 0x03: linker revision
|
||||
uint16_t entryTableOffset; // 0x04: offset to entry table (from NE header)
|
||||
uint16_t entryTableSize; // 0x06: size of entry table
|
||||
uint32_t fileCrc; // 0x08: file CRC
|
||||
uint16_t moduleFlags; // 0x0C: module flags
|
||||
uint16_t autoDataSegIndex; // 0x0E: auto data segment index (1-based)
|
||||
uint16_t initialHeapSize; // 0x10: initial heap size
|
||||
uint16_t initialStackSize; // 0x12: initial stack size
|
||||
uint16_t entryPointIP; // 0x14: CS:IP entry point (IP)
|
||||
uint16_t entryPointCS; // 0x16: CS:IP entry point (CS segment index)
|
||||
uint16_t initialSP; // 0x18: SS:SP initial stack (SP)
|
||||
uint16_t initialSS; // 0x1A: SS:SP initial stack (SS segment index)
|
||||
uint16_t segmentCount; // 0x1C: number of segment table entries
|
||||
uint16_t moduleRefCount; // 0x1E: number of module reference table entries
|
||||
uint16_t nonResNameSize; // 0x20: size of non-resident name table
|
||||
uint16_t segmentTableOffset; // 0x22: offset to segment table (from NE header)
|
||||
uint16_t resourceTableOffset; // 0x24: offset to resource table (from NE header)
|
||||
uint16_t resNameTableOffset; // 0x26: offset to resident name table (from NE)
|
||||
uint16_t modRefTableOffset; // 0x28: offset to module reference table (from NE)
|
||||
uint16_t importNameTableOffset; // 0x2A: offset to imported names table (from NE)
|
||||
uint32_t nonResNameTableFileOffset; // 0x2C: file offset of non-resident name table
|
||||
uint16_t movableEntryCount; // 0x30: number of movable entry points
|
||||
uint16_t sectorAlignShift; // 0x32: sector alignment shift count
|
||||
uint16_t resourceSegCount; // 0x34: number of resource segments
|
||||
uint8_t targetOS; // 0x36: target operating system
|
||||
uint8_t otherFlags; // 0x37: additional flags
|
||||
uint16_t gangLoadAreaOffset; // 0x38: offset to gang-load area
|
||||
uint16_t gangLoadAreaSize; // 0x3A: size of gang-load area
|
||||
uint16_t swapAreaSize; // 0x3C: minimum code swap area size
|
||||
uint16_t expectedWinVer; // 0x3E: expected Windows version
|
||||
} NeHeaderT;
|
||||
|
||||
// NE module flags (moduleFlags field)
|
||||
#define NE_FFLAGS_SINGLEDATA 0x0001 // Single shared DGROUP
|
||||
#define NE_FFLAGS_MULTIPLEDATA 0x0002 // Multiple DGROUP (DLL with per-instance data)
|
||||
#define NE_FFLAGS_GLOBALINIT 0x0004 // Global initialization
|
||||
#define NE_FFLAGS_PROTMODE 0x0008 // Protected mode only
|
||||
#define NE_FFLAGS_8086 0x0010 // 8086 instructions
|
||||
#define NE_FFLAGS_80286 0x0020 // 80286 instructions
|
||||
#define NE_FFLAGS_80386 0x0040 // 80386 instructions
|
||||
#define NE_FFLAGS_80x87 0x0080 // uses 80x87
|
||||
#define NE_FFLAGS_FULLSCREEN 0x0100 // full-screen application (not a DLL)
|
||||
#define NE_FFLAGS_DLL 0x8000 // DLL or driver (not a task)
|
||||
|
||||
// NE target OS values
|
||||
#define NE_OS_UNKNOWN 0x00
|
||||
#define NE_OS_OS2 0x01
|
||||
#define NE_OS_WINDOWS 0x02
|
||||
#define NE_OS_DOS4 0x03
|
||||
#define NE_OS_WIN386 0x04
|
||||
|
||||
// ============================================================================
|
||||
// NE segment table entry
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t fileSectorOffset; // Logical sector offset in file (0 = no data)
|
||||
uint16_t fileLength; // Length of segment in file (0 = 64K)
|
||||
uint16_t flags; // Segment flags
|
||||
uint16_t minAllocSize; // Minimum allocation size (0 = 64K)
|
||||
} NeSegEntryT;
|
||||
|
||||
// Segment flags
|
||||
#define NE_SEGF_DATA 0x0001 // Data segment (0 = code)
|
||||
#define NE_SEGF_ALLOCATED 0x0002 // Loader has allocated memory
|
||||
#define NE_SEGF_LOADED 0x0004 // Segment is loaded
|
||||
#define NE_SEGF_MOVEABLE 0x0010 // Moveable segment
|
||||
#define NE_SEGF_SHAREABLE 0x0020 // Shareable segment
|
||||
#define NE_SEGF_PRELOAD 0x0040 // Preload segment
|
||||
#define NE_SEGF_READONLY 0x0080 // Read-only (code) or execute-only (data)
|
||||
#define NE_SEGF_HASRELOC 0x0100 // Has relocation data
|
||||
#define NE_SEGF_DISCARD 0x1000 // Discardable
|
||||
|
||||
// ============================================================================
|
||||
// NE relocation record
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t srcType; // Source (fixup) type
|
||||
uint8_t flags; // Relocation flags
|
||||
uint16_t srcOffset; // Offset within segment of the fixup location
|
||||
uint16_t target1; // Module index (1-based) or segment number
|
||||
uint16_t target2; // Ordinal/offset or offset within segment
|
||||
} NeRelocT;
|
||||
|
||||
// Relocation source types (srcType field)
|
||||
#define NE_RELOC_LOBYTE 0x00 // Low byte fixup
|
||||
#define NE_RELOC_SEGMENT 0x02 // 16-bit segment fixup
|
||||
#define NE_RELOC_FAR_ADDR 0x03 // 32-bit far pointer (seg:off) fixup
|
||||
#define NE_RELOC_OFFSET 0x05 // 16-bit offset fixup
|
||||
#define NE_RELOC_FAR48_ADDR 0x0B // 48-bit far pointer fixup
|
||||
#define NE_RELOC_OFFSET32 0x0D // 32-bit offset fixup
|
||||
|
||||
// Relocation target flags (flags field)
|
||||
#define NE_RELF_INTERNALREF 0x00 // Internal reference
|
||||
#define NE_RELF_IMPORTORD 0x01 // Import by ordinal
|
||||
#define NE_RELF_IMPORTNAME 0x02 // Import by name
|
||||
#define NE_RELF_OSFIXUP 0x03 // OS fixup
|
||||
#define NE_RELF_TARGET_MASK 0x03 // Mask for target type
|
||||
#define NE_RELF_ADDITIVE 0x04 // Additive fixup (don't zero target first)
|
||||
|
||||
// ============================================================================
|
||||
// NE entry table structures
|
||||
// ============================================================================
|
||||
|
||||
// Entry table is a series of bundles. Each bundle starts with:
|
||||
// BYTE count - number of entries in this bundle (0 = end of table)
|
||||
// BYTE indicator - 0x00 = empty, 0xFF = moveable, else fixed segment number
|
||||
|
||||
// Fixed entry (indicator = segment number 1-254)
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t flags; // Entry flags
|
||||
uint16_t offset; // Offset within segment
|
||||
} NeFixedEntryT;
|
||||
|
||||
// Moveable entry (indicator = 0xFF)
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t flags; // Entry flags
|
||||
uint16_t int3fh; // INT 3Fh instruction (0xCD3F)
|
||||
uint8_t segIndex; // Segment number (1-based)
|
||||
uint16_t offset; // Offset within segment
|
||||
} NeMoveableEntryT;
|
||||
|
||||
// Entry flags
|
||||
#define NE_ENTRY_EXPORTED 0x01 // Entry is exported
|
||||
#define NE_ENTRY_SHDATA 0x02 // Entry uses shared data segment
|
||||
|
||||
// ============================================================================
|
||||
// Display driver ordinal numbers (standard DDI exports)
|
||||
// ============================================================================
|
||||
|
||||
#define DDI_ORD_BITBLT 1
|
||||
#define DDI_ORD_COLORINFO 2
|
||||
#define DDI_ORD_CONTROL 3
|
||||
#define DDI_ORD_DISABLE 4
|
||||
#define DDI_ORD_ENABLE 5
|
||||
#define DDI_ORD_ENUMDFFONTS 6
|
||||
#define DDI_ORD_ENUMOBJ 7
|
||||
#define DDI_ORD_OUTPUT 8
|
||||
#define DDI_ORD_PIXEL 9
|
||||
#define DDI_ORD_REALIZEOBJECT 10
|
||||
#define DDI_ORD_STRBLT 11
|
||||
#define DDI_ORD_SCANLR 12
|
||||
#define DDI_ORD_DEVICEMODE 13
|
||||
#define DDI_ORD_EXTTEXTOUT 14
|
||||
#define DDI_ORD_GETCHARWIDTH 15
|
||||
#define DDI_ORD_DEVICEBITMAP 16
|
||||
#define DDI_ORD_FASTBORDER 17
|
||||
#define DDI_ORD_SETATTRIBUTE 18
|
||||
#define DDI_ORD_DIBTODEVICE 19
|
||||
#define DDI_ORD_CREATEBITMAP 20
|
||||
#define DDI_ORD_DELETEBITMAP 21
|
||||
#define DDI_ORD_SELECTBITMAP 22
|
||||
#define DDI_ORD_BITMAPBITS 23
|
||||
#define DDI_ORD_RECLIP 24
|
||||
#define DDI_ORD_GETPALETTE 25
|
||||
#define DDI_ORD_SETPALETTE 26
|
||||
#define DDI_ORD_SETPALETTETRANS 27
|
||||
#define DDI_ORD_UPDATECOLORS 28
|
||||
#define DDI_ORD_STRETCHBLT 29
|
||||
#define DDI_ORD_STRETCHDIBITS 30
|
||||
#define DDI_ORD_SELECTPALETTE 31
|
||||
#define DDI_ORD_INQUIRE 101
|
||||
#define DDI_ORD_SETCURSOR 102
|
||||
#define DDI_ORD_MOVECURSOR 103
|
||||
#define DDI_ORD_CHECKCRSR 104
|
||||
#define DDI_ORD_GETDRIVERRESID 450
|
||||
|
||||
// Maximum DDI ordinal we track
|
||||
#define DDI_MAX_ORDINAL 500
|
||||
|
||||
#endif // NEFORMAT_H
|
||||
962
win31drv/neload.c
Normal file
962
win31drv/neload.c
Normal file
|
|
@ -0,0 +1,962 @@
|
|||
// ============================================================================
|
||||
// neload.c - NE (New Executable) format loader
|
||||
//
|
||||
// Loads Windows 3.x 16-bit DLLs/drivers into protected mode memory
|
||||
// using DPMI to allocate LDT descriptors for 16-bit code/data segments.
|
||||
// ============================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
#include <dpmi.h>
|
||||
#include <go32.h>
|
||||
#include <sys/movedata.h>
|
||||
#include <sys/nearptr.h>
|
||||
|
||||
#include "neload.h"
|
||||
#include "wintypes.h"
|
||||
#include "log.h"
|
||||
|
||||
// Forward declarations
|
||||
static bool readHeaders(NeModuleT *mod, FILE *fp);
|
||||
static bool loadSegments(NeModuleT *mod, FILE *fp, ImportResolverT resolver);
|
||||
static bool allocateSegment(NeModuleT *mod, int segIdx, uint32_t size, bool isCode);
|
||||
static bool loadSegmentData(NeModuleT *mod, int segIdx, FILE *fp, uint32_t fileOffset, uint32_t fileSize);
|
||||
static bool processRelocations(NeModuleT *mod, int segIdx, FILE *fp, ImportResolverT resolver);
|
||||
static bool parseEntryTable(NeModuleT *mod, FILE *fp);
|
||||
static bool parseModuleReferences(NeModuleT *mod, FILE *fp);
|
||||
static bool parseResidentNames(NeModuleT *mod, FILE *fp);
|
||||
static void freeSegment(LoadedSegT *seg);
|
||||
static uint16_t makeDescriptor16(uint32_t base, uint32_t limit, bool isCode);
|
||||
static void dbgPrint(const char *fmt, ...);
|
||||
|
||||
static bool gDebug = false;
|
||||
|
||||
|
||||
static void dbgPrint(const char *fmt, ...)
|
||||
{
|
||||
if (!gDebug) {
|
||||
return;
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
logErrV(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
bool neLoadModule(NeModuleT *mod, const char *filePath, ImportResolverT resolver)
|
||||
{
|
||||
memset(mod, 0, sizeof(NeModuleT));
|
||||
|
||||
FILE *fp = fopen(filePath, "rb");
|
||||
if (!fp) {
|
||||
logErr("neload: cannot open '%s'\n", filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
|
||||
// Step 1: Read and validate MZ + NE headers
|
||||
if (!readHeaders(mod, fp)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Step 2: Parse module reference table (imported module names)
|
||||
if (!parseModuleReferences(mod, fp)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Step 3: Parse resident name table (module name + exports by name)
|
||||
if (!parseResidentNames(mod, fp)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Step 4: Parse entry table (export ordinals -> segment:offset)
|
||||
if (!parseEntryTable(mod, fp)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Step 5: Load segments, apply relocations
|
||||
if (!loadSegments(mod, fp, resolver)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Resolve auto data segment selector
|
||||
if (mod->neHeader.autoDataSegIndex > 0 &&
|
||||
mod->neHeader.autoDataSegIndex <= mod->segmentCount) {
|
||||
mod->autoDataSel = mod->segments[mod->neHeader.autoDataSegIndex - 1].selector;
|
||||
}
|
||||
|
||||
mod->loaded = true;
|
||||
ok = true;
|
||||
|
||||
done:
|
||||
fclose(fp);
|
||||
if (!ok) {
|
||||
neUnloadModule(mod);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
void neUnloadModule(NeModuleT *mod)
|
||||
{
|
||||
for (uint16_t i = 0; i < mod->segmentCount; i++) {
|
||||
freeSegment(&mod->segments[i]);
|
||||
}
|
||||
mod->segmentCount = 0;
|
||||
mod->loaded = false;
|
||||
}
|
||||
|
||||
|
||||
bool neLookupExport(const NeModuleT *mod, uint16_t ordinal, uint16_t *seg, uint16_t *off, uint16_t *sel)
|
||||
{
|
||||
if (ordinal == 0 || ordinal >= NE_MAX_EXPORTS) {
|
||||
return false;
|
||||
}
|
||||
if (mod->exports[ordinal].segIndex == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExportEntryT *e = (ExportEntryT *)&mod->exports[ordinal];
|
||||
uint16_t sIdx = e->segIndex - 1;
|
||||
|
||||
if (sIdx >= mod->segmentCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (seg) {
|
||||
*seg = e->segIndex;
|
||||
}
|
||||
if (off) {
|
||||
*off = e->offset;
|
||||
}
|
||||
if (sel) {
|
||||
*sel = mod->segments[sIdx].selector;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint16_t neLookupExportByName(const NeModuleT *mod, const char *name, const char *filePath)
|
||||
{
|
||||
// We need to re-read the resident name table from the file to search by name.
|
||||
// The first entry is the module name; subsequent entries are exports.
|
||||
FILE *fp = fopen(filePath, "rb");
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t tableOff = mod->neHeaderFileOffset + mod->neHeader.resNameTableOffset;
|
||||
fseek(fp, tableOff, SEEK_SET);
|
||||
|
||||
uint16_t ordinal = 0;
|
||||
while (1) {
|
||||
uint8_t nameLen;
|
||||
if (fread(&nameLen, 1, 1, fp) != 1 || nameLen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char entryName[256];
|
||||
if (fread(entryName, 1, nameLen, fp) != nameLen) {
|
||||
break;
|
||||
}
|
||||
entryName[nameLen] = '\0';
|
||||
|
||||
uint16_t ord;
|
||||
if (fread(&ord, 2, 1, fp) != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcasecmp(entryName, name) == 0) {
|
||||
ordinal = ord;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
|
||||
void neDumpModule(const NeModuleT *mod)
|
||||
{
|
||||
logErr("=== NE Module: %s ===\n", mod->moduleName);
|
||||
logErr(" Segments: %u\n", mod->segmentCount);
|
||||
logErr(" Auto data segment: %u\n", mod->neHeader.autoDataSegIndex);
|
||||
logErr(" Module flags: 0x%04X", mod->neHeader.moduleFlags);
|
||||
if (mod->neHeader.moduleFlags & NE_FFLAGS_DLL) {
|
||||
logErr(" (DLL)");
|
||||
}
|
||||
logErr("\n");
|
||||
logErr(" Target OS: 0x%02X\n", mod->neHeader.targetOS);
|
||||
logErr(" Expected Windows version: %u.%u\n",
|
||||
mod->neHeader.expectedWinVer >> 8,
|
||||
mod->neHeader.expectedWinVer & 0xFF);
|
||||
|
||||
logErr(" Segments:\n");
|
||||
for (uint16_t i = 0; i < mod->segmentCount; i++) {
|
||||
LoadedSegT *s = (LoadedSegT *)&mod->segments[i];
|
||||
logErr(" [%u] %s sel=0x%04X base=0x%08" PRIX32 " size=%" PRIu32,
|
||||
i + 1,
|
||||
s->isCode ? "CODE" : "DATA",
|
||||
s->selector,
|
||||
s->linearAddr,
|
||||
s->size);
|
||||
if (s->flags & NE_SEGF_PRELOAD) {
|
||||
logErr(" PRELOAD");
|
||||
}
|
||||
if (s->flags & NE_SEGF_MOVEABLE) {
|
||||
logErr(" MOVEABLE");
|
||||
}
|
||||
logErr("\n");
|
||||
}
|
||||
|
||||
logErr(" Module references:\n");
|
||||
for (uint16_t i = 0; i < mod->modRefCount; i++) {
|
||||
logErr(" [%u] %s\n", i + 1, mod->modRefNames[i]);
|
||||
}
|
||||
|
||||
logErr(" Exports:\n");
|
||||
for (uint16_t i = 1; i < NE_MAX_EXPORTS; i++) {
|
||||
if (mod->exports[i].segIndex != 0) {
|
||||
logErr(" ord %u -> seg %u : 0x%04X (sel 0x%04X)\n",
|
||||
i,
|
||||
mod->exports[i].segIndex,
|
||||
mod->exports[i].offset,
|
||||
mod->exports[i].selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Internal implementation
|
||||
// ============================================================================
|
||||
|
||||
static bool readHeaders(NeModuleT *mod, FILE *fp)
|
||||
{
|
||||
// Read MZ header
|
||||
MzHeaderT mz;
|
||||
if (fread(&mz, sizeof(mz), 1, fp) != 1) {
|
||||
logErr("neload: failed to read MZ header\n");
|
||||
return false;
|
||||
}
|
||||
if (mz.signature != MZ_SIGNATURE) {
|
||||
logErr("neload: not an MZ executable (sig=0x%04X)\n", mz.signature);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read NE header
|
||||
mod->neHeaderFileOffset = mz.neHeaderOffset;
|
||||
fseek(fp, mz.neHeaderOffset, SEEK_SET);
|
||||
|
||||
if (fread(&mod->neHeader, sizeof(NeHeaderT), 1, fp) != 1) {
|
||||
logErr("neload: failed to read NE header\n");
|
||||
return false;
|
||||
}
|
||||
if (mod->neHeader.signature != NE_SIGNATURE) {
|
||||
logErr("neload: not an NE executable (sig=0x%04X)\n",
|
||||
mod->neHeader.signature);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate
|
||||
if (mod->neHeader.segmentCount > NE_MAX_SEGMENTS) {
|
||||
logErr("neload: too many segments (%u, max %u)\n",
|
||||
mod->neHeader.segmentCount, NE_MAX_SEGMENTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
mod->segmentCount = mod->neHeader.segmentCount;
|
||||
mod->sectorAlignShift = mod->neHeader.sectorAlignShift;
|
||||
if (mod->sectorAlignShift == 0) {
|
||||
mod->sectorAlignShift = 9; // Default: 512-byte sectors
|
||||
}
|
||||
|
||||
dbgPrint("neload: NE header at 0x%08" PRIX32 ", %u segments, %u align shift\n",
|
||||
mod->neHeaderFileOffset, mod->segmentCount, mod->sectorAlignShift);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool parseModuleReferences(NeModuleT *mod, FILE *fp)
|
||||
{
|
||||
if (mod->neHeader.moduleRefCount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mod->modRefCount = mod->neHeader.moduleRefCount;
|
||||
if (mod->modRefCount > NE_MAX_MODREFS) {
|
||||
logErr("neload: too many module references (%u, max %u)\n",
|
||||
mod->modRefCount, NE_MAX_MODREFS);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read module reference table (array of offsets into imported name table)
|
||||
uint32_t modRefTableOff = mod->neHeaderFileOffset + mod->neHeader.modRefTableOffset;
|
||||
fseek(fp, modRefTableOff, SEEK_SET);
|
||||
|
||||
uint16_t nameOffsets[NE_MAX_MODREFS];
|
||||
if (fread(nameOffsets, 2, mod->modRefCount, fp) != mod->modRefCount) {
|
||||
logErr("neload: failed to read module reference table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resolve each offset to a name from the imported name table
|
||||
uint32_t importNameTableOff = mod->neHeaderFileOffset + mod->neHeader.importNameTableOffset;
|
||||
|
||||
for (uint16_t i = 0; i < mod->modRefCount; i++) {
|
||||
uint32_t nameOff = importNameTableOff + nameOffsets[i];
|
||||
fseek(fp, nameOff, SEEK_SET);
|
||||
|
||||
uint8_t nameLen;
|
||||
if (fread(&nameLen, 1, 1, fp) != 1) {
|
||||
logErr("neload: failed to read module name length\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameLen > 31) {
|
||||
nameLen = 31;
|
||||
}
|
||||
if (fread(mod->modRefNames[i], 1, nameLen, fp) != nameLen) {
|
||||
logErr("neload: failed to read module name\n");
|
||||
return false;
|
||||
}
|
||||
mod->modRefNames[i][nameLen] = '\0';
|
||||
|
||||
dbgPrint("neload: module ref [%u] = '%s'\n", i + 1, mod->modRefNames[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool parseResidentNames(NeModuleT *mod, FILE *fp)
|
||||
{
|
||||
uint32_t tableOff = mod->neHeaderFileOffset + mod->neHeader.resNameTableOffset;
|
||||
fseek(fp, tableOff, SEEK_SET);
|
||||
|
||||
bool firstEntry = true;
|
||||
while (1) {
|
||||
uint8_t nameLen;
|
||||
if (fread(&nameLen, 1, 1, fp) != 1 || nameLen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char name[256];
|
||||
if (fread(name, 1, nameLen, fp) != nameLen) {
|
||||
break;
|
||||
}
|
||||
name[nameLen] = '\0';
|
||||
|
||||
uint16_t ordinal;
|
||||
if (fread(&ordinal, 2, 1, fp) != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (firstEntry) {
|
||||
// First entry is the module name (ordinal 0)
|
||||
memcpy(mod->moduleName, name, sizeof(mod->moduleName) - 1);
|
||||
mod->moduleName[sizeof(mod->moduleName) - 1] = '\0';
|
||||
dbgPrint("neload: module name = '%s'\n", mod->moduleName);
|
||||
firstEntry = false;
|
||||
} else {
|
||||
dbgPrint("neload: resident name '%s' = ordinal %u\n", name, ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool parseEntryTable(NeModuleT *mod, FILE *fp)
|
||||
{
|
||||
uint32_t tableOff = mod->neHeaderFileOffset + mod->neHeader.entryTableOffset;
|
||||
uint32_t tableEnd = tableOff + mod->neHeader.entryTableSize;
|
||||
|
||||
fseek(fp, tableOff, SEEK_SET);
|
||||
|
||||
uint16_t currentOrdinal = 1;
|
||||
|
||||
while (ftell(fp) < (long)tableEnd) {
|
||||
uint8_t bundleCount;
|
||||
uint8_t indicator;
|
||||
|
||||
if (fread(&bundleCount, 1, 1, fp) != 1 || bundleCount == 0) {
|
||||
break; // End of entry table
|
||||
}
|
||||
if (fread(&indicator, 1, 1, fp) != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (indicator == 0x00) {
|
||||
// Empty bundle - skip these ordinals
|
||||
currentOrdinal += bundleCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < bundleCount; i++) {
|
||||
if (currentOrdinal >= NE_MAX_EXPORTS) {
|
||||
logErr("neload: export ordinal %u exceeds max\n", currentOrdinal);
|
||||
currentOrdinal++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (indicator == 0xFF) {
|
||||
// Moveable segment entry
|
||||
NeMoveableEntryT entry;
|
||||
if (fread(&entry, sizeof(entry), 1, fp) != 1) {
|
||||
return false;
|
||||
}
|
||||
mod->exports[currentOrdinal].segIndex = entry.segIndex;
|
||||
mod->exports[currentOrdinal].offset = entry.offset;
|
||||
mod->exports[currentOrdinal].flags = entry.flags;
|
||||
mod->exportCount++;
|
||||
|
||||
dbgPrint("neload: entry ord %u -> seg %u : 0x%04X (moveable)\n",
|
||||
currentOrdinal, entry.segIndex, entry.offset);
|
||||
} else {
|
||||
// Fixed segment entry (indicator = segment number)
|
||||
NeFixedEntryT entry;
|
||||
if (fread(&entry, sizeof(entry), 1, fp) != 1) {
|
||||
return false;
|
||||
}
|
||||
mod->exports[currentOrdinal].segIndex = indicator;
|
||||
mod->exports[currentOrdinal].offset = entry.offset;
|
||||
mod->exports[currentOrdinal].flags = entry.flags;
|
||||
mod->exportCount++;
|
||||
|
||||
dbgPrint("neload: entry ord %u -> seg %u : 0x%04X (fixed)\n",
|
||||
currentOrdinal, indicator, entry.offset);
|
||||
}
|
||||
|
||||
currentOrdinal++;
|
||||
}
|
||||
}
|
||||
|
||||
dbgPrint("neload: %u export entries parsed\n", mod->exportCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool loadSegments(NeModuleT *mod, FILE *fp, ImportResolverT resolver)
|
||||
{
|
||||
// Read segment table
|
||||
uint32_t segTableOff = mod->neHeaderFileOffset + mod->neHeader.segmentTableOffset;
|
||||
fseek(fp, segTableOff, SEEK_SET);
|
||||
|
||||
NeSegEntryT segTable[NE_MAX_SEGMENTS];
|
||||
if (fread(segTable, sizeof(NeSegEntryT), mod->segmentCount, fp) != mod->segmentCount) {
|
||||
logErr("neload: failed to read segment table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load each segment
|
||||
for (uint16_t i = 0; i < mod->segmentCount; i++) {
|
||||
NeSegEntryT *se = &segTable[i];
|
||||
bool isCode = !(se->flags & NE_SEGF_DATA);
|
||||
|
||||
// Determine segment size
|
||||
uint32_t fileLen = se->fileLength;
|
||||
if (fileLen == 0 && se->fileSectorOffset != 0) {
|
||||
fileLen = 0x10000; // 64K
|
||||
}
|
||||
|
||||
uint32_t allocSize = se->minAllocSize;
|
||||
if (allocSize == 0) {
|
||||
allocSize = 0x10000; // 64K
|
||||
}
|
||||
if (allocSize < fileLen) {
|
||||
allocSize = fileLen;
|
||||
}
|
||||
|
||||
// Allocate memory and create descriptor
|
||||
if (!allocateSegment(mod, i, allocSize, isCode)) {
|
||||
logErr("neload: failed to allocate segment %u\n", i + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
mod->segments[i].flags = se->flags;
|
||||
mod->segments[i].fileSize = fileLen;
|
||||
|
||||
// Load data from file
|
||||
if (se->fileSectorOffset != 0 && fileLen > 0) {
|
||||
uint32_t fileOffset = (uint32_t)se->fileSectorOffset << mod->sectorAlignShift;
|
||||
if (!loadSegmentData(mod, i, fp, fileOffset, fileLen)) {
|
||||
logErr("neload: failed to load segment %u data\n", i + 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dbgPrint("neload: loaded seg %u: %s size=%" PRIu32 " filelen=%" PRIu32 " sel=0x%04X base=0x%08" PRIX32 "\n",
|
||||
i + 1, isCode ? "CODE" : "DATA",
|
||||
allocSize, fileLen,
|
||||
mod->segments[i].selector,
|
||||
mod->segments[i].linearAddr);
|
||||
}
|
||||
|
||||
// Resolve export selectors now that segments are loaded
|
||||
for (uint16_t i = 1; i < NE_MAX_EXPORTS; i++) {
|
||||
if (mod->exports[i].segIndex > 0 &&
|
||||
mod->exports[i].segIndex <= mod->segmentCount) {
|
||||
mod->exports[i].selector = mod->segments[mod->exports[i].segIndex - 1].selector;
|
||||
}
|
||||
}
|
||||
|
||||
// Process relocations for each segment
|
||||
for (uint16_t i = 0; i < mod->segmentCount; i++) {
|
||||
if (segTable[i].flags & NE_SEGF_HASRELOC) {
|
||||
if (!processRelocations(mod, i, fp, resolver)) {
|
||||
logErr("neload: relocation failed for segment %u\n", i + 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool allocateSegment(NeModuleT *mod, int segIdx, uint32_t size, bool isCode)
|
||||
{
|
||||
LoadedSegT *seg = &mod->segments[segIdx];
|
||||
|
||||
// Allocate memory using DJGPP's malloc (extended memory, flat model)
|
||||
// The memory is accessible via the flat DS selector, but we also need
|
||||
// a 16-bit selector pointing to it.
|
||||
uint8_t *mem = (uint8_t *)calloc(1, size);
|
||||
if (!mem) {
|
||||
logErr("neload: failed to allocate %" PRIu32 " bytes for segment %u\n", size, segIdx + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// In DJGPP, pointer values are offsets from the DS base.
|
||||
// The true linear address = pointer + __djgpp_base_address.
|
||||
// We store the pointer value (for C access) but use the linear
|
||||
// address when setting up the LDT descriptor base.
|
||||
uint32_t ptrVal = (uint32_t)mem;
|
||||
uint32_t linearAddr = ptrVal + __djgpp_base_address;
|
||||
|
||||
// Create a 16-bit LDT descriptor for this segment
|
||||
uint16_t sel = makeDescriptor16(linearAddr, size - 1, isCode);
|
||||
if (sel == 0) {
|
||||
free(mem);
|
||||
return false;
|
||||
}
|
||||
|
||||
seg->linearAddr = ptrVal; // Store DJGPP pointer (for C access via cast)
|
||||
seg->selector = sel;
|
||||
seg->size = size;
|
||||
seg->isCode = isCode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static uint16_t makeDescriptor16(uint32_t base, uint32_t limit, bool isCode)
|
||||
{
|
||||
int sel = __dpmi_allocate_ldt_descriptors(1);
|
||||
if (sel < 0) {
|
||||
logErr("neload: failed to allocate LDT descriptor\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (__dpmi_set_segment_base_address(sel, base) < 0) {
|
||||
logErr("neload: failed to set segment base\n");
|
||||
__dpmi_free_ldt_descriptor(sel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (__dpmi_set_segment_limit(sel, limit) < 0) {
|
||||
logErr("neload: failed to set segment limit\n");
|
||||
__dpmi_free_ldt_descriptor(sel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set access rights for 16-bit segment
|
||||
// Code: present, DPL 3, code, readable, non-conforming = 0xFA
|
||||
// Data: present, DPL 3, data, writable = 0xF2
|
||||
// High byte: G=0, D=0 (16-bit), 0, AVL=0 = 0x00
|
||||
uint16_t rights = isCode ? 0x00FA : 0x00F2;
|
||||
if (__dpmi_set_descriptor_access_rights(sel, rights) < 0) {
|
||||
logErr("neload: failed to set access rights (0x%04X)\n", rights);
|
||||
__dpmi_free_ldt_descriptor(sel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint16_t)sel;
|
||||
}
|
||||
|
||||
|
||||
static bool loadSegmentData(NeModuleT *mod, int segIdx, FILE *fp, uint32_t fileOffset, uint32_t fileSize)
|
||||
{
|
||||
LoadedSegT *seg = &mod->segments[segIdx];
|
||||
|
||||
fseek(fp, fileOffset, SEEK_SET);
|
||||
|
||||
// Read directly into the allocated memory (flat model, so pointer works)
|
||||
uint8_t *dest = (uint8_t *)seg->linearAddr;
|
||||
if (fread(dest, 1, fileSize, fp) != fileSize) {
|
||||
logErr("neload: short read loading segment %u data\n", segIdx + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool processRelocations(NeModuleT *mod, int segIdx, FILE *fp, ImportResolverT resolver)
|
||||
{
|
||||
LoadedSegT *seg = &mod->segments[segIdx];
|
||||
|
||||
// Relocation data follows the segment data in the file.
|
||||
// The segment table entry gives us the file offset; relocation data
|
||||
// starts at fileOffset + fileLength.
|
||||
uint32_t segTableOff = mod->neHeaderFileOffset + mod->neHeader.segmentTableOffset;
|
||||
fseek(fp, segTableOff + segIdx * sizeof(NeSegEntryT), SEEK_SET);
|
||||
|
||||
NeSegEntryT se;
|
||||
if (fread(&se, sizeof(se), 1, fp) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t fileOffset = (uint32_t)se.fileSectorOffset << mod->sectorAlignShift;
|
||||
uint32_t fileLen = se.fileLength;
|
||||
if (fileLen == 0 && se.fileSectorOffset != 0) {
|
||||
fileLen = 0x10000;
|
||||
}
|
||||
|
||||
// Relocation records follow the segment data
|
||||
uint32_t relocOff = fileOffset + fileLen;
|
||||
fseek(fp, relocOff, SEEK_SET);
|
||||
|
||||
// First word is the count of relocation records
|
||||
uint16_t relocCount;
|
||||
if (fread(&relocCount, 2, 1, fp) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgPrint("neload: segment %u has %u relocations\n", segIdx + 1, relocCount);
|
||||
|
||||
uint8_t *segData = (uint8_t *)seg->linearAddr;
|
||||
|
||||
for (uint16_t i = 0; i < relocCount; i++) {
|
||||
NeRelocT rec;
|
||||
if (fread(&rec, sizeof(rec), 1, fp) != 1) {
|
||||
logErr("neload: failed to read relocation %u/%u\n", i + 1, relocCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t targetType = rec.flags & NE_RELF_TARGET_MASK;
|
||||
bool additive = (rec.flags & NE_RELF_ADDITIVE) != 0;
|
||||
|
||||
// Resolve the target address
|
||||
uint16_t targetSel = 0;
|
||||
uint16_t targetOff = 0;
|
||||
bool resolved = false;
|
||||
|
||||
switch (targetType) {
|
||||
case NE_RELF_INTERNALREF: {
|
||||
// Internal reference: target1 = segment index (1-based)
|
||||
// For moveable segments, target2 is an entry table ordinal.
|
||||
// For fixed segments, target2 is the offset.
|
||||
uint16_t tSegIdx = rec.target1;
|
||||
if (rec.target1 == 0xFF) {
|
||||
// Moveable reference via entry table
|
||||
uint16_t ordinal = rec.target2;
|
||||
if (ordinal > 0 && ordinal < NE_MAX_EXPORTS &&
|
||||
mod->exports[ordinal].segIndex > 0) {
|
||||
tSegIdx = mod->exports[ordinal].segIndex;
|
||||
targetOff = mod->exports[ordinal].offset;
|
||||
targetSel = mod->segments[tSegIdx - 1].selector;
|
||||
resolved = true;
|
||||
}
|
||||
} else if (tSegIdx > 0 && tSegIdx <= mod->segmentCount) {
|
||||
targetSel = mod->segments[tSegIdx - 1].selector;
|
||||
targetOff = rec.target2;
|
||||
resolved = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NE_RELF_IMPORTORD: {
|
||||
// Import by ordinal: target1 = module ref index (1-based)
|
||||
// target2 = ordinal number
|
||||
uint16_t modIdx = rec.target1;
|
||||
uint16_t ordinal = rec.target2;
|
||||
|
||||
if (modIdx > 0 && modIdx <= mod->modRefCount && resolver) {
|
||||
FarPtr16T addr = resolver(mod->modRefNames[modIdx - 1], ordinal, NULL);
|
||||
if (addr.segment != 0 || addr.offset != 0) {
|
||||
targetSel = addr.segment;
|
||||
targetOff = addr.offset;
|
||||
resolved = true;
|
||||
}
|
||||
dbgPrint("neload: RELOC seg %u off 0x%04X srcType=%u: %s.%u -> %04X:%04X\n",
|
||||
segIdx + 1, rec.srcOffset, rec.srcType,
|
||||
mod->modRefNames[modIdx - 1], ordinal,
|
||||
targetSel, targetOff);
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
dbgPrint("neload: UNRESOLVED import %s.%u in seg %u at 0x%04X\n",
|
||||
(modIdx > 0 && modIdx <= mod->modRefCount) ?
|
||||
mod->modRefNames[modIdx - 1] : "???",
|
||||
ordinal, segIdx + 1, rec.srcOffset);
|
||||
// Patch in a dummy value (INT 3 / breakpoint)
|
||||
targetSel = mod->segments[0].selector; // Point to first code seg
|
||||
targetOff = 0;
|
||||
resolved = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NE_RELF_IMPORTNAME: {
|
||||
// Import by name: target1 = module ref index (1-based)
|
||||
// target2 = offset into imported names table
|
||||
uint16_t modIdx = rec.target1;
|
||||
uint16_t nameOff16 = rec.target2;
|
||||
|
||||
// Read the function name from the imported names table
|
||||
char funcName[64] = "";
|
||||
uint32_t importNameTableOff = mod->neHeaderFileOffset +
|
||||
mod->neHeader.importNameTableOffset;
|
||||
long savedPos = ftell(fp);
|
||||
fseek(fp, importNameTableOff + nameOff16, SEEK_SET);
|
||||
|
||||
uint8_t nameLen;
|
||||
if (fread(&nameLen, 1, 1, fp) == 1 && nameLen < 64) {
|
||||
fread(funcName, 1, nameLen, fp);
|
||||
funcName[nameLen] = '\0';
|
||||
}
|
||||
fseek(fp, savedPos, SEEK_SET);
|
||||
|
||||
if (modIdx > 0 && modIdx <= mod->modRefCount && resolver) {
|
||||
FarPtr16T addr = resolver(mod->modRefNames[modIdx - 1], 0, funcName);
|
||||
if (addr.segment != 0 || addr.offset != 0) {
|
||||
targetSel = addr.segment;
|
||||
targetOff = addr.offset;
|
||||
resolved = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
dbgPrint("neload: UNRESOLVED import %s.%s in seg %u at 0x%04X\n",
|
||||
(modIdx > 0 && modIdx <= mod->modRefCount) ?
|
||||
mod->modRefNames[modIdx - 1] : "???",
|
||||
funcName, segIdx + 1, rec.srcOffset);
|
||||
targetSel = mod->segments[0].selector;
|
||||
targetOff = 0;
|
||||
resolved = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NE_RELF_OSFIXUP: {
|
||||
// OS fixup (floating point emulation, etc.)
|
||||
// target1 = fixup type (1=FIARQQ, 2=FJARQQ, etc.)
|
||||
// We just patch in NOPs or a far return.
|
||||
targetSel = mod->segments[0].selector;
|
||||
targetOff = 0;
|
||||
resolved = true;
|
||||
dbgPrint("neload: OS fixup type %u at seg %u offset 0x%04X\n",
|
||||
rec.target1, segIdx + 1, rec.srcOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
logErr("neload: failed to resolve reloc in seg %u at 0x%04X\n",
|
||||
segIdx + 1, rec.srcOffset);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply the fixup to the segment data.
|
||||
// NE relocations can be chained: the word at srcOffset contains
|
||||
// the offset of the next fixup location (forming a linked list),
|
||||
// UNLESS the relocation is additive.
|
||||
uint32_t fixupOff = rec.srcOffset;
|
||||
|
||||
if (additive) {
|
||||
// Single fixup, add to existing value
|
||||
switch (rec.srcType) {
|
||||
case NE_RELOC_LOBYTE:
|
||||
if (fixupOff < seg->size) {
|
||||
segData[fixupOff] += (uint8_t)targetOff;
|
||||
}
|
||||
break;
|
||||
case NE_RELOC_OFFSET:
|
||||
if (fixupOff + 1 < seg->size) {
|
||||
uint16_t *p = (uint16_t *)(segData + fixupOff);
|
||||
*p += targetOff;
|
||||
}
|
||||
break;
|
||||
case NE_RELOC_SEGMENT:
|
||||
if (fixupOff + 1 < seg->size) {
|
||||
uint16_t *p = (uint16_t *)(segData + fixupOff);
|
||||
*p += targetSel;
|
||||
}
|
||||
break;
|
||||
case NE_RELOC_FAR_ADDR:
|
||||
if (fixupOff + 3 < seg->size) {
|
||||
uint16_t *pOff = (uint16_t *)(segData + fixupOff);
|
||||
uint16_t *pSeg = (uint16_t *)(segData + fixupOff + 2);
|
||||
*pOff += targetOff;
|
||||
*pSeg += targetSel;
|
||||
}
|
||||
break;
|
||||
case NE_RELOC_OFFSET32:
|
||||
if (fixupOff + 3 < seg->size) {
|
||||
uint32_t *p = (uint32_t *)(segData + fixupOff);
|
||||
*p += ((uint32_t)targetSel << 16) | targetOff;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Chained fixups: follow the linked list
|
||||
int chainLimit = 4096; // Safety limit
|
||||
int chainCount = 0;
|
||||
while (fixupOff != 0xFFFF && chainLimit-- > 0) {
|
||||
if (fixupOff + 1 >= seg->size) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t nextOff;
|
||||
chainCount++;
|
||||
|
||||
switch (rec.srcType) {
|
||||
case NE_RELOC_LOBYTE:
|
||||
if (fixupOff < seg->size) {
|
||||
nextOff = segData[fixupOff]; // Next in chain
|
||||
segData[fixupOff] = (uint8_t)targetOff;
|
||||
} else {
|
||||
nextOff = 0xFFFF;
|
||||
}
|
||||
break;
|
||||
|
||||
case NE_RELOC_OFFSET: {
|
||||
uint16_t *p = (uint16_t *)(segData + fixupOff);
|
||||
nextOff = *p;
|
||||
*p = targetOff;
|
||||
break;
|
||||
}
|
||||
|
||||
case NE_RELOC_SEGMENT: {
|
||||
uint16_t *p = (uint16_t *)(segData + fixupOff);
|
||||
nextOff = *p;
|
||||
*p = targetSel;
|
||||
break;
|
||||
}
|
||||
|
||||
case NE_RELOC_FAR_ADDR: {
|
||||
if (fixupOff + 3 >= seg->size) {
|
||||
nextOff = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
uint16_t *pOff = (uint16_t *)(segData + fixupOff);
|
||||
uint16_t *pSeg = (uint16_t *)(segData + fixupOff + 2);
|
||||
nextOff = *pOff; // Chain is in offset field
|
||||
*pOff = targetOff;
|
||||
*pSeg = targetSel;
|
||||
break;
|
||||
}
|
||||
|
||||
case NE_RELOC_OFFSET32: {
|
||||
if (fixupOff + 3 >= seg->size) {
|
||||
nextOff = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
uint16_t *p16 = (uint16_t *)(segData + fixupOff);
|
||||
nextOff = *p16;
|
||||
uint32_t *p32 = (uint32_t *)(segData + fixupOff);
|
||||
*p32 = ((uint32_t)targetSel << 16) | targetOff;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
dbgPrint("neload: unknown reloc srcType 0x%02X\n", rec.srcType);
|
||||
nextOff = 0xFFFF;
|
||||
break;
|
||||
}
|
||||
|
||||
fixupOff = nextOff;
|
||||
}
|
||||
|
||||
if (chainCount > 1) {
|
||||
dbgPrint("neload: chain: %d links patched\n", chainCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void freeSegment(LoadedSegT *seg)
|
||||
{
|
||||
if (seg->selector != 0) {
|
||||
__dpmi_free_ldt_descriptor(seg->selector);
|
||||
seg->selector = 0;
|
||||
}
|
||||
if (seg->linearAddr != 0) {
|
||||
free((void *)seg->linearAddr);
|
||||
seg->linearAddr = 0;
|
||||
}
|
||||
seg->size = 0;
|
||||
}
|
||||
|
||||
|
||||
bool neExtendSegment(NeModuleT *mod, int segIdx, uint32_t extraSize, uint32_t *oldSizeOut)
|
||||
{
|
||||
if (segIdx < 0 || segIdx >= mod->segmentCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadedSegT *seg = &mod->segments[segIdx];
|
||||
uint32_t oldSize = seg->size;
|
||||
uint32_t newSize = oldSize + extraSize;
|
||||
|
||||
// 16-bit segments are limited to 64K
|
||||
if (newSize > 0x10000) {
|
||||
logErr("neload: cannot extend segment %d beyond 64K (old=%" PRIu32 " extra=%" PRIu32 ")\n",
|
||||
segIdx + 1, oldSize, extraSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *newMem = (uint8_t *)realloc((void *)seg->linearAddr, newSize);
|
||||
if (!newMem) {
|
||||
logErr("neload: realloc failed extending segment %d\n", segIdx + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zero the new space
|
||||
memset(newMem + oldSize, 0, extraSize);
|
||||
|
||||
uint32_t newPtrVal = (uint32_t)newMem;
|
||||
uint32_t newLinAddr = newPtrVal + __djgpp_base_address;
|
||||
|
||||
seg->linearAddr = newPtrVal;
|
||||
seg->size = newSize;
|
||||
|
||||
// Update LDT descriptor base (may have moved) and limit
|
||||
__dpmi_set_segment_base_address(seg->selector, newLinAddr);
|
||||
__dpmi_set_segment_limit(seg->selector, newSize - 1);
|
||||
|
||||
if (oldSizeOut) {
|
||||
*oldSizeOut = oldSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Enable debug output for the NE loader
|
||||
void neSetDebug(bool enable)
|
||||
{
|
||||
gDebug = enable;
|
||||
}
|
||||
115
win31drv/neload.h
Normal file
115
win31drv/neload.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef NELOAD_H
|
||||
#define NELOAD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "neformat.h"
|
||||
#include "wintypes.h"
|
||||
|
||||
// ============================================================================
|
||||
// Loaded segment descriptor
|
||||
// ============================================================================
|
||||
|
||||
typedef struct {
|
||||
uint32_t linearAddr; // Linear (physical) address of segment data
|
||||
uint16_t selector; // DPMI selector for this segment
|
||||
uint16_t flags; // Original NE segment flags
|
||||
uint32_t size; // Actual size in memory (from minAllocSize or fileLength)
|
||||
uint32_t fileSize; // Size of data in the file (0 if no file data)
|
||||
bool isCode; // true = code segment, false = data segment
|
||||
} LoadedSegT;
|
||||
|
||||
// ============================================================================
|
||||
// Resolved export entry
|
||||
// ============================================================================
|
||||
|
||||
typedef struct {
|
||||
uint16_t segIndex; // 1-based segment index
|
||||
uint16_t offset; // Offset within segment
|
||||
uint16_t selector; // DPMI selector (resolved at load time)
|
||||
uint8_t flags; // Entry flags
|
||||
} ExportEntryT;
|
||||
|
||||
// ============================================================================
|
||||
// Import resolution callback
|
||||
//
|
||||
// Called by the NE loader when it encounters an imported reference.
|
||||
// The callback should return the far pointer (selector:offset) that the
|
||||
// import should resolve to. Return FARPTR16_NULL if the import cannot
|
||||
// be resolved (the loader will log a warning and patch in a stub).
|
||||
// ============================================================================
|
||||
|
||||
typedef FarPtr16T (*ImportResolverT)(const char *moduleName, uint16_t ordinal, const char *funcName);
|
||||
|
||||
// ============================================================================
|
||||
// Loaded NE module
|
||||
// ============================================================================
|
||||
|
||||
#define NE_MAX_SEGMENTS 64
|
||||
#define NE_MAX_EXPORTS 2048
|
||||
#define NE_MAX_MODREFS 16
|
||||
|
||||
typedef struct {
|
||||
// Header info
|
||||
NeHeaderT neHeader;
|
||||
uint32_t neHeaderFileOffset; // File offset of NE header
|
||||
uint16_t sectorAlignShift; // Sector alignment
|
||||
|
||||
// Module name (from resident name table)
|
||||
char moduleName[64];
|
||||
|
||||
// Segments
|
||||
uint16_t segmentCount;
|
||||
LoadedSegT segments[NE_MAX_SEGMENTS];
|
||||
|
||||
// Exports (indexed by ordinal)
|
||||
uint16_t exportCount;
|
||||
ExportEntryT exports[NE_MAX_EXPORTS];
|
||||
|
||||
// Module references (imported module names)
|
||||
uint16_t modRefCount;
|
||||
char modRefNames[NE_MAX_MODREFS][32];
|
||||
|
||||
// Auto data segment selector (DGROUP)
|
||||
uint16_t autoDataSel;
|
||||
|
||||
// DOS memory block (for conventional memory allocations)
|
||||
int dosMemSeg; // DOS memory segment (real mode)
|
||||
int dosMemSel; // DOS memory selector (PM)
|
||||
uint32_t dosMemSize; // Size in bytes
|
||||
|
||||
// Is the module valid/loaded?
|
||||
bool loaded;
|
||||
} NeModuleT;
|
||||
|
||||
// ============================================================================
|
||||
// NE loader functions
|
||||
// ============================================================================
|
||||
|
||||
// Load a NE executable from a file.
|
||||
// importResolver is called for each imported reference.
|
||||
// Returns true on success.
|
||||
bool neLoadModule(NeModuleT *mod, const char *filePath, ImportResolverT resolver);
|
||||
|
||||
// Unload a previously loaded module, freeing all segments and selectors.
|
||||
void neUnloadModule(NeModuleT *mod);
|
||||
|
||||
// Look up an export by ordinal number.
|
||||
// Returns true and fills seg/off/sel if found.
|
||||
bool neLookupExport(const NeModuleT *mod, uint16_t ordinal, uint16_t *seg, uint16_t *off, uint16_t *sel);
|
||||
|
||||
// Look up an export by name (searches the resident name table in the loaded file).
|
||||
// Returns the ordinal if found, 0 if not found.
|
||||
uint16_t neLookupExportByName(const NeModuleT *mod, const char *name, const char *filePath);
|
||||
|
||||
// Extend a loaded segment by extraSize bytes.
|
||||
// The new space is zeroed. *oldSizeOut receives the original size
|
||||
// (i.e., the offset of the new area within the segment).
|
||||
// Returns true on success. Fails if the result would exceed 64K.
|
||||
bool neExtendSegment(NeModuleT *mod, int segIdx, uint32_t extraSize, uint32_t *oldSizeOut);
|
||||
|
||||
// Debug: dump module information to stderr.
|
||||
void neDumpModule(const NeModuleT *mod);
|
||||
|
||||
#endif // NELOAD_H
|
||||
1110
win31drv/thunk.c
Normal file
1110
win31drv/thunk.c
Normal file
File diff suppressed because it is too large
Load diff
135
win31drv/thunk.h
Normal file
135
win31drv/thunk.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#ifndef THUNK_H
|
||||
#define THUNK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "wintypes.h"
|
||||
|
||||
// ============================================================================
|
||||
// 32-to-16 bit thunking layer
|
||||
//
|
||||
// Provides a mechanism for 32-bit DJGPP code to call into 16-bit Windows
|
||||
// driver code running in 16-bit protected mode segments.
|
||||
//
|
||||
// Architecture:
|
||||
// - A small 16-bit relay thunk is placed in a 16-bit code segment
|
||||
// - Parameters are pre-built on a dedicated 16-bit stack
|
||||
// - The relay handles 32/16-bit return address translation
|
||||
// - Driver functions use Pascal calling convention (callee cleans stack)
|
||||
//
|
||||
// The relay thunk code:
|
||||
// 1. Pops the 32-bit return address (8 bytes: 32-bit CS + 32-bit EIP)
|
||||
// 2. Pushes parameters from the shared data area onto the 16-bit stack
|
||||
// 3. Does a 16-bit far call to the target driver function
|
||||
// 4. Saves the return value (DX:AX)
|
||||
// 5. Pushes the 32-bit return address back
|
||||
// 6. Does a 32-bit far return (o32 retf)
|
||||
// ============================================================================
|
||||
|
||||
// ============================================================================
|
||||
// Thunk shared data area (in 16-bit addressable memory)
|
||||
//
|
||||
// The 32-bit side writes target address and parameters here before
|
||||
// calling the relay. The relay reads from here.
|
||||
// ============================================================================
|
||||
|
||||
#define THUNK_MAX_PARAMS 32 // Max 16-bit words of parameters per call
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t targetOff; // 0x00: target function offset
|
||||
uint16_t targetSeg; // 0x02: target function segment (selector)
|
||||
uint16_t paramCount; // 0x04: number of 16-bit parameter words
|
||||
uint16_t params[THUNK_MAX_PARAMS]; // 0x06: parameter words
|
||||
} ThunkDataT;
|
||||
|
||||
// ============================================================================
|
||||
// Thunk context (initialized once, used for all calls)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct {
|
||||
// 16-bit relay code segment
|
||||
uint16_t relayCodeSel; // Selector for relay code segment
|
||||
uint32_t relayCodeBase; // Linear base address of relay code
|
||||
uint16_t relayCodeSize; // Size of relay code
|
||||
|
||||
// 16-bit data segment (for ThunkDataT)
|
||||
uint16_t dataSegSel; // Selector for shared data segment
|
||||
uint32_t dataSegBase; // Linear base address of data segment
|
||||
uint16_t dataSegSize; // Size of data segment
|
||||
|
||||
// 16-bit stack segment
|
||||
uint16_t stackSel; // Selector for 16-bit stack
|
||||
uint32_t stackBase; // Linear base address of stack
|
||||
uint16_t stackSize; // Size of stack
|
||||
|
||||
// DOS memory for all 16-bit segments (single allocation)
|
||||
int dosMemSeg; // Real-mode segment
|
||||
int dosMemSel; // PM selector from DOS alloc
|
||||
uint32_t dosMemSize; // Total bytes allocated
|
||||
|
||||
// Driver's auto data segment (DGROUP) selector.
|
||||
// Set this before calling thunkCall16 so the relay loads DS correctly.
|
||||
uint16_t dgroupSel;
|
||||
|
||||
bool initialized;
|
||||
} ThunkContextT;
|
||||
|
||||
// ============================================================================
|
||||
// Thunk layer functions
|
||||
// ============================================================================
|
||||
|
||||
// Initialize the thunking infrastructure.
|
||||
// Allocates DOS memory, creates 16-bit segments, installs relay code.
|
||||
bool thunkInit(ThunkContextT *ctx);
|
||||
|
||||
// Enable or disable verbose callback tracing.
|
||||
void thunkSetDebug(bool debug);
|
||||
|
||||
// Set a watchpoint on 3 bytes at sel:off. Logs any changes during callbacks.
|
||||
void thunkSetWatch(uint16_t sel, uint32_t off);
|
||||
|
||||
// Shut down the thunking infrastructure and free resources.
|
||||
void thunkShutdown(ThunkContextT *ctx);
|
||||
|
||||
// Call a 16-bit function via the thunk.
|
||||
// targetSel:targetOff - far address of the 16-bit function
|
||||
// params - array of 16-bit parameter words in Pascal order:
|
||||
// params[0] = leftmost parameter (pushed first, deepest)
|
||||
// params[N-1] = rightmost parameter (pushed last, top)
|
||||
// paramCount - number of 16-bit words in params
|
||||
//
|
||||
// Returns DX:AX combined as a uint32_t (AX in low 16, DX in high 16).
|
||||
// For functions returning WORD, just use the low 16 bits.
|
||||
uint32_t thunkCall16(ThunkContextT *ctx, uint16_t targetSel, uint16_t targetOff, const uint16_t *params, uint16_t paramCount);
|
||||
|
||||
// Convenience: call with individual parameters (up to 12 words).
|
||||
uint32_t thunkCall16v(ThunkContextT *ctx, uint16_t targetSel, uint16_t targetOff, uint16_t paramCount, ...);
|
||||
|
||||
// ============================================================================
|
||||
// 16-bit stub generation
|
||||
//
|
||||
// For Windows API stubs that the driver calls back into, we need 16-bit
|
||||
// entry points that thunk UP to 32-bit code.
|
||||
// ============================================================================
|
||||
|
||||
// Callback function type for 16-to-32 callbacks.
|
||||
// Receives the parameters as an array of 16-bit words.
|
||||
// Returns DX:AX as uint32_t.
|
||||
typedef uint32_t (*ThunkCallbackT)(uint16_t *params, uint16_t paramCount);
|
||||
|
||||
// Maximum number of registered callbacks
|
||||
#define THUNK_MAX_CALLBACKS 128
|
||||
|
||||
// Register a callback and get a 16-bit far pointer (sel:off) that,
|
||||
// when called from 16-bit code, will invoke the callback in 32-bit.
|
||||
// paramWords = number of 16-bit parameter words the function expects
|
||||
// (used by the stub to clean the stack with retf N).
|
||||
// Returns true on success.
|
||||
bool thunkRegisterCallback(ThunkContextT *ctx, ThunkCallbackT callback, uint16_t paramWords, FarPtr16T *result);
|
||||
|
||||
// Sanitize the callback frame after freeing a selector.
|
||||
// If the saved ES or DS in the callback interrupt frame matches freedSel,
|
||||
// zero it out so the IRET doesn't try to load a freed descriptor.
|
||||
void thunkSanitizeCbFrame(uint16_t freedSel);
|
||||
|
||||
#endif // THUNK_H
|
||||
260
win31drv/winddi.h
Normal file
260
win31drv/winddi.h
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
#ifndef WINDDI_H
|
||||
#define WINDDI_H
|
||||
|
||||
#include "wintypes.h"
|
||||
|
||||
// ============================================================================
|
||||
// GDIINFO - Device capabilities structure filled by Enable()
|
||||
// This is the 16-bit Windows 3.1 DDK GDIINFO structure.
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t dpVersion; // 0x00: driver version (0x030A for 3.10)
|
||||
int16_t dpTechnology; // 0x02: device technology
|
||||
int16_t dpHorzSize; // 0x04: horizontal size in mm
|
||||
int16_t dpVertSize; // 0x06: vertical size in mm
|
||||
int16_t dpHorzRes; // 0x08: horizontal resolution (pixels)
|
||||
int16_t dpVertRes; // 0x0A: vertical resolution (pixels)
|
||||
int16_t dpBitsPixel; // 0x0C: bits per pixel
|
||||
int16_t dpPlanes; // 0x0E: number of bit planes
|
||||
int16_t dpNumBrushes; // 0x10: number of device brushes
|
||||
int16_t dpNumPens; // 0x12: number of device pens
|
||||
int16_t dpNumFonts; // 0x14: number of device fonts
|
||||
int16_t dpNumColors; // 0x16: number of colors in color table
|
||||
int16_t dpDEVICEsize; // 0x18: size of PDEVICE structure
|
||||
uint16_t dpCurves; // 0x1A: curve capabilities
|
||||
uint16_t dpLines; // 0x1C: line capabilities
|
||||
uint16_t dpPolygonals; // 0x1E: polygon capabilities
|
||||
uint16_t dpText; // 0x20: text capabilities
|
||||
uint16_t dpClip; // 0x22: clipping capabilities
|
||||
uint16_t dpRaster; // 0x24: raster capabilities
|
||||
int16_t dpAspectX; // 0x26: x aspect ratio
|
||||
int16_t dpAspectY; // 0x28: y aspect ratio
|
||||
int16_t dpAspectXY; // 0x2A: diagonal aspect ratio
|
||||
int16_t dpStyleLen; // 0x2C: length of styled line segment
|
||||
Point16T dpMLoWin; // 0x2E: metric lo-res window
|
||||
Point16T dpMLoVpt; // 0x32: metric lo-res viewport
|
||||
Point16T dpMHiWin; // 0x36: metric hi-res window
|
||||
Point16T dpMHiVpt; // 0x3A: metric hi-res viewport
|
||||
Point16T dpELoWin; // 0x3E: english lo-res window
|
||||
Point16T dpELoVpt; // 0x42: english lo-res viewport
|
||||
Point16T dpEHiWin; // 0x46: english hi-res window
|
||||
Point16T dpEHiVpt; // 0x4A: english hi-res viewport
|
||||
Point16T dpTwpWin; // 0x4E: twips window
|
||||
Point16T dpTwpVpt; // 0x52: twips viewport
|
||||
int16_t dpLogPixelsX; // 0x56: logical pixels per inch X
|
||||
int16_t dpLogPixelsY; // 0x58: logical pixels per inch Y
|
||||
int16_t dpDCManage; // 0x5A: DC management flags
|
||||
uint16_t reserved1[5]; // 0x5C: reserved
|
||||
uint16_t dpPalColors; // 0x66: number of palette colors
|
||||
uint16_t dpPalReserved; // 0x68: number of reserved palette entries
|
||||
uint16_t dpPalResolution; // 0x6A: palette DAC resolution (bits per gun)
|
||||
} GdiInfo16T;
|
||||
|
||||
// dpTechnology values
|
||||
#define DT_PLOTTER 0
|
||||
#define DT_RASDISPLAY 1
|
||||
#define DT_RASPRINTER 2
|
||||
#define DT_RASCAMERA 3
|
||||
#define DT_CHARSTREAM 4
|
||||
#define DT_METAFILE 5
|
||||
#define DT_DISPFILE 6
|
||||
|
||||
// dpRaster capability bits
|
||||
#define RC_BITBLT 0x0001
|
||||
#define RC_BANDING 0x0002
|
||||
#define RC_SCALING 0x0004
|
||||
#define RC_BITMAP64 0x0008
|
||||
#define RC_GDI20_OUTPUT 0x0010
|
||||
#define RC_DI_BITMAP 0x0080
|
||||
#define RC_PALETTE 0x0100
|
||||
#define RC_DIBTODEV 0x0200
|
||||
#define RC_BIGFONT 0x0400
|
||||
#define RC_STRETCHBLT 0x0800
|
||||
#define RC_FLOODFILL 0x1000
|
||||
#define RC_STRETCHDIB 0x2000
|
||||
|
||||
// ============================================================================
|
||||
// PDEVICE - Physical device descriptor
|
||||
// The first word indicates the type. The rest is driver-specific.
|
||||
// We allocate a generous buffer for the driver to fill in.
|
||||
// ============================================================================
|
||||
|
||||
#define PDEVICE_MAX_SIZE 4096
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t pdType; // 0 = memory bitmap, nonzero = physical device
|
||||
uint8_t pdData[PDEVICE_MAX_SIZE - 2]; // driver-specific data
|
||||
} PDevice16T;
|
||||
|
||||
// ============================================================================
|
||||
// DRAWMODE - Drawing mode structure (passed to BitBlt, Output, etc.)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t rop2; // 0x00: raster operation (R2_*)
|
||||
int16_t bkMode; // 0x02: background mode (TRANSPARENT=1, OPAQUE=2)
|
||||
uint32_t bkColor; // 0x04: background color (physical)
|
||||
uint32_t textColor; // 0x08: text color (physical)
|
||||
int16_t tBreakExtra; // 0x0C: total break extra
|
||||
int16_t breakExtra; // 0x0E: break extra per char
|
||||
int16_t breakErr; // 0x10: accumulated break error
|
||||
int16_t breakRem; // 0x12: break remainder
|
||||
int16_t breakCount; // 0x14: break count
|
||||
int16_t charExtra; // 0x16: extra pixels per char
|
||||
uint32_t lbkColor; // 0x18: logical background color
|
||||
uint32_t ltextColor; // 0x1C: logical text color
|
||||
uint16_t icrBk; // 0x20: index to background color
|
||||
uint16_t icrText; // 0x22: index to text color
|
||||
} DrawMode16T;
|
||||
|
||||
// Background mode constants
|
||||
#define BM_TRANSPARENT 1
|
||||
#define BM_OPAQUE 2
|
||||
|
||||
// ============================================================================
|
||||
// Logical brush (for RealizeObject)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t lbStyle; // Brush style
|
||||
uint32_t lbColor; // Brush color (COLORREF)
|
||||
int16_t lbHatch; // Hatch pattern
|
||||
uint32_t lbBkColor; // Background color (Win 3.1)
|
||||
} LogBrush16T;
|
||||
|
||||
// Brush styles
|
||||
#define BS_SOLID 0
|
||||
#define BS_HOLLOW 1
|
||||
#define BS_NULL 1
|
||||
#define BS_HATCHED 2
|
||||
#define BS_PATTERN 3
|
||||
#define BS_DIBPATTERN 5
|
||||
|
||||
// Hatch styles
|
||||
#define HS_HORIZONTAL 0
|
||||
#define HS_VERTICAL 1
|
||||
#define HS_FDIAGONAL 2
|
||||
#define HS_BDIAGONAL 3
|
||||
#define HS_CROSS 4
|
||||
#define HS_DIAGCROSS 5
|
||||
|
||||
// ============================================================================
|
||||
// Logical pen (for RealizeObject)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t lopnStyle; // Pen style
|
||||
Point16T lopnWidth; // Pen width
|
||||
uint32_t lopnColor; // Pen color (COLORREF)
|
||||
} LogPen16T;
|
||||
|
||||
// Pen styles
|
||||
#define PS_SOLID 0
|
||||
#define PS_DASH 1
|
||||
#define PS_DOT 2
|
||||
#define PS_DASHDOT 3
|
||||
#define PS_DASHDOTDOT 4
|
||||
#define PS_NULL 5
|
||||
#define PS_INSIDEFRAME 6
|
||||
|
||||
// ============================================================================
|
||||
// CURSORINFO - Cursor shape description
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t csHotX; // Hotspot X
|
||||
int16_t csHotY; // Hotspot Y
|
||||
int16_t csWidth; // Cursor width
|
||||
int16_t csHeight; // Cursor height
|
||||
int16_t csWidthB; // Width in bytes
|
||||
int16_t csColor; // Planes * bitsPixel
|
||||
} CursorInfo16T;
|
||||
|
||||
// ============================================================================
|
||||
// Enable() style parameter values
|
||||
// ============================================================================
|
||||
|
||||
#define ENABLE_INQUIRE 0 // First call: fill GDIINFO
|
||||
#define ENABLE_ENABLE 1 // Second call: initialize PDEVICE
|
||||
|
||||
// ============================================================================
|
||||
// Output() style values
|
||||
// ============================================================================
|
||||
|
||||
#define OS_ARC 3
|
||||
#define OS_SCANLINES 4
|
||||
#define OS_RECTANGLE 6
|
||||
#define OS_ELLIPSE 7
|
||||
#define OS_MARKER 8
|
||||
#define OS_POLYLINE 18
|
||||
#define OS_ALTPOLYGON 22
|
||||
#define OS_WINDPOLYGON 20
|
||||
#define OS_PIE 23
|
||||
#define OS_POLYMARKER 24
|
||||
#define OS_CHORD 39
|
||||
#define OS_CIRCLE 55
|
||||
#define OS_ROUNDRECT 72
|
||||
|
||||
// ============================================================================
|
||||
// Control() function codes
|
||||
// ============================================================================
|
||||
|
||||
#define CTRL_GETSCALINGFACTOR 14
|
||||
#define CTRL_RESETDEVICE 128
|
||||
#define CTRL_MOUSETRAILS 39
|
||||
|
||||
// ============================================================================
|
||||
// RealizeObject() styles
|
||||
// ============================================================================
|
||||
|
||||
#define OBJ_PEN 1
|
||||
#define OBJ_BRUSH 2
|
||||
#define OBJ_FONT 3
|
||||
|
||||
// ============================================================================
|
||||
// Physical brush/pen structures (driver-specific, maximum size)
|
||||
// ============================================================================
|
||||
|
||||
#define PHYS_OBJ_MAX_SIZE 128
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t data[PHYS_OBJ_MAX_SIZE];
|
||||
} PhysObj16T;
|
||||
|
||||
// ============================================================================
|
||||
// DIBENGINE structures (for drivers that use the DIB engine)
|
||||
// ============================================================================
|
||||
|
||||
// DIB_BitmapInfo passed to DIB engine functions
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t bmType;
|
||||
int16_t bmWidth;
|
||||
int16_t bmHeight;
|
||||
int16_t bmWidthBytes;
|
||||
uint8_t bmPlanes;
|
||||
uint8_t bmBitsPixel;
|
||||
uint32_t bmBits; // Far pointer to bits (as DWORD)
|
||||
uint32_t bmWidthPlanes;
|
||||
uint32_t bmBitsLong; // Selector:0 far pointer
|
||||
uint16_t bmSegmentIndex;
|
||||
uint16_t bmScanSegment;
|
||||
uint16_t bmFillBytes;
|
||||
uint16_t reserved1;
|
||||
uint16_t reserved2;
|
||||
} DibBitmapInfo16T;
|
||||
|
||||
// DIB engine PDEVICE extension (placed at start of PDEVICE by DIB-based drivers)
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t deType; // Device type
|
||||
uint16_t deWidth; // Width in pixels
|
||||
uint16_t deHeight; // Height in pixels
|
||||
uint16_t deWidthBytes; // Bytes per scan line
|
||||
uint8_t dePlanes; // Number of planes
|
||||
uint8_t deBitsPixel; // Bits per pixel
|
||||
uint32_t delpPDevice; // Pointer to next PDEVICE
|
||||
uint32_t dlpColorTable; // Pointer to color table
|
||||
// ... additional fields follow
|
||||
} DibPDevice16T;
|
||||
|
||||
#endif // WINDDI_H
|
||||
3301
win31drv/windrv.c
Normal file
3301
win31drv/windrv.c
Normal file
File diff suppressed because it is too large
Load diff
189
win31drv/windrv.h
Normal file
189
win31drv/windrv.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#ifndef WINDRV_H
|
||||
#define WINDRV_H
|
||||
|
||||
// ============================================================================
|
||||
// windrv.h - Public API for using Windows 3.x display drivers from DOS
|
||||
//
|
||||
// This library loads Windows 3.x accelerated display drivers (16-bit NE
|
||||
// format DLLs) and provides a clean 32-bit C API for DOS programs compiled
|
||||
// with DJGPP to use their hardware-accelerated drawing functions.
|
||||
//
|
||||
// The library handles:
|
||||
// - NE executable loading with segment relocation
|
||||
// - 32-bit to 16-bit protected mode thunking via DPMI
|
||||
// - Windows API stub functions that drivers import
|
||||
// - DDI (Device Driver Interface) function wrappers
|
||||
//
|
||||
// Usage:
|
||||
// 1. Call wdrvInit() to initialize the library
|
||||
// 2. Call wdrvLoadDriver() with path to a .DRV file
|
||||
// 3. Call wdrvEnable() to set a video mode
|
||||
// 4. Use drawing functions (wdrvBitBlt, wdrvLine, etc.)
|
||||
// 5. Call wdrvDisable() to restore text mode
|
||||
// 6. Call wdrvUnloadDriver() and wdrvShutdown() to clean up
|
||||
// ============================================================================
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "wintypes.h"
|
||||
|
||||
// ============================================================================
|
||||
// Error codes
|
||||
// ============================================================================
|
||||
|
||||
#define WDRV_OK 0
|
||||
#define WDRV_ERR_INIT -1 // Initialization failed
|
||||
#define WDRV_ERR_NO_DPMI -2 // DPMI not available or insufficient
|
||||
#define WDRV_ERR_FILE_NOT_FOUND -3 // Driver file not found
|
||||
#define WDRV_ERR_BAD_FORMAT -4 // Not a valid NE executable
|
||||
#define WDRV_ERR_LOAD_FAILED -5 // Failed to load driver segments
|
||||
#define WDRV_ERR_NO_MEMORY -6 // Out of memory (conventional or extended)
|
||||
#define WDRV_ERR_RELOC_FAILED -7 // Relocation processing failed
|
||||
#define WDRV_ERR_NO_ENTRY -8 // Required DDI entry point not found
|
||||
#define WDRV_ERR_ENABLE_FAILED -9 // Driver Enable() call failed
|
||||
#define WDRV_ERR_THUNK_FAILED -10 // Thunk setup failed
|
||||
#define WDRV_ERR_NOT_LOADED -11 // No driver loaded
|
||||
#define WDRV_ERR_NOT_ENABLED -12 // Driver not enabled
|
||||
#define WDRV_ERR_UNSUPPORTED -13 // Operation not supported by driver
|
||||
|
||||
// ============================================================================
|
||||
// Opaque driver handle
|
||||
// ============================================================================
|
||||
|
||||
typedef struct WdrvDriverS *WdrvHandleT;
|
||||
|
||||
// ============================================================================
|
||||
// Driver information (returned by wdrvGetInfo)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct {
|
||||
char driverName[64]; // Module name from NE header
|
||||
uint16_t driverVersion; // Driver version number
|
||||
int32_t maxWidth; // Maximum supported width
|
||||
int32_t maxHeight; // Maximum supported height
|
||||
int32_t maxBpp; // Maximum bits per pixel
|
||||
int32_t numColors; // Number of colors
|
||||
uint32_t rasterCaps; // Raster capability bits (RC_*)
|
||||
bool hasBitBlt; // Driver exports BitBlt
|
||||
bool hasOutput; // Driver exports Output (lines, shapes)
|
||||
bool hasPixel; // Driver exports Pixel
|
||||
bool hasStretchBlt; // Driver exports StretchBlt
|
||||
bool hasExtTextOut; // Driver exports ExtTextOut
|
||||
bool hasSetPalette; // Driver exports SetPalette
|
||||
bool hasSetCursor; // Driver exports SetCursor
|
||||
} WdrvInfoT;
|
||||
|
||||
// ============================================================================
|
||||
// BitBlt parameters
|
||||
// ============================================================================
|
||||
|
||||
typedef struct {
|
||||
int16_t dstX;
|
||||
int16_t dstY;
|
||||
int16_t srcX;
|
||||
int16_t srcY;
|
||||
int16_t width;
|
||||
int16_t height;
|
||||
uint32_t rop3; // Raster operation (SRCCOPY, PATCOPY, etc.)
|
||||
} WdrvBitBltParamsT;
|
||||
|
||||
// ============================================================================
|
||||
// Library initialization / shutdown
|
||||
// ============================================================================
|
||||
|
||||
// Initialize the library. Must be called before any other functions.
|
||||
// Sets up DPMI descriptors, thunk infrastructure, and API stubs.
|
||||
int32_t wdrvInit(void);
|
||||
|
||||
// Shut down the library and free all resources.
|
||||
void wdrvShutdown(void);
|
||||
|
||||
// ============================================================================
|
||||
// Driver loading
|
||||
// ============================================================================
|
||||
|
||||
// Load a Windows 3.x display driver (.DRV file).
|
||||
// Returns a driver handle on success, NULL on failure.
|
||||
// Call wdrvGetLastError() for details on failure.
|
||||
WdrvHandleT wdrvLoadDriver(const char *driverPath);
|
||||
|
||||
// Unload a previously loaded driver.
|
||||
void wdrvUnloadDriver(WdrvHandleT handle);
|
||||
|
||||
// Get information about a loaded driver.
|
||||
// The driver must be loaded but need not be enabled.
|
||||
int32_t wdrvGetInfo(WdrvHandleT handle, WdrvInfoT *info);
|
||||
|
||||
// ============================================================================
|
||||
// Mode setting
|
||||
// ============================================================================
|
||||
|
||||
// Enable the driver (set video mode and initialize hardware).
|
||||
// width/height/bpp are the requested mode; the driver may adjust.
|
||||
// Pass 0 for defaults (driver's preferred resolution).
|
||||
int32_t wdrvEnable(WdrvHandleT handle, int32_t width, int32_t height, int32_t bpp);
|
||||
|
||||
// Disable the driver (restore text mode, release hardware).
|
||||
int32_t wdrvDisable(WdrvHandleT handle);
|
||||
|
||||
// ============================================================================
|
||||
// Drawing operations
|
||||
// ============================================================================
|
||||
|
||||
// Block transfer (hardware-accelerated if supported).
|
||||
int32_t wdrvBitBlt(WdrvHandleT handle, WdrvBitBltParamsT *params);
|
||||
|
||||
// Solid rectangle fill using PatBlt with a solid brush.
|
||||
int32_t wdrvFillRect(WdrvHandleT handle, int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color);
|
||||
|
||||
// Set a single pixel.
|
||||
int32_t wdrvSetPixel(WdrvHandleT handle, int16_t x, int16_t y, uint32_t color);
|
||||
|
||||
// Get a single pixel's color.
|
||||
uint32_t wdrvGetPixel(WdrvHandleT handle, int16_t x, int16_t y);
|
||||
|
||||
// Draw a polyline using the Output DDI function.
|
||||
int32_t wdrvPolyline(WdrvHandleT handle, Point16T *points, int16_t count, uint32_t color);
|
||||
|
||||
// Draw a rectangle outline.
|
||||
int32_t wdrvRectangle(WdrvHandleT handle, int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color);
|
||||
|
||||
// ============================================================================
|
||||
// Palette operations (for 8bpp modes)
|
||||
// ============================================================================
|
||||
|
||||
// Set palette entries. colors is an array of RGBQUAD (R,G,B,flags).
|
||||
int32_t wdrvSetPalette(WdrvHandleT handle, int32_t startIndex, int32_t count, const uint8_t *colors);
|
||||
|
||||
// ============================================================================
|
||||
// Direct framebuffer access
|
||||
// ============================================================================
|
||||
|
||||
// Get a near pointer to the linear framebuffer (if available).
|
||||
// Returns NULL if the driver doesn't provide linear access.
|
||||
void *wdrvGetFramebuffer(WdrvHandleT handle);
|
||||
|
||||
// Get the framebuffer pitch (bytes per scanline).
|
||||
int32_t wdrvGetPitch(WdrvHandleT handle);
|
||||
|
||||
// ============================================================================
|
||||
// Error reporting
|
||||
// ============================================================================
|
||||
|
||||
// Get the last error code.
|
||||
int32_t wdrvGetLastError(void);
|
||||
|
||||
// Get a human-readable description of the last error.
|
||||
const char *wdrvGetLastErrorString(void);
|
||||
|
||||
// ============================================================================
|
||||
// Debugging
|
||||
// ============================================================================
|
||||
|
||||
// Enable/disable verbose debug output to stderr.
|
||||
void wdrvSetDebug(bool enable);
|
||||
|
||||
// Dump all segment base addresses for debugging.
|
||||
void wdrvDumpSegmentBases(WdrvHandleT handle);
|
||||
|
||||
#endif // WINDRV_H
|
||||
1400
win31drv/winstub.c
Normal file
1400
win31drv/winstub.c
Normal file
File diff suppressed because it is too large
Load diff
257
win31drv/winstub.h
Normal file
257
win31drv/winstub.h
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#ifndef WINSTUB_H
|
||||
#define WINSTUB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "wintypes.h"
|
||||
#include "thunk.h"
|
||||
#include "neload.h"
|
||||
|
||||
// ============================================================================
|
||||
// Windows API stub layer
|
||||
//
|
||||
// Provides minimal implementations of KERNEL, GDI, and USER functions
|
||||
// that Windows 3.x display drivers import. These stubs are registered as
|
||||
// 16-bit callbacks via the thunking layer so the driver can call them.
|
||||
//
|
||||
// Supported modules:
|
||||
// KERNEL - Memory management (GlobalAlloc/Lock/Free), module queries,
|
||||
// selector management, system info
|
||||
// GDI - Minimal DC management, palette, object stubs
|
||||
// USER - GetSystemMetrics, MessageBox (stub)
|
||||
// ============================================================================
|
||||
|
||||
// ============================================================================
|
||||
// Stub context
|
||||
// ============================================================================
|
||||
|
||||
#define STUB_MAX_ALLOCS 64 // Max GlobalAlloc blocks
|
||||
#define STUB_MAX_SELECTORS 32 // Extra selector allocations
|
||||
|
||||
// Pre-allocated DOS memory pool for GlobalDOSAlloc.
|
||||
// Avoids calling __dpmi_allocate_dos_memory at runtime (which uses INT
|
||||
// 21h AH=48h and walks the MCB chain) by bump-allocating from a single
|
||||
// block allocated at init time.
|
||||
#define STUB_DOS_POOL_SIZE 0x4000 // 16KB pool
|
||||
#define STUB_MAX_DOS_ALLOCS 8
|
||||
|
||||
typedef struct {
|
||||
// Memory allocation tracking
|
||||
struct {
|
||||
uint16_t handle; // HGLOBAL16
|
||||
uint32_t linearAddr; // Linear address
|
||||
uint16_t selector; // PM selector for the block
|
||||
uint32_t size; // Block size
|
||||
uint16_t lockCount; // Lock count
|
||||
bool inUse;
|
||||
} allocs[STUB_MAX_ALLOCS];
|
||||
uint16_t nextHandle; // Next handle value to assign
|
||||
|
||||
// Extra selectors (for AllocSelector, AllocCStoDSAlias, etc.)
|
||||
struct {
|
||||
uint16_t selector;
|
||||
bool inUse;
|
||||
} selectors[STUB_MAX_SELECTORS];
|
||||
|
||||
// DOS memory pool for GlobalDOSAlloc (pre-allocated at init)
|
||||
struct {
|
||||
uint16_t paraOff; // Paragraph offset from pool base
|
||||
uint16_t paragraphs; // Size in paragraphs
|
||||
uint16_t selector; // PM selector for this sub-block
|
||||
bool inUse;
|
||||
} dosAllocs[STUB_MAX_DOS_ALLOCS];
|
||||
uint16_t dosPoolSeg; // Real-mode segment of pool
|
||||
uint16_t dosPoolSel; // DPMI selector for pool (for freeing)
|
||||
uint16_t dosPoolParas; // Total pool size in paragraphs
|
||||
uint16_t dosPoolNextPara; // Next free paragraph offset (bump allocator)
|
||||
|
||||
// Well-known memory region selectors
|
||||
uint16_t biosDataSel; // 0040:0000 BIOS data area
|
||||
uint16_t vramSel; // A000:0000 VGA graphics RAM
|
||||
uint16_t monoTextSel; // B000:0000 Mono text video
|
||||
uint16_t colorTextSel; // B800:0000 Color text video
|
||||
uint16_t videoBiosSel; // C000:0000 Video BIOS ROM
|
||||
uint16_t upperMemD000Sel; // D000:0000 Upper memory
|
||||
uint16_t upperMemE000Sel; // E000:0000 Upper memory
|
||||
uint16_t sysBiosSel; // F000:0000 System BIOS ROM
|
||||
|
||||
// Thunk context reference
|
||||
ThunkContextT *thunkCtx;
|
||||
|
||||
// NE module reference (for GetProcAddress lookups)
|
||||
NeModuleT *neModule;
|
||||
|
||||
// Lookup table: module name + ordinal -> FarPtr16T
|
||||
// Built during stub registration
|
||||
struct {
|
||||
char module[16];
|
||||
uint16_t ordinal;
|
||||
FarPtr16T addr;
|
||||
} stubTable[256];
|
||||
uint16_t stubCount;
|
||||
|
||||
bool initialized;
|
||||
} StubContextT;
|
||||
|
||||
// ============================================================================
|
||||
// Stub layer functions
|
||||
// ============================================================================
|
||||
|
||||
// Initialize the stub layer. Must be called after thunkInit().
|
||||
bool stubInit(StubContextT *ctx, ThunkContextT *thunkCtx);
|
||||
|
||||
// Enable or disable verbose stub logging.
|
||||
void stubSetDebug(bool debug);
|
||||
|
||||
// Shut down the stub layer.
|
||||
void stubShutdown(StubContextT *ctx);
|
||||
|
||||
// Set the NE module reference for GetProcAddress lookups.
|
||||
void stubSetModule(StubContextT *ctx, NeModuleT *mod);
|
||||
|
||||
// Resolve an imported function. Called by the NE loader's import resolver.
|
||||
// Returns the 16-bit far pointer to the stub function, or FARPTR16_NULL
|
||||
// if the import is unknown.
|
||||
FarPtr16T stubResolveImport(StubContextT *ctx, const char *moduleName, uint16_t ordinal, const char *funcName);
|
||||
|
||||
// ============================================================================
|
||||
// KERNEL stubs - ordinal numbers
|
||||
// These are the most commonly imported KERNEL functions by display drivers.
|
||||
// ============================================================================
|
||||
|
||||
// Error handling
|
||||
#define KERNEL_ORD_FATALEXIT 1
|
||||
#define KERNEL_ORD_FATALAPPEXIT 137
|
||||
|
||||
// Memory management - Global
|
||||
#define KERNEL_ORD_GLOBALALLOC 15
|
||||
#define KERNEL_ORD_GLOBALREALLOC 16
|
||||
#define KERNEL_ORD_GLOBALFREE 17
|
||||
#define KERNEL_ORD_GLOBALLOCK 18
|
||||
#define KERNEL_ORD_GLOBALUNLOCK 19
|
||||
#define KERNEL_ORD_GLOBALSIZE 20
|
||||
#define KERNEL_ORD_GLOBALFLAGS 22
|
||||
#define KERNEL_ORD_GLOBALDOSALLOC 84
|
||||
#define KERNEL_ORD_GLOBALDOSFREE 85
|
||||
#define KERNEL_ORD_GLOBALDOSALLOC2 184 // Duplicate ordinal for GlobalDOSAlloc
|
||||
#define KERNEL_ORD_GLOBALDOSFREE2 185 // Duplicate ordinal for GlobalDOSFree
|
||||
|
||||
// Memory management - Local
|
||||
#define KERNEL_ORD_LOCALINIT 4
|
||||
#define KERNEL_ORD_LOCALALLOC 5
|
||||
#define KERNEL_ORD_LOCALREALLOC 6
|
||||
#define KERNEL_ORD_LOCALFREE 7
|
||||
#define KERNEL_ORD_LOCALLOCK 8
|
||||
#define KERNEL_ORD_LOCALUNLOCK 9
|
||||
#define KERNEL_ORD_LOCALSIZE 10
|
||||
|
||||
// Memory info
|
||||
#define KERNEL_ORD_GETFREESPACE 102
|
||||
#define KERNEL_ORD_LOCKSEGMENT 23
|
||||
#define KERNEL_ORD_UNLOCKSEGMENT 24
|
||||
#define KERNEL_ORD_SETSWAPAREA 81
|
||||
#define KERNEL_ORD_GETCURRENTPDB 82
|
||||
|
||||
// Module management
|
||||
#define KERNEL_ORD_GETMODULEHANDLE 47
|
||||
#define KERNEL_ORD_GETMODULEUSAGE 35
|
||||
#define KERNEL_ORD_GETPROFILEINT 48
|
||||
#define KERNEL_ORD_GETPROFILEINT2 57 // Alternate ordinal used by some drivers
|
||||
#define KERNEL_ORD_WRITEPROFILESTRING 59
|
||||
#define KERNEL_ORD_GETMODULEFILENAME 49
|
||||
#define KERNEL_ORD_GETPROCADDRESS 50
|
||||
#define KERNEL_ORD_LOADLIBRARY 95
|
||||
#define KERNEL_ORD_FREELIBRARY 96
|
||||
#define KERNEL_ORD_INITTASK 91
|
||||
#define KERNEL_ORD_GETEXEPTR 133
|
||||
|
||||
// Resource management
|
||||
#define KERNEL_ORD_FINDRESOURCE 60
|
||||
#define KERNEL_ORD_LOADRESOURCE 61
|
||||
#define KERNEL_ORD_FREERESOURCE 63
|
||||
#define KERNEL_ORD_LOCKRESOURCE 62
|
||||
#define KERNEL_ORD_SIZEOFRESOURCE 65
|
||||
|
||||
// Selector management
|
||||
#define KERNEL_ORD_ALLOCSELECTOR 175
|
||||
#define KERNEL_ORD_FREESELECTOR 176
|
||||
#define KERNEL_ORD_ALLOCCSTODSALIAS 170
|
||||
#define KERNEL_ORD_ALLOCDSTOCSALIAS 171
|
||||
#define KERNEL_ORD_SETSELECTORBASE 187
|
||||
#define KERNEL_ORD_GETSELECTORBASE 186
|
||||
#define KERNEL_ORD_SETSELECTORLIMIT 189
|
||||
#define KERNEL_ORD_PRESTOCHANGOSELECTOR 177
|
||||
#define KERNEL_ORD_SELECTORACCESSRIGHTS 196
|
||||
#define KERNEL_ORD_ALLOCSELECTORARRAY 206
|
||||
|
||||
// System info
|
||||
#define KERNEL_ORD_GETVERSION 3
|
||||
#define KERNEL_ORD_GETWINFLAGS 132
|
||||
#define KERNEL_ORD_GETSYSTEMDIRECTORY 135
|
||||
#define KERNEL_ORD_GETDOSENVIRONMENT 131
|
||||
#define KERNEL_ORD_GETPRIVATEPROFILEINT 127
|
||||
#define KERNEL_ORD_GETPRIVATEPROFILESTRING 128
|
||||
#define KERNEL_ORD_WRITEPRIVATEPROFILESTRING 129
|
||||
|
||||
// Selector arithmetic (variables, not functions)
|
||||
#define KERNEL_ORD___AHSHIFT 113
|
||||
#define KERNEL_ORD___AHINCR 114
|
||||
#define KERNEL_ORD___WINFLAGS 178
|
||||
|
||||
// Segment selectors to well-known memory regions (variables, not functions)
|
||||
#define KERNEL_ORD___0000H 183
|
||||
#define KERNEL_ORD___0040H 193
|
||||
#define KERNEL_ORD___A000H 174
|
||||
#define KERNEL_ORD___B000H 181
|
||||
#define KERNEL_ORD___B800H 182
|
||||
#define KERNEL_ORD___C000H 195
|
||||
#define KERNEL_ORD___D000H 179
|
||||
#define KERNEL_ORD___E000H 190
|
||||
#define KERNEL_ORD___F000H 194
|
||||
#define KERNEL_ORD___ROMBIOS 173
|
||||
|
||||
// Selector query
|
||||
#define KERNEL_ORD_GETSELECTORLIMIT 188
|
||||
#define KERNEL_ORD_GETSELECTORBASE2 186
|
||||
|
||||
// Debug
|
||||
#define KERNEL_ORD_OUTPUTDEBUGSTRING 115
|
||||
|
||||
// ============================================================================
|
||||
// GDI stubs - ordinal numbers
|
||||
// ============================================================================
|
||||
|
||||
#define GDI_ORD_CREATEDC 53
|
||||
#define GDI_ORD_DELETEDC 68
|
||||
#define GDI_ORD_SELECTOBJECT 45
|
||||
#define GDI_ORD_DELETEOBJECT 69
|
||||
#define GDI_ORD_GETDEVICECAPS 80
|
||||
#define GDI_ORD_SETBKCOLOR 1
|
||||
#define GDI_ORD_SETTEXTCOLOR 6
|
||||
#define GDI_ORD_GETPALETTE_GDI 5
|
||||
|
||||
// DIB Engine exports (from DIBENG.DLL or built into GDI)
|
||||
#define DIBENG_ORD_DIBBITBLT 1
|
||||
#define DIBENG_ORD_DIBOUTPUT 2
|
||||
#define DIBENG_ORD_DIBPIXEL 3
|
||||
#define DIBENG_ORD_DIBSTRBLT 4
|
||||
#define DIBENG_ORD_DIBCOLORINFO 5
|
||||
#define DIBENG_ORD_DIBREALIZE 6
|
||||
#define DIBENG_ORD_DIBCREATEBITMAP 7
|
||||
#define DIBENG_ORD_DIBSCANLR 8
|
||||
#define DIBENG_ORD_DIBEXTOUT 9
|
||||
|
||||
// ============================================================================
|
||||
// USER stubs - ordinal numbers
|
||||
// ============================================================================
|
||||
|
||||
#define USER_ORD_GETSYSTEMMETRICS 179
|
||||
#define USER_ORD_MESSAGEBOX 1
|
||||
|
||||
// ============================================================================
|
||||
// KEYBOARD stubs - ordinal numbers
|
||||
// ============================================================================
|
||||
|
||||
#define KEYBOARD_ORD_SCREENSWITCHENABLE 100
|
||||
|
||||
#endif // WINSTUB_H
|
||||
152
win31drv/wintypes.h
Normal file
152
win31drv/wintypes.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#ifndef WINTYPES_H
|
||||
#define WINTYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// ============================================================================
|
||||
// Windows 16-bit basic types
|
||||
// ============================================================================
|
||||
|
||||
typedef uint16_t WORD;
|
||||
typedef int16_t SWORD;
|
||||
typedef uint32_t DWORD;
|
||||
typedef int32_t LONG;
|
||||
typedef uint8_t BYTE;
|
||||
typedef int16_t BOOL16;
|
||||
|
||||
// 16-bit handle types
|
||||
typedef uint16_t HANDLE16;
|
||||
typedef uint16_t HWND16;
|
||||
typedef uint16_t HDC16;
|
||||
typedef uint16_t HMODULE16;
|
||||
typedef uint16_t HINSTANCE16;
|
||||
typedef uint16_t HGLOBAL16;
|
||||
typedef uint16_t HBITMAP16;
|
||||
typedef uint16_t HBRUSH16;
|
||||
typedef uint16_t HPEN16;
|
||||
typedef uint16_t HPALETTE16;
|
||||
typedef uint16_t HRGN16;
|
||||
typedef uint16_t HCURSOR16;
|
||||
typedef uint16_t ATOM16;
|
||||
|
||||
// ============================================================================
|
||||
// Far pointer types (16:16 segment:offset, stored little-endian)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint16_t offset;
|
||||
uint16_t segment;
|
||||
} FarPtr16T;
|
||||
|
||||
#define FARPTR16_NULL ((FarPtr16T){0, 0})
|
||||
|
||||
static inline FarPtr16T makeFarPtr16(uint16_t seg, uint16_t off)
|
||||
{
|
||||
FarPtr16T fp;
|
||||
fp.segment = seg;
|
||||
fp.offset = off;
|
||||
return fp;
|
||||
}
|
||||
|
||||
static inline uint32_t farPtr16ToLinear(FarPtr16T fp)
|
||||
{
|
||||
// For real-mode addresses only; PM addresses need descriptor lookup
|
||||
return ((uint32_t)fp.segment << 4) + fp.offset;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Color types
|
||||
// ============================================================================
|
||||
|
||||
typedef uint32_t ColorRefT;
|
||||
|
||||
#define RGB_RED(c) ((uint8_t)(c))
|
||||
#define RGB_GREEN(c) ((uint8_t)((c) >> 8))
|
||||
#define RGB_BLUE(c) ((uint8_t)((c) >> 16))
|
||||
#define MAKE_RGB(r, g, b) ((ColorRefT)(((BYTE)(r)) | ((WORD)((BYTE)(g)) << 8) | ((DWORD)((BYTE)(b)) << 16)))
|
||||
|
||||
// Palette index color reference
|
||||
#define PALETTEINDEX(i) ((ColorRefT)(0x01000000L | (DWORD)(WORD)(i)))
|
||||
|
||||
// ============================================================================
|
||||
// Geometry types (16-bit, packed for compatibility with Win16 structures)
|
||||
// ============================================================================
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
} Point16T;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
int16_t left;
|
||||
int16_t top;
|
||||
int16_t right;
|
||||
int16_t bottom;
|
||||
} Rect16T;
|
||||
|
||||
// ============================================================================
|
||||
// Windows constants
|
||||
// ============================================================================
|
||||
|
||||
// GetWinFlags return values
|
||||
#define WF_PMODE 0x0001
|
||||
#define WF_CPU286 0x0002
|
||||
#define WF_CPU386 0x0004
|
||||
#define WF_CPU486 0x0008
|
||||
#define WF_STANDARD 0x0010
|
||||
#define WF_ENHANCED 0x0020
|
||||
#define WF_80x87 0x0400
|
||||
|
||||
// GlobalAlloc flags
|
||||
#define GMEM_FIXED 0x0000
|
||||
#define GMEM_MOVEABLE 0x0002
|
||||
#define GMEM_ZEROINIT 0x0040
|
||||
#define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT)
|
||||
#define GPTR (GMEM_FIXED | GMEM_ZEROINIT)
|
||||
|
||||
// Boolean
|
||||
#define WIN_FALSE 0
|
||||
#define WIN_TRUE 1
|
||||
|
||||
// Null handle
|
||||
#define NULL16 ((uint16_t)0)
|
||||
|
||||
// ============================================================================
|
||||
// Raster operation codes (ROP2 and ROP3)
|
||||
// ============================================================================
|
||||
|
||||
#define R2_BLACK 1
|
||||
#define R2_NOTMERGEPEN 2
|
||||
#define R2_MASKNOTPEN 3
|
||||
#define R2_NOTCOPYPEN 4
|
||||
#define R2_MASKPENNOT 5
|
||||
#define R2_NOT 6
|
||||
#define R2_XORPEN 7
|
||||
#define R2_NOTMASKPEN 8
|
||||
#define R2_MASKPEN 9
|
||||
#define R2_NOTXORPEN 10
|
||||
#define R2_NOP 11
|
||||
#define R2_MERGENOTPEN 12
|
||||
#define R2_COPYPEN 13
|
||||
#define R2_MERGEPENNOT 14
|
||||
#define R2_MERGEPEN 15
|
||||
#define R2_WHITE 16
|
||||
|
||||
// Common ROP3 codes
|
||||
#define SRCCOPY 0x00CC0020L
|
||||
#define SRCPAINT 0x00EE0086L
|
||||
#define SRCAND 0x008800C6L
|
||||
#define SRCINVERT 0x00660046L
|
||||
#define SRCERASE 0x00440328L
|
||||
#define NOTSRCCOPY 0x00330008L
|
||||
#define NOTSRCERASE 0x001100A6L
|
||||
#define MERGECOPY 0x00C000CAL
|
||||
#define MERGEPAINT 0x00BB0226L
|
||||
#define PATCOPY 0x00F00021L
|
||||
#define PATPAINT 0x00FB0A09L
|
||||
#define PATINVERT 0x005A0049L
|
||||
#define DSTINVERT 0x00550009L
|
||||
#define BLACKNESS 0x00000042L
|
||||
#define WHITENESS 0x00FF0062L
|
||||
|
||||
#endif // WINTYPES_H
|
||||
Loading…
Add table
Reference in a new issue