Compare commits

...

2 commits

Author SHA1 Message Date
4218da32eb Rename output from COMM.DRV to KPCOMM.DRV to avoid clashing with stock driver
LIBRARY name stays COMM for import resolution. Debug strings updated to KPCOMM.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 20:07:45 -06:00
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
10 changed files with 525 additions and 80 deletions

View file

@ -1,4 +1,4 @@
// commdrv.c - High-speed COMM.DRV replacement // commdrv.c - KPCOMM.DRV -- High-speed COMM.DRV replacement
// //
// All exported COMM.DRV functions, ring buffer management, port // All exported COMM.DRV functions, ring buffer management, port
// initialization, 16550 FIFO detection, and flow control. // initialization, 16550 FIFO detection, and flow control.
@ -10,12 +10,154 @@
#include <dos.h> #include <dos.h>
#include <string.h> #include <string.h>
// -----------------------------------------------------------------------
// Debug logging via OutputDebugString
// -----------------------------------------------------------------------
#define DBG_ENABLED 0
#if DBG_ENABLED
static void dbgStr(const char FAR *msg)
{
OutputDebugString(msg);
}
// Log a label and a 16-bit hex value: "label: 0xNNNN\r\n"
static void dbgHex16(const char FAR *label, uint16_t val)
{
static const char hex[] = "0123456789ABCDEF";
char buf[48];
int i;
for (i = 0; i < 40 && label[i]; i++) {
buf[i] = label[i];
}
buf[i++] = ':';
buf[i++] = ' ';
buf[i++] = '0';
buf[i++] = 'x';
buf[i++] = hex[(val >> 12) & 0xF];
buf[i++] = hex[(val >> 8) & 0xF];
buf[i++] = hex[(val >> 4) & 0xF];
buf[i++] = hex[val & 0xF];
buf[i++] = '\r';
buf[i++] = '\n';
buf[i] = '\0';
OutputDebugString(buf);
}
// Log a label and a signed 16-bit value: "label: -NNNN\r\n"
static void dbgInt16(const char FAR *label, int16_t val)
{
char buf[48];
int i;
uint16_t uv;
for (i = 0; i < 40 && label[i]; i++) {
buf[i] = label[i];
}
buf[i++] = ':';
buf[i++] = ' ';
if (val < 0) {
buf[i++] = '-';
uv = (uint16_t)(-(int)val);
} else {
uv = (uint16_t)val;
}
// Print decimal (up to 5 digits)
{
char digits[6];
int n = 0;
do {
digits[n++] = (char)('0' + (uv % 10));
uv /= 10;
} while (uv > 0);
while (n > 0) {
buf[i++] = digits[--n];
}
}
buf[i++] = '\r';
buf[i++] = '\n';
buf[i] = '\0';
OutputDebugString(buf);
}
// Log a FAR pointer as "label: SSSS:OOOO\r\n"
static void dbgFarPtr(const char FAR *label, void FAR *ptr)
{
static const char hex[] = "0123456789ABCDEF";
char buf[48];
int i;
uint16_t seg;
uint16_t off;
seg = _FP_SEG(ptr);
off = _FP_OFF(ptr);
for (i = 0; i < 32 && label[i]; i++) {
buf[i] = label[i];
}
buf[i++] = ':';
buf[i++] = ' ';
buf[i++] = hex[(seg >> 12) & 0xF];
buf[i++] = hex[(seg >> 8) & 0xF];
buf[i++] = hex[(seg >> 4) & 0xF];
buf[i++] = hex[seg & 0xF];
buf[i++] = ':';
buf[i++] = hex[(off >> 12) & 0xF];
buf[i++] = hex[(off >> 8) & 0xF];
buf[i++] = hex[(off >> 4) & 0xF];
buf[i++] = hex[off & 0xF];
buf[i++] = '\r';
buf[i++] = '\n';
buf[i] = '\0';
OutputDebugString(buf);
}
// Hex dump first 'count' bytes at a FAR pointer
static void dbgDump(const char FAR *label, void FAR *ptr, int count)
{
static const char hex[] = "0123456789ABCDEF";
char buf[80];
uint8_t FAR *p;
int i;
int bi;
p = (uint8_t FAR *)ptr;
// Print label
for (bi = 0; bi < 32 && label[bi]; bi++) {
buf[bi] = label[bi];
}
buf[bi++] = ':';
buf[bi++] = ' ';
for (i = 0; i < count && bi < 70; i++) {
buf[bi++] = hex[(p[i] >> 4) & 0xF];
buf[bi++] = hex[p[i] & 0xF];
buf[bi++] = ' ';
}
buf[bi++] = '\r';
buf[bi++] = '\n';
buf[bi] = '\0';
OutputDebugString(buf);
}
#else
#define dbgStr(msg) ((void)0)
#define dbgHex16(label, val) ((void)0)
#define dbgInt16(label, val) ((void)0)
#define dbgFarPtr(label, p) ((void)0)
#define dbgDump(l, p, n) ((void)0)
#endif
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Prototypes // Prototypes
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize); int16_t FAR PASCAL _export inicom(DCB FAR *dcb);
int16_t FAR PASCAL _export setcom(DCB FAR *dcb); int16_t FAR PASCAL _export setcom(DCB FAR *dcb);
int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBuf, int16_t rxSize, char FAR *txBuf, int16_t txSize); int16_t FAR PASCAL _export setque(int16_t commId, int16_t rxSize, int16_t txSize);
int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch); int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch);
@ -27,13 +169,14 @@ int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func);
int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue); int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue);
int16_t FAR PASCAL _export csetbrk(int16_t commId); int16_t FAR PASCAL _export csetbrk(int16_t commId);
int16_t FAR PASCAL _export cclrbrk(int16_t commId); int16_t FAR PASCAL _export cclrbrk(int16_t commId);
int16_t FAR PASCAL _export getdcb(int16_t commId, DCB FAR *dcb); DCB FAR * FAR PASCAL _export getdcb(int16_t commId);
void FAR PASCAL _export suspendOpenCommPorts(void); void FAR PASCAL _export suspendOpenCommPorts(void);
void FAR PASCAL _export reactivateOpenCommPorts(void); void FAR PASCAL _export reactivateOpenCommPorts(void);
int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh); int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh);
int FAR PASCAL _export commNotifyWndProc(void);
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpCmdLine); int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpCmdLine);
int FAR PASCAL _export WEP(int nParam); int FAR PASCAL _export WEP(int nParam);
@ -46,7 +189,7 @@ static int16_t freeBuffers(PortStateT *port);
static int16_t initBuffers(PortStateT *port, uint16_t rxSz, uint16_t txSz); static int16_t initBuffers(PortStateT *port, uint16_t rxSz, uint16_t txSz);
static void initPortState(PortStateT *port, int16_t commId); static void initPortState(PortStateT *port, int16_t commId);
void primeTx(PortStateT *port); void primeTx(PortStateT *port);
static void readPortConfig(int16_t commId, uint16_t *baseAddr, uint8_t *irq); static void readPortConfig(int16_t commId, uint16_t FAR *baseAddr, uint8_t FAR *irq);
static uint16_t readSystemIni(const char FAR *section, const char FAR *key, uint16_t defVal); static uint16_t readSystemIni(const char FAR *section, const char FAR *key, uint16_t defVal);
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -70,6 +213,18 @@ PortStateT ports[MAX_PORTS];
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
static HANDLE ghInstance = NULL; static HANDLE ghInstance = NULL;
// -----------------------------------------------------------------------
// Dynamically resolved PostMessage
//
// comm.drv is loaded from [boot] before USER.EXE, so we can't have a
// static import for PostMessage. Resolve it on first use via
// GetProcAddress(GetModuleHandle("USER"), "PostMessage").
// -----------------------------------------------------------------------
PostMessageProcT pfnPostMessage = NULL;
// ISR hit counter for diagnostics
volatile uint16_t isrHitCount = 0;
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// applyBaudRate - Set the baud rate divisor on the UART // applyBaudRate - Set the baud rate divisor on the UART
@ -215,6 +370,8 @@ int16_t FAR PASCAL _export cclrbrk(int16_t commId)
PortStateT *port; PortStateT *port;
uint8_t lcr; uint8_t lcr;
dbgInt16("KPCOMM: cclrbrk Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -243,6 +400,22 @@ int32_t FAR PASCAL _export cevt(int16_t commId, int16_t evtMask)
{ {
PortStateT *port; PortStateT *port;
uint16_t FAR *ptr; uint16_t FAR *ptr;
int32_t retVal;
#if DBG_ENABLED
{
uint16_t ssSeg;
uint16_t bpVal;
_asm mov ssSeg, ss
_asm mov bpVal, bp
dbgHex16("KPCOMM: cevt SS", ssSeg);
dbgHex16("KPCOMM: cevt BP", bpVal);
dbgDump("KPCOMM: cevt stk", (void FAR *)((uint32_t)ssSeg << 16 | (bpVal + 6)), 16);
}
#endif
dbgInt16("KPCOMM: cevt Id", commId);
dbgHex16("KPCOMM: cevt mask", (uint16_t)evtMask);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return 0L; return 0L;
@ -250,13 +423,18 @@ int32_t FAR PASCAL _export cevt(int16_t commId, int16_t evtMask)
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("KPCOMM: cevt not open\r\n");
return 0L; return 0L;
} }
port->evtMask = (uint16_t)evtMask; port->comDeb.evtMask = (uint16_t)evtMask;
ptr = &port->evtWord; ptr = &port->comDeb.evtWord;
return (int32_t)(void FAR *)ptr; retVal = (int32_t)(void FAR *)ptr;
dbgHex16("KPCOMM: cevt retSeg", (uint16_t)(retVal >> 16));
dbgHex16("KPCOMM: cevt retOff", (uint16_t)(retVal & 0xFFFF));
dbgStr("KPCOMM: cevt OK\r\n");
return retVal;
} }
@ -270,6 +448,8 @@ uint16_t FAR PASCAL _export cevtget(int16_t commId, int16_t evtMask)
PortStateT *port; PortStateT *port;
uint16_t events; uint16_t events;
dbgInt16("KPCOMM: cevtget Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return 0; return 0;
} }
@ -280,8 +460,8 @@ uint16_t FAR PASCAL _export cevtget(int16_t commId, int16_t evtMask)
} }
_disable(); _disable();
events = port->evtWord & (uint16_t)evtMask; events = port->comDeb.evtWord & (uint16_t)evtMask;
port->evtWord &= ~(uint16_t)evtMask; port->comDeb.evtWord &= ~(uint16_t)evtMask;
_enable(); _enable();
return events; return events;
@ -299,6 +479,9 @@ int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func)
uint8_t mcr; uint8_t mcr;
uint16_t base; uint16_t base;
dbgInt16("KPCOMM: cextfcn Id", commId);
dbgInt16("KPCOMM: cextfcn func", func);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -328,9 +511,9 @@ int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func)
mcr &= ~MCR_RTS; mcr &= ~MCR_RTS;
port->rtsState = FALSE; port->rtsState = FALSE;
break; break;
case SETBREAK: case ESC_SETBREAK:
return csetbrk(commId); return csetbrk(commId);
case CLRBREAK: case ESC_CLRBREAK:
return cclrbrk(commId); return cclrbrk(commId);
case RESETDEV: case RESETDEV:
// No-op for serial ports // No-op for serial ports
@ -364,6 +547,8 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue)
{ {
PortStateT *port; PortStateT *port;
dbgInt16("KPCOMM: cflush Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -378,6 +563,9 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue)
port->rxHead = 0; port->rxHead = 0;
port->rxTail = 0; port->rxTail = 0;
port->rxCount = 0; port->rxCount = 0;
port->comDeb.qInHead = 0;
port->comDeb.qInTail = 0;
port->comDeb.qInCount = 0;
// Reset FIFO to clear hardware buffer too // Reset FIFO to clear hardware buffer too
if (port->is16550 && port->fifoEnabled) { if (port->is16550 && port->fifoEnabled) {
_outp(port->baseAddr + UART_FCR, FCR_ENABLE | FCR_RX_RESET | port->fifoTrigger); _outp(port->baseAddr + UART_FCR, FCR_ENABLE | FCR_RX_RESET | port->fifoTrigger);
@ -386,6 +574,9 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue)
port->txHead = 0; port->txHead = 0;
port->txTail = 0; port->txTail = 0;
port->txCount = 0; port->txCount = 0;
port->comDeb.qOutHead = 0;
port->comDeb.qOutTail = 0;
port->comDeb.qOutCount = 0;
if (port->is16550 && port->fifoEnabled) { if (port->is16550 && port->fifoEnabled) {
_outp(port->baseAddr + UART_FCR, FCR_ENABLE | FCR_TX_RESET | port->fifoTrigger); _outp(port->baseAddr + UART_FCR, FCR_ENABLE | FCR_TX_RESET | port->fifoTrigger);
} }
@ -401,10 +592,26 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len) int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len)
{ {
dbgInt16("KPCOMM: commWriteStr Id", commId);
return sndcom(commId, buf, len); return sndcom(commId, buf, len);
} }
// -----------------------------------------------------------------------
// commNotifyWndProc - Ordinal 101 stub (undocumented, Win 3.1 compat)
//
// The stock COMM.DRV and CyberCom export an unnamed function at ordinal
// 101. Purpose is undocumented; may be an internal callback used by
// USER.EXE. CyberCom's implementation uses plain RETF (no params).
// Return 0 (not handled).
// -----------------------------------------------------------------------
int FAR PASCAL _export commNotifyWndProc(void)
{
dbgStr("KPCOMM: ord101 called\r\n");
return 0;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// csetbrk - Assert break signal (ordinal 13) // csetbrk - Assert break signal (ordinal 13)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -413,6 +620,8 @@ int16_t FAR PASCAL _export csetbrk(int16_t commId)
PortStateT *port; PortStateT *port;
uint8_t lcr; uint8_t lcr;
dbgInt16("KPCOMM: csetbrk Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -437,6 +646,8 @@ int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch)
{ {
PortStateT *port; PortStateT *port;
dbgInt16("KPCOMM: ctx Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -504,19 +715,35 @@ int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t
{ {
PortStateT *port; PortStateT *port;
dbgInt16("KPCOMM: enableNotif Id", commId);
dbgHex16("KPCOMM: enableNotif hwnd", (uint16_t)hwnd);
dbgInt16("KPCOMM: enableNotif rxTh", rxThresh);
dbgInt16("KPCOMM: enableNotif txTh", txThresh);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return FALSE; return FALSE;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("KPCOMM: enableNotif not open\r\n");
return FALSE; return FALSE;
} }
// Lazily resolve PostMessage from USER.EXE
// (not available at boot when comm.drv is loaded from [boot])
if (!pfnPostMessage) {
HMODULE hUser = GetModuleHandle("USER");
if (hUser) {
pfnPostMessage = (PostMessageProcT)GetProcAddress(hUser, "PostMessage");
}
}
port->hwndNotify = hwnd; port->hwndNotify = hwnd;
port->rxNotifyThresh = rxThresh; port->rxNotifyThresh = rxThresh;
port->txNotifyThresh = txThresh; port->txNotifyThresh = txThresh;
dbgStr("KPCOMM: enableNotif OK\r\n");
return TRUE; return TRUE;
} }
@ -551,23 +778,32 @@ static int16_t freeBuffers(PortStateT *port)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// getdcb - Copy current DCB settings (ordinal 15) // getdcb - Return pointer to internal DCB (ordinal 15)
//
// Takes int16_t commId (2 bytes, RETF 2). Returns DCB FAR * in DX:AX.
// The caller (USER.EXE / COMMTASK.DLL) copies from this pointer.
// CyberCom confirms: RETF 2, return pointer.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export getdcb(int16_t commId, DCB FAR *dcb) DCB FAR * FAR PASCAL _export getdcb(int16_t commId)
{ {
PortStateT *port; PortStateT *port;
dbgInt16("KPCOMM: getdcb Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; dbgStr("KPCOMM: getdcb bad id\r\n");
return NULL;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
return -1; dbgStr("KPCOMM: getdcb not open\r\n");
return NULL;
} }
_fmemcpy(dcb, &port->dcb, sizeof(DCB)); dbgHex16("KPCOMM: getdcb isrHits", isrHitCount);
return 0; dbgStr("KPCOMM: getdcb OK\r\n");
return &port->dcb;
} }
@ -581,7 +817,7 @@ int16_t FAR PASCAL _export getdcb(int16_t commId, DCB FAR *dcb)
// //
// Returns commId (0-3) on success, negative error code on failure. // Returns commId (0-3) on success, negative error code on failure.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize) int16_t FAR PASCAL _export inicom(DCB FAR *dcb)
{ {
int16_t commId; int16_t commId;
PortStateT *port; PortStateT *port;
@ -589,25 +825,52 @@ int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize)
uint16_t txBufSize; uint16_t txBufSize;
uint8_t mcr; uint8_t mcr;
(void)rxBuf; dbgStr("KPCOMM: inicom enter\r\n");
(void)rxSize;
// Dump raw stack to see actual parameters passed by caller
{
uint16_t bpVal;
uint16_t ssVal;
void FAR *stkPtr;
_asm mov bpVal, bp
_asm mov ssVal, ss
stkPtr = (void FAR *)((uint32_t)ssVal << 16 | (bpVal + 6));
dbgHex16("KPCOMM: inicom SS", ssVal);
dbgHex16("KPCOMM: inicom BP", bpVal);
dbgDump("KPCOMM: inicom stk", stkPtr, 24);
}
if (!dcb) { if (!dcb) {
dbgStr("KPCOMM: inicom NULL dcb\r\n");
return IE_DEFAULT; return IE_DEFAULT;
} }
dbgFarPtr("KPCOMM: inicom dcb", (void FAR *)dcb);
dbgDump("KPCOMM: inicom raw", (void FAR *)dcb, 16);
dbgHex16("KPCOMM: sizeof(DCB)", (uint16_t)sizeof(DCB));
commId = dcb->Id; commId = dcb->Id;
dbgInt16("KPCOMM: inicom Id", commId);
dbgHex16("KPCOMM: inicom BaudRate", dcb->BaudRate);
dbgHex16("KPCOMM: inicom ByteSize", (uint16_t)dcb->ByteSize);
dbgHex16("KPCOMM: inicom Parity", (uint16_t)dcb->Parity);
dbgHex16("KPCOMM: inicom StopBits", (uint16_t)dcb->StopBits);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("KPCOMM: inicom IE_BADID\r\n");
return IE_BADID; return IE_BADID;
} }
port = &ports[commId]; port = &ports[commId];
if (port->isOpen) { if (port->isOpen) {
dbgStr("KPCOMM: inicom IE_OPEN\r\n");
return IE_OPEN; return IE_OPEN;
} }
// Initialize port state // Initialize port state
initPortState(port, commId); initPortState(port, commId);
dbgHex16("KPCOMM: inicom baseAddr", port->baseAddr);
dbgHex16("KPCOMM: inicom irq", (uint16_t)port->irq);
// Read buffer sizes from SYSTEM.INI if available (COMnBuffer key) // Read buffer sizes from SYSTEM.INI if available (COMnBuffer key)
{ {
@ -626,20 +889,26 @@ int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize)
rxBufSize = readSystemIni("386Enh", bufKey, DEFAULT_RX_SIZE); rxBufSize = readSystemIni("386Enh", bufKey, DEFAULT_RX_SIZE);
} }
txBufSize = rxBufSize; txBufSize = rxBufSize;
dbgHex16("KPCOMM: inicom rxBufSize", rxBufSize);
// Allocate ring buffers // Allocate ring buffers
if (initBuffers(port, rxBufSize, txBufSize) != 0) { if (initBuffers(port, rxBufSize, txBufSize) != 0) {
dbgStr("KPCOMM: inicom IE_MEMORY\r\n");
return IE_MEMORY; return IE_MEMORY;
} }
dbgStr("KPCOMM: inicom buffers OK\r\n");
// Detect 16550 FIFO // Detect 16550 FIFO
port->is16550 = (uint8_t)detect16550(port->baseAddr); port->is16550 = (uint8_t)detect16550(port->baseAddr);
dbgHex16("KPCOMM: inicom is16550", (uint16_t)port->is16550);
// Hook ISR // Hook ISR
if (hookIsr(port) != 0) { if (hookIsr(port) != 0) {
dbgStr("KPCOMM: inicom hookIsr FAIL\r\n");
freeBuffers(port); freeBuffers(port);
return IE_HARDWARE; return IE_HARDWARE;
} }
dbgStr("KPCOMM: inicom ISR hooked\r\n");
port->isOpen = TRUE; port->isOpen = TRUE;
@ -675,12 +944,19 @@ int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize)
// Clear any pending status // Clear any pending status
_inp(port->baseAddr + UART_LSR); _inp(port->baseAddr + UART_LSR);
_inp(port->baseAddr + UART_MSR); port->comDeb.msrShadow = (uint8_t)_inp(port->baseAddr + UART_MSR);
_inp(port->baseAddr + UART_RBR); _inp(port->baseAddr + UART_RBR);
// Populate ComDEB for third-party compatibility
port->comDeb.port = port->baseAddr;
port->comDeb.baudRate = port->baudRate;
port->comDeb.qInSize = port->rxSize;
port->comDeb.qOutSize = port->txSize;
// Enable receive and line status interrupts // Enable receive and line status interrupts
_outp(port->baseAddr + UART_IER, IER_RDA | IER_LSI | IER_MSI); _outp(port->baseAddr + UART_IER, IER_RDA | IER_LSI | IER_MSI);
dbgInt16("KPCOMM: inicom OK, ret", commId);
return commId; return commId;
} }
@ -833,6 +1109,8 @@ void FAR PASCAL _export reactivateOpenCommPorts(void)
PortStateT *port; PortStateT *port;
uint8_t mcr; uint8_t mcr;
dbgStr("KPCOMM: reactivate\r\n");
for (i = 0; i < MAX_PORTS; i++) { for (i = 0; i < MAX_PORTS; i++) {
port = &ports[i]; port = &ports[i];
if (!port->isOpen) { if (!port->isOpen) {
@ -873,6 +1151,7 @@ void FAR PASCAL _export reactivateOpenCommPorts(void)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len) int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len)
{ {
dbgInt16("KPCOMM: readCommStr Id", commId);
return reccom(commId, buf, len); return reccom(commId, buf, len);
} }
@ -883,7 +1162,7 @@ int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t
// Checks [386Enh] for COMnBase (hex) and COMnIRQ overrides. // Checks [386Enh] for COMnBase (hex) and COMnIRQ overrides.
// Falls back to standard defaults if not present. // Falls back to standard defaults if not present.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
static void readPortConfig(int16_t commId, uint16_t *baseAddr, uint8_t *irq) static void readPortConfig(int16_t commId, uint16_t FAR *baseAddr, uint8_t FAR *irq)
{ {
char key[10]; char key[10];
char buf[16]; char buf[16];
@ -963,6 +1242,8 @@ int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len)
uint8_t FAR *dst; uint8_t FAR *dst;
int16_t bytesRead; int16_t bytesRead;
dbgInt16("KPCOMM: reccom Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -1002,29 +1283,47 @@ int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len)
_enable(); _enable();
} }
// Sync ComDEB queue counts
port->comDeb.qInCount = port->rxCount;
port->comDeb.qInTail = port->rxTail;
return bytesRead; return bytesRead;
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// setcom - Apply DCB settings to hardware (ordinal 2) // setcom - Apply DCB settings to hardware (ordinal 2)
//
// Takes DCB FAR * (4 bytes, RETF 4). CommId is in dcb->Id.
// Both USER.EXE and COMMTASK.DLL push 4 bytes for this call.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export setcom(DCB FAR *dcb) int16_t FAR PASCAL _export setcom(DCB FAR *dcb)
{ {
int16_t commId; int16_t commId;
PortStateT *port; PortStateT *port;
dbgStr("KPCOMM: setcom enter\r\n");
if (!dcb) { if (!dcb) {
dbgStr("KPCOMM: setcom NULL dcb\r\n");
return -1; return -1;
} }
commId = dcb->Id; commId = dcb->Id;
dbgInt16("KPCOMM: setcom Id", commId);
dbgHex16("KPCOMM: setcom BaudRate", dcb->BaudRate);
dbgHex16("KPCOMM: setcom ByteSize", (uint16_t)dcb->ByteSize);
dbgHex16("KPCOMM: setcom Parity", (uint16_t)dcb->Parity);
dbgHex16("KPCOMM: setcom StopBits", (uint16_t)dcb->StopBits);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("KPCOMM: setcom IE_BADID\r\n");
return IE_BADID; return IE_BADID;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("KPCOMM: setcom port not open\r\n");
return -1; return -1;
} }
@ -1053,8 +1352,14 @@ int16_t FAR PASCAL _export setcom(DCB FAR *dcb)
port->xonLim = dcb->XonLim; port->xonLim = dcb->XonLim;
port->xoffLim = dcb->XoffLim; port->xoffLim = dcb->XoffLim;
// Sync ComDEB shadow fields
port->comDeb.baudRate = port->baudRate;
port->comDeb.lcrShadow = (uint8_t)_inp(port->baseAddr + UART_LCR);
port->comDeb.mcrShadow = (uint8_t)_inp(port->baseAddr + UART_MCR);
_enable(); _enable();
dbgStr("KPCOMM: setcom OK\r\n");
return 0; return 0;
} }
@ -1062,22 +1367,25 @@ int16_t FAR PASCAL _export setcom(DCB FAR *dcb)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// setque - Resize RX/TX buffers (ordinal 3) // setque - Resize RX/TX buffers (ordinal 3)
// //
// The stock driver ignores the buffer pointers passed by the caller // Caller passes port ID and requested RX/TX buffer sizes.
// and manages its own. We do the same: free old buffers, alloc new. // We manage our own GlobalAlloc'd buffers.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBufArg, int16_t rxSz, char FAR *txBufArg, int16_t txSz) int16_t FAR PASCAL _export setque(int16_t commId, int16_t rxSz, int16_t txSz)
{ {
PortStateT *port; PortStateT *port;
(void)rxBufArg; dbgInt16("KPCOMM: setque Id", commId);
(void)txBufArg; dbgInt16("KPCOMM: setque rxSz", rxSz);
dbgInt16("KPCOMM: setque txSz", txSz);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("KPCOMM: setque bad id\r\n");
return -1; return -1;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("KPCOMM: setque port not open\r\n");
return -1; return -1;
} }
@ -1085,10 +1393,21 @@ int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBufArg, int16_t rx
freeBuffers(port); freeBuffers(port);
if (initBuffers(port, (uint16_t)rxSz, (uint16_t)txSz) != 0) { if (initBuffers(port, (uint16_t)rxSz, (uint16_t)txSz) != 0) {
_enable(); _enable();
dbgStr("KPCOMM: setque IE_MEMORY\r\n");
return IE_MEMORY; return IE_MEMORY;
} }
// Sync ComDEB queue sizes
port->comDeb.qInSize = port->rxSize;
port->comDeb.qInCount = 0;
port->comDeb.qInHead = 0;
port->comDeb.qInTail = 0;
port->comDeb.qOutSize = port->txSize;
port->comDeb.qOutCount = 0;
port->comDeb.qOutHead = 0;
port->comDeb.qOutTail = 0;
_enable(); _enable();
dbgStr("KPCOMM: setque OK\r\n");
return 0; return 0;
} }
@ -1105,6 +1424,8 @@ int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len)
uint8_t FAR *src; uint8_t FAR *src;
int16_t bytesWritten; int16_t bytesWritten;
dbgInt16("KPCOMM: sndcom Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
@ -1134,6 +1455,10 @@ int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len)
} }
_enable(); _enable();
// Sync ComDEB queue counts
port->comDeb.qOutCount = port->txCount;
port->comDeb.qOutHead = port->txHead;
// Prime transmitter if we wrote anything // Prime transmitter if we wrote anything
if (bytesWritten > 0 && !port->txStopped) { if (bytesWritten > 0 && !port->txStopped) {
primeTx(port); primeTx(port);
@ -1154,12 +1479,16 @@ int16_t FAR PASCAL _export stacom(int16_t commId, COMSTAT FAR *stat)
PortStateT *port; PortStateT *port;
int16_t errors; int16_t errors;
dbgInt16("KPCOMM: stacom Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("KPCOMM: stacom bad id\r\n");
return -1; return -1;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("KPCOMM: stacom not open\r\n");
return -1; return -1;
} }
@ -1177,6 +1506,7 @@ int16_t FAR PASCAL _export stacom(int16_t commId, COMSTAT FAR *stat)
stat->cbOutQue = port->txCount; stat->cbOutQue = port->txCount;
} }
dbgHex16("KPCOMM: stacom errors", (uint16_t)errors);
return errors; return errors;
} }
@ -1192,6 +1522,8 @@ void FAR PASCAL _export suspendOpenCommPorts(void)
int16_t i; int16_t i;
PortStateT *port; PortStateT *port;
dbgStr("KPCOMM: suspend\r\n");
for (i = 0; i < MAX_PORTS; i++) { for (i = 0; i < MAX_PORTS; i++) {
port = &ports[i]; port = &ports[i];
if (!port->isOpen) { if (!port->isOpen) {
@ -1221,12 +1553,15 @@ int16_t FAR PASCAL _export trmcom(int16_t commId)
{ {
PortStateT *port; PortStateT *port;
dbgInt16("KPCOMM: trmcom Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; return -1;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("KPCOMM: trmcom port not open\r\n");
return -1; return -1;
} }
@ -1252,6 +1587,8 @@ int16_t FAR PASCAL _export trmcom(int16_t commId)
port->isOpen = FALSE; port->isOpen = FALSE;
dbgHex16("KPCOMM: trmcom isrHits", isrHitCount);
dbgStr("KPCOMM: trmcom OK\r\n");
return 0; return 0;
} }
@ -1268,6 +1605,8 @@ int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lp
ghInstance = hInstance; ghInstance = hInstance;
dbgStr("KPCOMM: LibMain\r\n");
if (wHeapSize > 0) { if (wHeapSize > 0) {
UnlockData(0); UnlockData(0);
} }

View file

@ -1,5 +1,5 @@
LIBRARY COMM LIBRARY COMM
DESCRIPTION 'High-Speed Serial Communications Driver' DESCRIPTION 'KPCOMM -- High-Speed Serial Communications Driver'
EXETYPE WINDOWS EXETYPE WINDOWS
CODE PRELOAD FIXED CODE PRELOAD FIXED
DATA PRELOAD FIXED SINGLE DATA PRELOAD FIXED SINGLE
@ -20,8 +20,10 @@ EXPORTS
CSETBRK @13 CSETBRK @13
CCLRBRK @14 CCLRBRK @14
GETDCB @15 GETDCB @15
WEP @16 RESIDENTNAME
SUSPENDOPENCOMMPORTS @17 SUSPENDOPENCOMMPORTS @17
REACTIVATEOPENCOMMPORTS @18 REACTIVATEOPENCOMMPORTS @18
COMMWRITESTRING @19 COMMWRITESTRING @19
READCOMMSTRING @20 READCOMMSTRING @20
ENABLENOTIFICATION @100 ENABLENOTIFICATION @100
COMMNOTIFYWNDPROC @101

View file

@ -1,4 +1,4 @@
// commdrv.h - High-speed COMM.DRV replacement // commdrv.h - KPCOMM.DRV -- High-speed COMM.DRV replacement
// //
// Types, UART register definitions, port state structure, and prototypes. // Types, UART register definitions, port state structure, and prototypes.
// Drop-in replacement for Windows 3.1 stock COMM.DRV with proper 16550 // Drop-in replacement for Windows 3.1 stock COMM.DRV with proper 16550
@ -8,6 +8,7 @@
#define COMMDRV_H #define COMMDRV_H
#include <windows.h> #include <windows.h>
#include <conio.h>
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// stdint types for MSVC 1.52 (no stdint.h available) // stdint types for MSVC 1.52 (no stdint.h available)
@ -168,6 +169,7 @@ typedef signed char int8_t;
// exceeds 65535, high baud rates use CBR_* index constants instead // exceeds 65535, high baud rates use CBR_* index constants instead
// of raw values. Values >= 0xFF00 are indices, not raw rates. // of raw values. Values >= 0xFF00 are indices, not raw rates.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Most CBR_* constants defined by windows.h; add any missing ones
#ifndef CBR_110 #ifndef CBR_110
#define CBR_110 0xFF10 #define CBR_110 0xFF10
#define CBR_300 0xFF11 #define CBR_300 0xFF11
@ -176,10 +178,20 @@ typedef signed char int8_t;
#define CBR_2400 0xFF14 #define CBR_2400 0xFF14
#define CBR_4800 0xFF15 #define CBR_4800 0xFF15
#define CBR_9600 0xFF16 #define CBR_9600 0xFF16
#endif
#ifndef CBR_14400
#define CBR_14400 0xFF17 #define CBR_14400 0xFF17
#endif
#ifndef CBR_19200
#define CBR_19200 0xFF18 #define CBR_19200 0xFF18
#endif
#ifndef CBR_38400
#define CBR_38400 0xFF1B #define CBR_38400 0xFF1B
#endif
#ifndef CBR_56000
#define CBR_56000 0xFF1F #define CBR_56000 0xFF1F
#endif
#ifndef CBR_115200
#define CBR_115200 0xFF24 #define CBR_115200 0xFF24
#endif #endif
@ -189,15 +201,29 @@ typedef signed char int8_t;
#define FIFO_DEPTH 16 #define FIFO_DEPTH 16
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Comm error flags (CE_* -- matches Windows SDK definitions) // Comm error flags (CE_* -- most defined by windows.h, fill any gaps)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
#define CE_RXOVER 0x0001 // Receive Queue overflow #ifndef CE_RXOVER
#define CE_OVERRUN 0x0002 // Hardware overrun #define CE_RXOVER 0x0001
#define CE_RXPARITY 0x0004 // Parity error #endif
#define CE_FRAME 0x0008 // Framing error #ifndef CE_OVERRUN
#define CE_BREAK 0x0010 // Break detected #define CE_OVERRUN 0x0002
#define CE_TXFULL 0x0020 // TX Queue is full #endif
#define CE_MODE 0x8000 // Requested mode unsupported #ifndef CE_RXPARITY
#define CE_RXPARITY 0x0004
#endif
#ifndef CE_FRAME
#define CE_FRAME 0x0008
#endif
#ifndef CE_BREAK
#define CE_BREAK 0x0010
#endif
#ifndef CE_TXFULL
#define CE_TXFULL 0x0020
#endif
#ifndef CE_MODE
#define CE_MODE 0x8000
#endif
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Comm event flags (EV_* -- matches Windows SDK definitions) // Comm event flags (EV_* -- matches Windows SDK definitions)
@ -237,7 +263,10 @@ typedef signed char int8_t;
#define HS_BOTH 3 #define HS_BOTH 3
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// EscapeCommFunction codes (matches Windows SDK) // EscapeCommFunction codes
//
// SETXON through RESETDEV defined by windows.h.
// SETBREAK/CLRBREAK are our extensions for routing through cextfcn.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
#ifndef SETXON #ifndef SETXON
#define SETXON 1 #define SETXON 1
@ -247,9 +276,9 @@ typedef signed char int8_t;
#define SETDTR 5 #define SETDTR 5
#define CLRDTR 6 #define CLRDTR 6
#define RESETDEV 7 #define RESETDEV 7
#define SETBREAK 8
#define CLRBREAK 9
#endif #endif
#define ESC_SETBREAK 8
#define ESC_CLRBREAK 9
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Flush queue selectors // Flush queue selectors
@ -257,10 +286,48 @@ typedef signed char int8_t;
#define FLUSH_RX 0 #define FLUSH_RX 0
#define FLUSH_TX 1 #define FLUSH_TX 1
// -----------------------------------------------------------------------
// Stock COMM.DRV compatible ComDEB structure
//
// SetCommEventMask (cevt) returns a FAR pointer to offset 0 of this
// structure. Third-party code (ProComm COMMTASK.DLL, etc.) accesses
// fields at known offsets -- most importantly offset 35 (MSR shadow)
// per Microsoft KB Q101417. Our layout must match the stock driver.
// -----------------------------------------------------------------------
typedef struct {
uint16_t evtWord; // +0: Accumulated event flags (EV_*)
uint16_t evtMask; // +2: Event enable mask
uint16_t qInAddr; // +4: RX queue offset (compat, unused)
uint16_t qInSize; // +6: RX buffer size
uint16_t qInCount; // +8: RX bytes in buffer
uint16_t qInHead; // +10: RX write position
uint16_t qInTail; // +12: RX read position
uint16_t qOutAddr; // +14: TX queue offset (compat, unused)
uint16_t qOutSize; // +16: TX buffer size
uint16_t qOutCount; // +18: TX bytes in buffer
uint16_t qOutHead; // +20: TX write position
uint16_t qOutTail; // +22: TX read position
uint16_t port; // +24: UART base I/O address
uint16_t baudRate; // +26: Current baud rate
uint8_t lcrShadow; // +28: LCR shadow
uint8_t mcrShadow; // +29: MCR shadow
uint8_t ierShadow; // +30: IER shadow
uint8_t commErr; // +31: Error flags (low byte)
uint8_t flags1; // +32: Internal flags
uint8_t flags2; // +33: More internal flags
uint8_t recvTrigger; // +34: FIFO trigger level
uint8_t msrShadow; // +35: MSR shadow (documented KB Q101417)
uint8_t padding[4]; // +36: Pad to 40 bytes
} ComDebT;
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Port state structure // Port state structure
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
typedef struct { typedef struct {
// Stock-compatible ComDEB -- must be kept synchronized.
// cevt returns a FAR pointer to this.
ComDebT comDeb;
uint16_t baseAddr; // UART base I/O address uint16_t baseAddr; // UART base I/O address
uint8_t irq; // IRQ number (3 or 4) uint8_t irq; // IRQ number (3 or 4)
int16_t commId; // Port ID (0=COM1, 1=COM2, ...) int16_t commId; // Port ID (0=COM1, 1=COM2, ...)
@ -305,8 +372,6 @@ typedef struct {
uint16_t errorFlags; // CE_* error flags (sticky until read) uint16_t errorFlags; // CE_* error flags (sticky until read)
// Event notification // Event notification
uint16_t evtMask; // Event enable mask
uint16_t evtWord; // Accumulated events
HWND hwndNotify; // Window for WM_COMMNOTIFY HWND hwndNotify; // Window for WM_COMMNOTIFY
int16_t rxNotifyThresh; // CN_RECEIVE threshold (-1=disabled) int16_t rxNotifyThresh; // CN_RECEIVE threshold (-1=disabled)
int16_t txNotifyThresh; // CN_TRANSMIT threshold (-1=disabled) int16_t txNotifyThresh; // CN_TRANSMIT threshold (-1=disabled)
@ -328,14 +393,23 @@ typedef struct {
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
extern PortStateT ports[MAX_PORTS]; extern PortStateT ports[MAX_PORTS];
// -----------------------------------------------------------------------
// Dynamically resolved PostMessage (USER.EXE not loaded at boot)
// -----------------------------------------------------------------------
typedef BOOL (FAR PASCAL *PostMessageProcT)(HWND, UINT, WPARAM, LPARAM);
extern PostMessageProcT pfnPostMessage;
// ISR hit counter for diagnostics
extern volatile uint16_t isrHitCount;
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Exported function prototypes (COMM.DRV API) // Exported function prototypes (COMM.DRV API)
// //
// These use the Windows COMM.DRV calling convention: FAR PASCAL // These use the Windows COMM.DRV calling convention: FAR PASCAL
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize); int16_t FAR PASCAL _export inicom(DCB FAR *dcb);
int16_t FAR PASCAL _export setcom(DCB FAR *dcb); int16_t FAR PASCAL _export setcom(DCB FAR *dcb);
int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBuf, int16_t rxSize, char FAR *txBuf, int16_t txSize); int16_t FAR PASCAL _export setque(int16_t commId, int16_t rxSize, int16_t txSize);
int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch); int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch);
@ -347,12 +421,13 @@ int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func);
int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue); int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue);
int16_t FAR PASCAL _export csetbrk(int16_t commId); int16_t FAR PASCAL _export csetbrk(int16_t commId);
int16_t FAR PASCAL _export cclrbrk(int16_t commId); int16_t FAR PASCAL _export cclrbrk(int16_t commId);
int16_t FAR PASCAL _export getdcb(int16_t commId, DCB FAR *dcb); DCB FAR * FAR PASCAL _export getdcb(int16_t commId);
void FAR PASCAL _export suspendOpenCommPorts(void); void FAR PASCAL _export suspendOpenCommPorts(void);
void FAR PASCAL _export reactivateOpenCommPorts(void); void FAR PASCAL _export reactivateOpenCommPorts(void);
int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len); int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len);
int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh); int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh);
int FAR PASCAL _export commNotifyWndProc(void);
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ISR prototypes (isr.c) // ISR prototypes (isr.c)

View file

@ -1,4 +1,4 @@
// isr.c - Interrupt service routines for COMM.DRV replacement // isr.c - Interrupt service routines for KPCOMM.DRV
// //
// ISR entry points for IRQ3 (COM2/4) and IRQ4 (COM1/3), DPMI interrupt // ISR entry points for IRQ3 (COM2/4) and IRQ4 (COM1/3), DPMI interrupt
// vector hooking/unhooking, and interrupt dispatch with 16550 FIFO support. // vector hooking/unhooking, and interrupt dispatch with 16550 FIFO support.
@ -106,12 +106,12 @@ static void checkNotify(PortStateT *port)
} }
// CN_EVENT: any event bits accumulated // CN_EVENT: any event bits accumulated
if (port->evtWord & port->evtMask) { if (port->comDeb.evtWord & port->comDeb.evtMask) {
notifyBits |= CN_EVENT; notifyBits |= CN_EVENT;
} }
if (notifyBits) { if (notifyBits && pfnPostMessage) {
PostMessage(port->hwndNotify, WM_COMMNOTIFY, (WPARAM)port->commId, (LPARAM)notifyBits); pfnPostMessage(port->hwndNotify, WM_COMMNOTIFY, (WPARAM)port->commId, (LPARAM)notifyBits);
} }
} }
@ -132,10 +132,10 @@ static void handleLsr(PortStateT *port, uint8_t lsr)
} }
if (lsr & LSR_BI) { if (lsr & LSR_BI) {
port->errorFlags |= CE_BREAK; port->errorFlags |= CE_BREAK;
port->evtWord |= EV_BREAK; port->comDeb.evtWord |= EV_BREAK;
} }
if (lsr & (LSR_OE | LSR_PE | LSR_FE)) { if (lsr & (LSR_OE | LSR_PE | LSR_FE)) {
port->evtWord |= EV_ERR; port->comDeb.evtWord |= EV_ERR;
} }
} }
@ -149,8 +149,11 @@ static void handleMsr(PortStateT *port)
msr = (uint8_t)_inp(port->baseAddr + UART_MSR); msr = (uint8_t)_inp(port->baseAddr + UART_MSR);
// Update ComDEB MSR shadow (offset 35) for third-party code
port->comDeb.msrShadow = msr;
if (msr & MSR_DCTS) { if (msr & MSR_DCTS) {
port->evtWord |= EV_CTS; port->comDeb.evtWord |= EV_CTS;
// RTS/CTS flow control: stop/start TX based on CTS // RTS/CTS flow control: stop/start TX based on CTS
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) { if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
@ -167,15 +170,15 @@ static void handleMsr(PortStateT *port)
} }
if (msr & MSR_DDSR) { if (msr & MSR_DDSR) {
port->evtWord |= EV_DSR; port->comDeb.evtWord |= EV_DSR;
} }
if (msr & MSR_TERI) { if (msr & MSR_TERI) {
port->evtWord |= EV_RING; port->comDeb.evtWord |= EV_RING;
} }
if (msr & MSR_DDCD) { if (msr & MSR_DDCD) {
port->evtWord |= EV_RLSD; port->comDeb.evtWord |= EV_RLSD;
} }
} }
@ -230,7 +233,7 @@ static void handleRx(PortStateT *port, uint8_t lsr)
} }
// Set event bit // Set event bit
port->evtWord |= EV_RXCHAR; port->comDeb.evtWord |= EV_RXCHAR;
// Read LSR again for next byte // Read LSR again for next byte
lsr = (uint8_t)_inp(base + UART_LSR); lsr = (uint8_t)_inp(base + UART_LSR);
@ -270,7 +273,7 @@ static void handleTx(PortStateT *port)
// Nothing to send -- disable THRE interrupt // Nothing to send -- disable THRE interrupt
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) & ~IER_THRE)); _outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) & ~IER_THRE));
// TX empty event // TX empty event
port->evtWord |= EV_TXEMPTY; port->comDeb.evtWord |= EV_TXEMPTY;
return; return;
} }
@ -421,6 +424,14 @@ void isrDispatch(PortStateT *port)
} }
} }
// Sync queue counts into ComDEB for third-party code
port->comDeb.qInCount = port->rxCount;
port->comDeb.qInHead = port->rxHead;
port->comDeb.qInTail = port->rxTail;
port->comDeb.qOutCount = port->txCount;
port->comDeb.qOutHead = port->txHead;
port->comDeb.qOutTail = port->txTail;
checkNotify(port); checkNotify(port);
} }
@ -478,6 +489,12 @@ void _far _interrupt isr3(void)
{ {
int16_t i; int16_t i;
// Load DLL's data segment -- _interrupt saves/restores DS but doesn't
// set it. Without this, DS belongs to whatever code was interrupted
// and all global accesses (ports[], ring buffers) read garbage.
_asm mov ax, seg ports
_asm mov ds, ax
for (i = 0; i < MAX_PORTS; i++) { for (i = 0; i < MAX_PORTS; i++) {
if (ports[i].isOpen && ports[i].irq == 3) { if (ports[i].isOpen && ports[i].irq == 3) {
isrDispatch(&ports[i]); isrDispatch(&ports[i]);
@ -496,6 +513,12 @@ void _far _interrupt isr4(void)
{ {
int16_t i; int16_t i;
// Load DLL's data segment -- see comment in isr3
_asm mov ax, seg ports
_asm mov ds, ax
isrHitCount++;
for (i = 0; i < MAX_PORTS; i++) { for (i = 0; i < MAX_PORTS; i++) {
if (ports[i].isOpen && ports[i].irq == 4) { if (ports[i].isOpen && ports[i].irq == 4) {
isrDispatch(&ports[i]); isrDispatch(&ports[i]);

View file

@ -6,11 +6,11 @@
# Prerequisites: # Prerequisites:
# - MSVC 1.52 (cl, link in PATH) # - MSVC 1.52 (cl, link in PATH)
# #
# Output is COMM.DRV -- drop-in replacement for stock Windows 3.1 driver. # Output is KPCOMM.DRV -- drop-in replacement for stock Windows 3.1 driver.
# #
# Install: # Install:
# 1. Copy COMM.DRV to \WINDOWS\SYSTEM # 1. Copy KPCOMM.DRV to \WINDOWS\SYSTEM
# 2. Edit SYSTEM.INI [boot] section: comm.drv=comm.drv # 2. Edit SYSTEM.INI [boot] section: comm.drv=kpcomm.drv
# 3. Add to [386Enh] section: COM1FIFO=1 (for each port in use) # 3. Add to [386Enh] section: COM1FIFO=1 (for each port in use)
# 4. Restart Windows # 4. Restart Windows
# #
@ -28,9 +28,9 @@ LINK = link
# -ASw Small model, SS!=DS (Windows DLL) # -ASw Small model, SS!=DS (Windows DLL)
# -Gsw No stack probes, Windows prolog/epilog # -Gsw No stack probes, Windows prolog/epilog
# -Ow Safe optimizations for Windows # -Ow Safe optimizations for Windows
# -Zp1 Pack structures on 1-byte boundaries (hardware register layouts) # -Zp2 Pack structures on 2-byte boundaries (matches Windows SDK)
# -Ze Enable Microsoft extensions # -Ze Enable Microsoft extensions
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp1 -Ze CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp2 -Ze
# Linker flags: # Linker flags:
# /NOD No default libraries # /NOD No default libraries
@ -44,7 +44,7 @@ LFLAGS = /NOD /NOE /AL:16
LIBS = sdllcew libw LIBS = sdllcew libw
# Output # Output
TARGET = comm.drv TARGET = kpcomm.drv
# Objects # Objects
OBJS = commdrv.obj isr.obj OBJS = commdrv.obj isr.obj

View file

@ -61,7 +61,7 @@ OBJS = mscomm.obj serial.obj
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
all: $(TARGET) all: $(TARGET)
$(TARGET): $(OBJS) mscomm.def mscomm.res $(TARGET): $(OBJS) mscomm.def mscomm.res vbapi.lib
$(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), mscomm.def $(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), mscomm.def
$(RC) mscomm.res $(TARGET) $(RC) mscomm.res $(TARGET)

View file

@ -5,6 +5,13 @@
#ifndef MSCOMM_H #ifndef MSCOMM_H
#define MSCOMM_H #define MSCOMM_H
// -----------------------------------------------------------------------
// Resource IDs
// -----------------------------------------------------------------------
#define IDB_MSCOMM 8000
#ifndef RC_INVOKED
#include "vbapi.h" #include "vbapi.h"
#include "serial.h" #include "serial.h"
@ -21,11 +28,6 @@ typedef signed char int8_t;
#define _STDINT_DEFINED #define _STDINT_DEFINED
#endif #endif
// -----------------------------------------------------------------------
// Resource IDs
// -----------------------------------------------------------------------
#define IDB_MSCOMM 8000
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Property indices (position in npproplist array) // Property indices (position in npproplist array)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -138,4 +140,6 @@ typedef struct {
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
extern HANDLE ghInstance; extern HANDLE ghInstance;
#endif // RC_INVOKED
#endif // MSCOMM_H #endif // MSCOMM_H

View file

@ -12,7 +12,7 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
int16_t serialClose(int16_t commId); int16_t serialClose(int16_t commId);
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings); int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
int16_t serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold); BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
int16_t serialEscape(int16_t commId, int16_t func); int16_t serialEscape(int16_t commId, int16_t func);
int16_t serialFlush(int16_t commId, int16_t queue); int16_t serialFlush(int16_t commId, int16_t queue);
UINT serialGetEventMask(int16_t commId, UINT mask); UINT serialGetEventMask(int16_t commId, UINT mask);

View file

@ -37,13 +37,17 @@ typedef signed char int8_t;
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Escape function constants (mirrors EscapeCommFunction values) // Escape function constants (mirrors EscapeCommFunction values)
//
// SETDTR through RESETDEV are defined by windows.h.
// SETBREAK/CLRBREAK are not in windows.h (SetCommBreak/ClearCommBreak
// are separate API functions), so we define numeric values directly.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
#define SERIAL_SETDTR SETDTR #define SERIAL_SETDTR SETDTR
#define SERIAL_CLRDTR CLRDTR #define SERIAL_CLRDTR CLRDTR
#define SERIAL_SETRTS SETRTS #define SERIAL_SETRTS SETRTS
#define SERIAL_CLRRTS CLRRTS #define SERIAL_CLRRTS CLRRTS
#define SERIAL_SETBREAK SETBREAK #define SERIAL_SETBREAK 8
#define SERIAL_CLRBREAK CLRBREAK #define SERIAL_CLRBREAK 9
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Function prototypes // Function prototypes

View file

@ -17,17 +17,15 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Core types // Core types
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
#ifndef USHORT
typedef unsigned short USHORT;
#endif
typedef WORD HCTL; // Handle to a VBX control instance typedef WORD HCTL; // Handle to a VBX control instance
typedef WORD HSZ; // Handle to a VB-managed string typedef WORD HSZ; // Handle to a VB-managed string
typedef LONG HLSTR; // Handle to a VB long string typedef LONG HLSTR; // Handle to a VB long string
typedef USHORT ERR; // Error code typedef USHORT ERR; // Error code
// Segment type for MSVC 1.52
#ifndef _SEGMENT_DEFINED
typedef unsigned short _segment;
#define _SEGMENT_DEFINED
#endif
// Flag type // Flag type
typedef DWORD FL; typedef DWORD FL;
@ -196,7 +194,7 @@ struct tagMODEL {
FL fl; // MODEL_* flags FL fl; // MODEL_* flags
PCTLPROC pctlproc; // Control procedure PCTLPROC pctlproc; // Control procedure
USHORT fsClassStyle; // Window class style bits USHORT fsClassStyle; // Window class style bits
USHORT flWndStyle; // Window style bits FL flWndStyle; // Window style bits
USHORT cbCtlExtra; // Bytes of per-instance data USHORT cbCtlExtra; // Bytes of per-instance data
USHORT idBmpPalette; // Toolbox bitmap resource ID USHORT idBmpPalette; // Toolbox bitmap resource ID
PSTR npszDefCtlName; // Default control name (near) PSTR npszDefCtlName; // Default control name (near)