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>
This commit is contained in:
Scott Duensing 2026-02-24 20:03:34 -06:00
parent f666825417
commit d68405550d
10 changed files with 517 additions and 72 deletions

View file

@ -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("COMM.DRV: 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("COMM.DRV: cevt SS", ssSeg);
dbgHex16("COMM.DRV: cevt BP", bpVal);
dbgDump("COMM.DRV: cevt stk", (void FAR *)((uint32_t)ssSeg << 16 | (bpVal + 6)), 16);
}
#endif
dbgInt16("COMM.DRV: cevt Id", commId);
dbgHex16("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: cevt retSeg", (uint16_t)(retVal >> 16));
dbgHex16("COMM.DRV: cevt retOff", (uint16_t)(retVal & 0xFFFF));
dbgStr("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: cextfcn Id", commId);
dbgInt16("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: enableNotif Id", commId);
dbgHex16("COMM.DRV: enableNotif hwnd", (uint16_t)hwnd);
dbgInt16("COMM.DRV: enableNotif rxTh", rxThresh);
dbgInt16("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: getdcb Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
return -1; dbgStr("COMM.DRV: getdcb bad id\r\n");
return NULL;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
return -1; dbgStr("COMM.DRV: getdcb not open\r\n");
return NULL;
} }
_fmemcpy(dcb, &port->dcb, sizeof(DCB)); dbgHex16("COMM.DRV: getdcb isrHits", isrHitCount);
return 0; dbgStr("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: inicom SS", ssVal);
dbgHex16("COMM.DRV: inicom BP", bpVal);
dbgDump("COMM.DRV: inicom stk", stkPtr, 24);
}
if (!dcb) { if (!dcb) {
dbgStr("COMM.DRV: inicom NULL dcb\r\n");
return IE_DEFAULT; return IE_DEFAULT;
} }
dbgFarPtr("COMM.DRV: inicom dcb", (void FAR *)dcb);
dbgDump("COMM.DRV: inicom raw", (void FAR *)dcb, 16);
dbgHex16("COMM.DRV: sizeof(DCB)", (uint16_t)sizeof(DCB));
commId = dcb->Id; commId = dcb->Id;
dbgInt16("COMM.DRV: inicom Id", commId);
dbgHex16("COMM.DRV: inicom BaudRate", dcb->BaudRate);
dbgHex16("COMM.DRV: inicom ByteSize", (uint16_t)dcb->ByteSize);
dbgHex16("COMM.DRV: inicom Parity", (uint16_t)dcb->Parity);
dbgHex16("COMM.DRV: inicom StopBits", (uint16_t)dcb->StopBits);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("COMM.DRV: inicom IE_BADID\r\n");
return IE_BADID; return IE_BADID;
} }
port = &ports[commId]; port = &ports[commId];
if (port->isOpen) { if (port->isOpen) {
dbgStr("COMM.DRV: inicom IE_OPEN\r\n");
return IE_OPEN; return IE_OPEN;
} }
// Initialize port state // Initialize port state
initPortState(port, commId); initPortState(port, commId);
dbgHex16("COMM.DRV: inicom baseAddr", port->baseAddr);
dbgHex16("COMM.DRV: 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("COMM.DRV: inicom rxBufSize", rxBufSize);
// Allocate ring buffers // Allocate ring buffers
if (initBuffers(port, rxBufSize, txBufSize) != 0) { if (initBuffers(port, rxBufSize, txBufSize) != 0) {
dbgStr("COMM.DRV: inicom IE_MEMORY\r\n");
return IE_MEMORY; return IE_MEMORY;
} }
dbgStr("COMM.DRV: 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("COMM.DRV: inicom is16550", (uint16_t)port->is16550);
// Hook ISR // Hook ISR
if (hookIsr(port) != 0) { if (hookIsr(port) != 0) {
dbgStr("COMM.DRV: inicom hookIsr FAIL\r\n");
freeBuffers(port); freeBuffers(port);
return IE_HARDWARE; return IE_HARDWARE;
} }
dbgStr("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: setcom enter\r\n");
if (!dcb) { if (!dcb) {
dbgStr("COMM.DRV: setcom NULL dcb\r\n");
return -1; return -1;
} }
commId = dcb->Id; commId = dcb->Id;
dbgInt16("COMM.DRV: setcom Id", commId);
dbgHex16("COMM.DRV: setcom BaudRate", dcb->BaudRate);
dbgHex16("COMM.DRV: setcom ByteSize", (uint16_t)dcb->ByteSize);
dbgHex16("COMM.DRV: setcom Parity", (uint16_t)dcb->Parity);
dbgHex16("COMM.DRV: setcom StopBits", (uint16_t)dcb->StopBits);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("COMM.DRV: setcom IE_BADID\r\n");
return IE_BADID; return IE_BADID;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: setque Id", commId);
(void)txBufArg; dbgInt16("COMM.DRV: setque rxSz", rxSz);
dbgInt16("COMM.DRV: setque txSz", txSz);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("COMM.DRV: setque bad id\r\n");
return -1; return -1;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: stacom Id", commId);
if (commId < 0 || commId >= MAX_PORTS) { if (commId < 0 || commId >= MAX_PORTS) {
dbgStr("COMM.DRV: stacom bad id\r\n");
return -1; return -1;
} }
port = &ports[commId]; port = &ports[commId];
if (!port->isOpen) { if (!port->isOpen) {
dbgStr("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: 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("COMM.DRV: trmcom isrHits", isrHitCount);
dbgStr("COMM.DRV: 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("COMM.DRV: LibMain\r\n");
if (wHeapSize > 0) { if (wHeapSize > 0) {
UnlockData(0); UnlockData(0);
} }

View file

@ -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

@ -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

@ -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

@ -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

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)