setque (called by USER.EXE after every inicom) was running GlobalAlloc and
GlobalFree with interrupts disabled. On the second open the global heap
could need compaction or DPMI selector work, deadlocking under CLI.
Restructured to allocate new buffers first, swap pointers under CLI, then
free old buffers after STI.
Ring buffers changed from GMEM_MOVEABLE to GMEM_FIXED so addresses are
inherently stable at interrupt time. Added rxBufH/txBufH handle fields
to PortStateT, eliminating the fragile GlobalHandle(SELECTOROF()) recovery
in freeBuffers.
trmcom now resets FIFOs and drains all pending UART conditions (LSR, MSR,
RBR, IIR) before unhooking the ISR, leaving hardware clean for reopen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Data now hits the screen immediately when it arrives — no artificial
delay. The render throttle (RenderTickMs, FLastRenderTick) is removed
entirely. ParseData and EndUpdate call FlipToScreen unconditionally;
callers control batching via BeginUpdate/EndUpdate.
Blink toggle no longer calls DirtyAll. New DirtyBlinkRows method only
marks the cursor row and rows containing blink cells, reducing blink
overhead from ~63ms (25 rows) to ~3ms (1-3 rows) on a 486. Cursor
ghost handling in FlipToScreen dirties the old cursor row when the
cursor moves between rows.
Constant mini-frame values (Stride, CellH, PixSeg, GlyphSeg) are
pushed once before the column loop instead of per-cell, saving 320
push instructions per row.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrite RenderRow inner loop: split each glyph byte into two nibbles,
look up 4 pre-resolved palette bytes per nibble from a 64-byte table,
and write as word stores — zero branching in the hot path. Replace 25
per-row GlobalAlloc buffers with a single reusable buffer and move glyph
data into a GlobalAlloc'd block shared with the nibble table. All
arithmetic is 16-bit Word (no Longint). Uses mini-frame technique to
safely access local variables from inline ASM after DS/SI/DI clobber.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Build a monochrome font atlas at startup (BuildAtlas), then render
terminal cells by writing palette indices directly into 8bpp DIB row
buffers (RenderRow). Each dirty row reaches the screen via a single
SetDIBitsToDevice call instead of ~12 GDI calls (TextOut, SetTextColor,
SetBkColor, BitBlt). This reduces per-frame GDI overhead by ~10x,
targeting smooth playback of BBS door games on Win16.
Key changes:
- TTermCell FG/BG from TColor to Byte (palette index 0-15)
- Font atlas: render 256 CP437 glyphs into monochrome bitmap, extract
per-glyph pixel masks via GetBitmapBits
- Per-row 8bpp DIB buffers via GlobalAlloc replace dual memory DCs
- RenderRow: zero-GDI atlas lookup + byte writes with cursor overlay
- FlipToScreen: ScrollDC on screen only, SetDIBitsToDevice per dirty row
- Text blink via FTextBlinkOn + re-render replaces dual-buffer phase swap
- Removed: CreateBuffers, DestroyBuffers, PaintLine, ClearBufRect,
RedrawBuffers, DrawRow, FBufDC/FBufBmp/FBlinkPhase fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes:
1. Cursor font mismatch: cursor overlay TextOut used the default DC
font instead of FPaintFont (OEM_CHARSET). The wrong font size left
residual pixels that buffer BitBlt didn't fully cover. Fix: select
FPaintFont + OPAQUE BkMode into the CS_OWNDC screen DC once in
RecalcCellSize, and re-assert before cursor TextOut in case VCL
Paint altered the DC state.
2. WM_TIMER starvation: on Win16, WM_TIMER is lowest-priority and
never dispatched while WM_COMMNOTIFY floods the queue. Fix: render
from EndUpdate using GetTickCount throttle (no timer dependency).
The timer remains as fallback for idle blink and trailing data.
3. Scroll performance: restore deferred ScrollDC at render time (not
during parsing) so only newly-revealed rows need PaintLine instead
of all 25. Dirty flags shift with each DoScrollUp to track the
correct logical line positions after scroll.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Data parsing (ProcessChar) is now purely memory operations. EndUpdate
and ParseData set FRenderPending instead of calling FlipToScreen.
A 55ms timer (~18 Hz, matching Win16 tick resolution) drives rendering:
dirty rows are painted and BitBlt'd to screen only on timer ticks.
Blink toggles every 9 ticks (~500ms). This prevents high-throughput
data (door games, file transfers) from saturating the CPU with GDI
calls — between timer ticks, data just accumulates in cell buffers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ScrollDC shifted buffer pixels during parsing before dirty rows were
rendered, causing unrendered garbage to propagate into non-dirty rows
on successive scrolls. Replace with FAllDirty to repaint all rows from
cell data after any scroll. With batched TextOut this costs ~250 GDI
calls per scroll instead of the old 12,000, so the overhead is minimal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Separate parsing from rendering to eliminate per-character GDI calls.
ProcessChar now only updates cell data in memory; rendering is deferred
to FlipToScreen which batches consecutive same-color cells into single
TextOut calls (~5-10 per row instead of 80). Partial BitBlt transfers
only the dirty row band to the screen. Non-blinking rows render to one
buffer and BitBlt to the second, halving GDI work for typical content.
Also removes redundant GetCommError from KPComm receive path and adds
BeginUpdate/EndUpdate batching in the test app's CommEvent handler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace TBitmap back buffer with raw GDI memory DC to prevent Delphi's
TCanvas from overriding the OEM_CHARSET font selection. Add OUT_RASTER_PRECIS
to CreateFontIndirect to ensure Windows maps to a true CP437 raster font
instead of a TrueType substitution. Optimize paint loop with fixed char
array instead of string concatenation. Restore Update call in ParseData
so WM_PAINT is not starved by WM_COMMNOTIFY. Add text blink support via
timer-driven FG/BG toggling and store blink as cell attribute instead of
mapping to bright background.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Handle ENQ (0x05), ESC[c (Device Attributes), ESC[5n (Device Status
Report), and ESC[6n (Cursor Position Report). Responses are sent
back through OnKeyData so BBSes can detect ANSI terminal capability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TKPAnsi is a TCustomControl descendant providing a visual ANSI terminal
with 16-color palette, scrollback buffer, blinking cursor, and ANSI music.
Supports CSI sequences (cursor movement, erase, SGR colors/attributes,
insert/delete lines/chars, scroll), DEC private modes (wrap, cursor
visibility), and keyboard translation (arrows, function keys, etc.).
Test app (TESTMAIN.PAS) updated to wire TKPAnsi to TKPComm as a
full terminal: received data feeds the terminal display, keystrokes
are sent out the serial port.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VBX registration is non-functional in VB4 16-bit (VBRegisterModel is a
no-op, VBGetModelInfo never called). Native Delphi component avoids all
DLL/export/registration issues — compiles directly into the IDE.
TKPComm is a TComponent descendant calling the Windows 3.1 comm API
directly. Uses RegisterClass/CreateWindow for WM_COMMNOTIFY dispatch
with the component instance pointer stored in cbWndExtra. Includes a
test application (KPTEST) with send/receive UI built entirely in code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve GPFs in both USER.EXE and COMMTASK.DLL by correcting RETF
sizes for SETCOM and GETDCB, confirmed by disassembling CyberCom.DRV.
Add stock-compatible ComDEB structure so third-party code (ProComm
COMMTASK.DLL) can safely access internal fields at known offsets per
Microsoft KB Q101417.
COMM.DRV changes:
- SETCOM (ord 2): RETF 4, takes DCB FAR * only (not commId + DCB*)
- GETDCB (ord 15): RETF 2, takes commId, returns DCB FAR * in DX:AX
- Add 40-byte ComDebT matching stock COMM.DRV internal layout
(evtWord at +0, MSR shadow at +35, queue counts at +8/+18)
- cevt returns pointer to ComDebT for third-party compatibility
- Sync ComDEB fields in ISR dispatch, reccom, sndcom, cflush, setque
- Move WEP to ordinal 16, add ordinal 101 stub (match stock/CyberCom)
- Default DBG_ENABLED to 0 (set to 1 to re-enable debug logging)
VBX control fixes:
- Fix SETBREAK/CLRBREAK constants (use numeric 8/9, not undefined macros)
- Fix serialEnableNotify return type (BOOL, not int16_t)
- Fix mscomm.h to work with RC compiler (#ifndef RC_INVOKED)
- Fix vbapi.h USHORT typedef and MODEL.flWndStyle type
- Add vbapi.lib dependency to makefile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>