WinComm/vbx/serial.c
Scott Duensing d68405550d Fix COMM.DRV calling conventions and add ComDEB compatibility
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>
2026-02-24 20:03:34 -06:00

253 lines
7.4 KiB
C

// serial.c - Serial port abstraction layer
//
// Wraps Windows 3.1 comm API for use by the MSComm VBX control.
// All functions operate on a comm ID returned by serialOpen().
#include <windows.h>
#include <string.h>
#include "serial.h"
// -----------------------------------------------------------------------
// Prototypes
// -----------------------------------------------------------------------
int16_t serialClose(int16_t commId);
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
int16_t serialEscape(int16_t commId, int16_t func);
int16_t serialFlush(int16_t commId, int16_t queue);
UINT serialGetEventMask(int16_t commId, UINT mask);
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat);
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize);
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len);
int16_t serialSetHandshaking(int16_t commId, int16_t mode);
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace);
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len);
// -----------------------------------------------------------------------
// serialClose - Close an open comm port
// -----------------------------------------------------------------------
int16_t serialClose(int16_t commId)
{
// Drop DTR and RTS before closing
EscapeCommFunction(commId, CLRDTR);
EscapeCommFunction(commId, CLRRTS);
return (int16_t)CloseComm(commId);
}
// -----------------------------------------------------------------------
// serialConfigure - Set baud, parity, data bits, stop bits
// -----------------------------------------------------------------------
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings)
{
DCB dcb;
char buf[64];
int rc;
// Get current state to preserve existing settings
rc = GetCommState(commId, &dcb);
if (rc != 0) {
return -1;
}
// Build DCB from "COMn:baud,parity,data,stop" string
wsprintf(buf, "COM%d:%s", (int)port, (LPSTR)settings);
rc = BuildCommDCB(buf, &dcb);
if (rc != 0) {
return -1;
}
// Ensure binary mode for reliable operation
dcb.fBinary = 1;
// Ensure comm ID matches our open port
dcb.Id = (BYTE)commId;
rc = SetCommState(&dcb);
if (rc != 0) {
return -1;
}
return 0;
}
// -----------------------------------------------------------------------
// serialEnableNotify - Enable WM_COMMNOTIFY messages
// -----------------------------------------------------------------------
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold)
{
// Enable event mask for modem status line changes, errors, and breaks
SetCommEventMask(commId, EV_CTS | EV_DSR | EV_RLSD | EV_RING | EV_ERR | EV_BREAK | EV_RXCHAR);
return EnableCommNotification(commId, hwnd, rxThreshold, txThreshold);
}
// -----------------------------------------------------------------------
// serialEscape - Execute escape function (DTR, RTS, break control)
// -----------------------------------------------------------------------
int16_t serialEscape(int16_t commId, int16_t func)
{
LONG rc;
rc = EscapeCommFunction(commId, func);
return (rc == 0) ? 0 : -1;
}
// -----------------------------------------------------------------------
// serialFlush - Flush receive or transmit buffer
// -----------------------------------------------------------------------
int16_t serialFlush(int16_t commId, int16_t queue)
{
return (int16_t)FlushComm(commId, queue);
}
// -----------------------------------------------------------------------
// serialGetEventMask - Get and clear event mask bits
// -----------------------------------------------------------------------
UINT serialGetEventMask(int16_t commId, UINT mask)
{
return GetCommEventMask(commId, mask);
}
// -----------------------------------------------------------------------
// serialGetStatus - Get error status and buffer counts
// -----------------------------------------------------------------------
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat)
{
return (int16_t)GetCommError(commId, stat);
}
// -----------------------------------------------------------------------
// serialOpen - Open a COM port with specified buffer sizes
// -----------------------------------------------------------------------
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize)
{
char name[8];
int commId;
wsprintf(name, "COM%d", (int)port);
commId = OpenComm(name, (UINT)inBufSize, (UINT)outBufSize);
return (int16_t)commId;
}
// -----------------------------------------------------------------------
// serialRead - Read data from receive buffer
// -----------------------------------------------------------------------
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len)
{
return (int16_t)ReadComm(commId, (LPSTR)buf, len);
}
// -----------------------------------------------------------------------
// serialSetHandshaking - Apply handshaking mode
// -----------------------------------------------------------------------
int16_t serialSetHandshaking(int16_t commId, int16_t mode)
{
DCB dcb;
int rc;
rc = GetCommState(commId, &dcb);
if (rc != 0) {
return -1;
}
// Clear all flow control settings
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fOutX = 0;
dcb.fInX = 0;
dcb.fRtsDisable = 0;
dcb.fRtsflow = 0;
switch (mode) {
case HANDSHAKE_XONXOFF:
dcb.fOutX = 1;
dcb.fInX = 1;
dcb.XonChar = 0x11; // Ctrl-Q
dcb.XoffChar = 0x13; // Ctrl-S
dcb.XonLim = 256;
dcb.XoffLim = 256;
break;
case HANDSHAKE_RTSCTS:
dcb.fOutxCtsFlow = 1;
dcb.fRtsflow = 1;
break;
case HANDSHAKE_BOTH:
dcb.fOutxCtsFlow = 1;
dcb.fRtsflow = 1;
dcb.fOutX = 1;
dcb.fInX = 1;
dcb.XonChar = 0x11;
dcb.XoffChar = 0x13;
dcb.XonLim = 256;
dcb.XoffLim = 256;
break;
case HANDSHAKE_NONE:
default:
break;
}
dcb.Id = (BYTE)commId;
rc = SetCommState(&dcb);
if (rc != 0) {
return -1;
}
return 0;
}
// -----------------------------------------------------------------------
// serialSetOptions - Apply null-discard and parity-replace settings
// -----------------------------------------------------------------------
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace)
{
DCB dcb;
int rc;
rc = GetCommState(commId, &dcb);
if (rc != 0) {
return -1;
}
dcb.fNull = nullDiscard ? 1 : 0;
if (parityReplace != NULL && parityReplace[0] != '\0') {
dcb.fParity = 1;
dcb.PeChar = parityReplace[0];
} else {
dcb.fParity = 0;
dcb.PeChar = '\0';
}
dcb.Id = (BYTE)commId;
rc = SetCommState(&dcb);
if (rc != 0) {
return -1;
}
return 0;
}
// -----------------------------------------------------------------------
// serialWrite - Write data to transmit buffer
// -----------------------------------------------------------------------
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len)
{
return (int16_t)WriteComm(commId, (LPSTR)buf, len);
}