WinDriver/CLAUDE.md
Scott Duensing fbb1cce5c3 Add TrueType font support via stb_truetype.h
Integrate stb_truetype.h to rasterize TTF glyphs into 1-bit .FNT v3
format consumed by Win3.x display drivers. Key implementation detail:
.FNT bitmaps use column-major byte order (all rows of byte-column 0
first, then byte-column 1, etc.), not row-major.

New API: wdrvLoadFontTtf(path, pointSize) loads any TTF at any size.
Demo 7 renders Liberation Sans/Serif/Mono at 16/20/24pt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 00:38:16 -06:00

8.4 KiB

Win31drv Project Memory

Build Environment

  • DJGPP cross-compiler: ~/djgpp/djgpp/bin/i586-pc-msdosdjgpp-gcc (GCC 12.2.0)
  • DJGPP binutils need libfl.so.2: stored in tools/lib/ (Makefiles set LD_LIBRARY_PATH)
  • CWSDPMI zip stored in tools/cwsdpmi.zip (extracted to bin/ during build)
  • DOSBox-X: flatpak run com.dosbox_x.DOSBox-X (installed as user flatpak)
  • CWSDPMI.EXE in bin/ directory for DPMI support under DOSBox-X
  • Config: dosbox-x.conf with S3 Trio64 machine type, 64MB RAM

Project Structure

windriver/
├── Makefile          # Top-level: builds demo, calls win31drv/Makefile
├── demo.c            # Demo program
├── dosbox-x.conf     # DOSBox-X config (S3 SVGA)
├── obj/              # Demo object files
├── bin/              # Executables + CWSDPMI.EXE
└── win31drv/         # Library
    ├── Makefile      # Builds libwindrv.a
    ├── obj/          # Library objects
    ├── neload.c/h    # NE format loader
    ├── neformat.h    # NE structures
    ├── thunk.c/h     # 32→16 bit thunking
    ├── windrv.c/h    # Main API
    ├── winstub.c/h   # Windows API stubs
    ├── winddi.h      # DDI structures
    └── wintypes.h    # Win16 types

DJGPP Portability Notes

  • uint32_t is unsigned long (not unsigned int) in DJGPP — use PRIu32/PRIX32 from <inttypes.h>
  • Always include <stdarg.h> explicitly for va_list/va_start/va_end
  • Headers must be self-contained (include their own dependencies)

Thunking Architecture Notes

  • SS == DS == DGROUP: Win3.x drivers assume SS == DS == DGROUP. VBESVGA's BBLT.ASM does PrestoChangeoSelector(SS, WorkSelector) to create a code alias for compiled blit code. thunkCall16 uses dgroupSel as SS (SP=0xFFF0) when available. Without this, the code alias has the wrong base and the CPU executes garbage.
  • Register corruption with -O2 inlining: When demo.c's demoDrawing is inlined into main, DJGPP GCC 12.2.0 mishandles callee-saved registers across thunk calls in long functions. Fix: __attribute__((noinline)) on demoDrawing. Symptom: handle pointer corrupted to a ColorInfo return value (e.g. 0xFF0001F6) between Demo 2 and Demo 3.

DOSBox-X Driver Notes

  • waitForEngine(): GP_STAT port 0x9AE8 bit 9 polling — S3 only (gIsS3 guard)
  • S3 detection: Probe CR30 chip ID register. S3 chips: 0x81-0xE1. ET4000: 0x00. Only apply S3-specific setup (cursor disable, dispYOffset, setDisplayStart) when isS3=true AND driver is not VGA-class (1bpp/4planes).
  • Pattern scratch artifact: S3 driver writes 8x8 dithered brush pattern to VRAM at fixed position (~(144,1)-(151,8)) during accelerated pattern fills. Fixed by shifting CRTC display start down 10 scanlines (dispYOffset) so the scratch area is off-screen.
  • -fno-gcse required for windrv.c: With -O2 GCSE, stack layout causes issues during 16-bit driver calls. Only windrv.c needs this. See WINDRV_CFLAGS in win31drv/Makefile.
  • Output DDI (polylines/rectangles) requires a physical pen from RealizeObject, not a raw LogPen16T. The pen must be in DGROUP (same as brush, drawMode, PDEVICE).
  • wdrvUnloadDriver does NOT auto-call Disable — caller must handle text mode restore
  • sleep() hangs under DOSBox-X because BIOS timer ticks don't advance without I/O
  • Debug output: -d flag enables verbose logging in neload, winstub, thunk, and windrv
  • Known issue: mode mismatch HW=800x600 vs GDIINFO=640x480

DGROUP Stack Management

  • VGA.DRV ships with DGROUP[0x0A]=0xFFFF (stack bottom = top of segment → no stack). Its BitBlt prolog calls a stack check function at 0x18CA that compares available stack against [SS:0x0A]. With 0xFFFF, ALL functions fail immediately (return 0).
  • Fix: patch [0x0A] to objBase after extending DGROUP. Only done when original = 0xFFFF.
  • S3TRIO.DRV and VBESVGA.DRV have [0x0A]=0x0000 — no patching needed.
  • Do NOT unconditionally overwrite DGROUP offsets 0x00-0x0F — VBESVGA.DRV stores driver-specific data there (0x030A at offset 0, 0x01 at offset 4).

BitBlt Source Device Rules

  • For pattern-only ROPs (PATCOPY=0xF0, BLACKNESS=0x00, WHITENESS=0xFF, etc.), lpSrcDev must be NULL (0:0) per DDI spec. VGA.DRV rejects non-NULL source for pattern-only ROPs. S3TRIO.DRV tolerates it but correct behavior is NULL.
  • Source dependency check: ropNeedsSrc = (((rop8 >> 2) ^ rop8) & 0x33) != 0

ExtTextOut DDI Notes

  • Font format: VBESVGA.DRV requires .FNT v3 (fsVersion=0x0300) when BigFontFlags is set (386 protected mode with WF_PMODE + WF_CPU386). v2 (0x0200) is rejected at runtime.
  • v3 char table: at file offset 0x94 (fs30CharOffset), 6-byte entries {WORD width, DWORD offset}. The DWORD offset is absolute from the font segment base. Use FntCharEntry30T.
  • Bitmap layout: per-character contiguous, column-major byte order. For each character, all pixHeight rows of byte-column 0 come first, then all rows of byte-column 1, etc. Address formula: (byteCol * pixHeight) + row. For 8px-wide chars (1 byte column), this is identical to row-major. VGA BIOS 8x16 font is already in this format — no transpose needed.
  • lpClipRect must NOT be NULL: VBESVGA's get_clip unconditionally dereferences lpClipRect (STRBLT.ASM:1008 "We assume that we will never get passed a null rectangle"). Pass a RECT covering the full screen (0, 0, 0x7FFF, 0x7FFF).
  • lpTextXForm: declared but never read by VBESVGA — pass NULL.
  • lp_font offset: passed as fontSel:0x42 (points to fsType within the .FNT block).
  • Return value: DX bit 15 = error. AX=0 is NOT necessarily failure.

INT 10h ES Translation

  • Different INT 10h function families use different ES:offset registers: VBE 4Fxx → ES:DI, AH=10h (palette) → ES:DX, AH=11h (fonts) → ES:BP, AH=1Bh → ES:DI
  • Only specific AL subfunctions use ES as a buffer pointer; most don't
  • Copy sizes must be exact (17 bytes for palette, CX*3 for DAC blocks, etc.)
  • Copy direction matters: "Set" = copy-in only, "Read/Get" = copy-out only

WINFLAGS Handling

  • WF_80x87 NOT used: We don't save/restore FPU state across thunk boundaries
  • VGA-class drivers need WF_STANDARD: VGA.DRV's physical_enable hangs in Enhanced mode (polls VDD that doesn't exist). Auto-detected after Enable(style=1) returns 1bpp/4planes GDIINFO → repatch __WINFLAGS in all segments (0x0025→0x0015).
  • SVGA drivers (S3TRIO, VBESVGA) use WF_ENHANCED normally

ET4000 Driver Notes

  • ET4000.DRV from Win 3.x distribution is SZDD-compressed; decompress with msexpand (rename to .DR_ first, output is .DR without the V — rename to .DRV)
  • DOSBox-X machine type: svga_et4000 for ET4000 hardware emulation
  • ET4000 is 640x480 8bpp, software-rendered (no accelerator engine in DOSBox-X)
  • CR30=0x00 on ET4000 → isS3=false → no S3 engine wait, no display start shift

Font Loading Notes

  • .FON files are NE containers with RT_FONT (type 8) resources; each resource is raw .FNT data
  • All Win 3.x .FON files contain v2 fonts (0x0200); VBESVGA.DRV requires v3 (0x0300)
  • v2→v3 conversion: insert 30-byte extension at 0x76, expand 4-byte char table to 6-byte entries
  • v2 char table offsets are absolute from segment base, not relative to fsBitsOffset. Correct v3 offset = v2offset + shift (where shift = newBitmapOff - origBitsOff)
  • wdrvLoadFontFon(path, index) loads from .FON; wdrvLoadFontFnt(path) loads raw .FNT
  • wdrvLoadFontBuiltin() returns the VGA ROM 8x16 singleton; must NOT be passed to wdrvUnloadFont
  • wdrvLoadFontTtf(path, pointSize) loads TrueType via stb_truetype, rasterizes to 1-bit v3 FNT
  • wdrvExtTextOut takes a WdrvFontT font parameter (NULL = built-in)
  • Available test fonts in fon/: COURE.FON (8x13, 9x16, 12x20), SSERIFE.FON, SERIFE.FON, VGASYS.FON, etc.
  • Available TTF fonts in ttf/: LIBMONO.TTF, LIBSANS.TTF, LIBSERIF.TTF (Liberation family)

Current Demo Status

  • S3TRIO.DRV, VBESVGA.DRV, VGA.DRV, ET4000.DRV all work: Load → Enable → Draw → Disable → Unload
  • Demo 1: Fill rectangles (BitBlt) — works
  • Demo 2: Pixel patterns (Pixel) — works
  • Demo 3: Lines/starburst (Output/Polyline) — works
  • Demo 4: Screen-to-screen blit (BitBlt SRCCOPY) — works
  • Demo 5: ExtTextOut text rendering — works (VBESVGA.DRV)
  • Demo 7: TrueType font rendering at multiple sizes — works
  • VGA.DRV: 640x480 4-plane 16-color mode; limited color palette but functional
  • ET4000.DRV: 640x480 8bpp on svga_et4000; software-only, no hw acceleration
  • Drivers stored in drivers/ directory, copied to bin/ during build