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:
parent
f666825417
commit
d68405550d
10 changed files with 517 additions and 72 deletions
393
drv/commdrv.c
393
drv/commdrv.c
|
|
@ -10,12 +10,154 @@
|
|||
#include <dos.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
|
||||
// -----------------------------------------------------------------------
|
||||
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 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 sndcom(int16_t commId, void FAR *buf, int16_t len);
|
||||
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 csetbrk(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 reactivateOpenCommPorts(void);
|
||||
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 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 _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 void initPortState(PortStateT *port, int16_t commId);
|
||||
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);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -70,6 +213,18 @@ PortStateT ports[MAX_PORTS];
|
|||
// -----------------------------------------------------------------------
|
||||
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
|
||||
|
|
@ -215,6 +370,8 @@ int16_t FAR PASCAL _export cclrbrk(int16_t commId)
|
|||
PortStateT *port;
|
||||
uint8_t lcr;
|
||||
|
||||
dbgInt16("COMM.DRV: cclrbrk Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -243,6 +400,22 @@ int32_t FAR PASCAL _export cevt(int16_t commId, int16_t evtMask)
|
|||
{
|
||||
PortStateT *port;
|
||||
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) {
|
||||
return 0L;
|
||||
|
|
@ -250,13 +423,18 @@ int32_t FAR PASCAL _export cevt(int16_t commId, int16_t evtMask)
|
|||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
dbgStr("COMM.DRV: cevt not open\r\n");
|
||||
return 0L;
|
||||
}
|
||||
|
||||
port->evtMask = (uint16_t)evtMask;
|
||||
port->comDeb.evtMask = (uint16_t)evtMask;
|
||||
|
||||
ptr = &port->evtWord;
|
||||
return (int32_t)(void FAR *)ptr;
|
||||
ptr = &port->comDeb.evtWord;
|
||||
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;
|
||||
uint16_t events;
|
||||
|
||||
dbgInt16("COMM.DRV: cevtget Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -280,8 +460,8 @@ uint16_t FAR PASCAL _export cevtget(int16_t commId, int16_t evtMask)
|
|||
}
|
||||
|
||||
_disable();
|
||||
events = port->evtWord & (uint16_t)evtMask;
|
||||
port->evtWord &= ~(uint16_t)evtMask;
|
||||
events = port->comDeb.evtWord & (uint16_t)evtMask;
|
||||
port->comDeb.evtWord &= ~(uint16_t)evtMask;
|
||||
_enable();
|
||||
|
||||
return events;
|
||||
|
|
@ -299,6 +479,9 @@ int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func)
|
|||
uint8_t mcr;
|
||||
uint16_t base;
|
||||
|
||||
dbgInt16("COMM.DRV: cextfcn Id", commId);
|
||||
dbgInt16("COMM.DRV: cextfcn func", func);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -328,9 +511,9 @@ int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func)
|
|||
mcr &= ~MCR_RTS;
|
||||
port->rtsState = FALSE;
|
||||
break;
|
||||
case SETBREAK:
|
||||
case ESC_SETBREAK:
|
||||
return csetbrk(commId);
|
||||
case CLRBREAK:
|
||||
case ESC_CLRBREAK:
|
||||
return cclrbrk(commId);
|
||||
case RESETDEV:
|
||||
// No-op for serial ports
|
||||
|
|
@ -364,6 +547,8 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue)
|
|||
{
|
||||
PortStateT *port;
|
||||
|
||||
dbgInt16("COMM.DRV: cflush Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -378,6 +563,9 @@ int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue)
|
|||
port->rxHead = 0;
|
||||
port->rxTail = 0;
|
||||
port->rxCount = 0;
|
||||
port->comDeb.qInHead = 0;
|
||||
port->comDeb.qInTail = 0;
|
||||
port->comDeb.qInCount = 0;
|
||||
// Reset FIFO to clear hardware buffer too
|
||||
if (port->is16550 && port->fifoEnabled) {
|
||||
_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->txTail = 0;
|
||||
port->txCount = 0;
|
||||
port->comDeb.qOutHead = 0;
|
||||
port->comDeb.qOutTail = 0;
|
||||
port->comDeb.qOutCount = 0;
|
||||
if (port->is16550 && port->fifoEnabled) {
|
||||
_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)
|
||||
{
|
||||
dbgInt16("COMM.DRV: commWriteStr Id", commId);
|
||||
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)
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -413,6 +620,8 @@ int16_t FAR PASCAL _export csetbrk(int16_t commId)
|
|||
PortStateT *port;
|
||||
uint8_t lcr;
|
||||
|
||||
dbgInt16("COMM.DRV: csetbrk Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -437,6 +646,8 @@ int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch)
|
|||
{
|
||||
PortStateT *port;
|
||||
|
||||
dbgInt16("COMM.DRV: ctx Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -504,19 +715,35 @@ int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t
|
|||
{
|
||||
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) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
dbgStr("COMM.DRV: enableNotif not open\r\n");
|
||||
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->rxNotifyThresh = rxThresh;
|
||||
port->txNotifyThresh = txThresh;
|
||||
|
||||
dbgStr("COMM.DRV: enableNotif OK\r\n");
|
||||
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;
|
||||
|
||||
dbgInt16("COMM.DRV: getdcb Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
dbgStr("COMM.DRV: getdcb bad id\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
return -1;
|
||||
dbgStr("COMM.DRV: getdcb not open\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_fmemcpy(dcb, &port->dcb, sizeof(DCB));
|
||||
return 0;
|
||||
dbgHex16("COMM.DRV: getdcb isrHits", isrHitCount);
|
||||
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.
|
||||
// -----------------------------------------------------------------------
|
||||
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;
|
||||
PortStateT *port;
|
||||
|
|
@ -589,25 +825,52 @@ int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize)
|
|||
uint16_t txBufSize;
|
||||
uint8_t mcr;
|
||||
|
||||
(void)rxBuf;
|
||||
(void)rxSize;
|
||||
dbgStr("COMM.DRV: inicom enter\r\n");
|
||||
|
||||
// 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) {
|
||||
dbgStr("COMM.DRV: inicom NULL dcb\r\n");
|
||||
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;
|
||||
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) {
|
||||
dbgStr("COMM.DRV: inicom IE_BADID\r\n");
|
||||
return IE_BADID;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (port->isOpen) {
|
||||
dbgStr("COMM.DRV: inicom IE_OPEN\r\n");
|
||||
return IE_OPEN;
|
||||
}
|
||||
|
||||
// Initialize port state
|
||||
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)
|
||||
{
|
||||
|
|
@ -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);
|
||||
}
|
||||
txBufSize = rxBufSize;
|
||||
dbgHex16("COMM.DRV: inicom rxBufSize", rxBufSize);
|
||||
|
||||
// Allocate ring buffers
|
||||
if (initBuffers(port, rxBufSize, txBufSize) != 0) {
|
||||
dbgStr("COMM.DRV: inicom IE_MEMORY\r\n");
|
||||
return IE_MEMORY;
|
||||
}
|
||||
dbgStr("COMM.DRV: inicom buffers OK\r\n");
|
||||
|
||||
// Detect 16550 FIFO
|
||||
port->is16550 = (uint8_t)detect16550(port->baseAddr);
|
||||
dbgHex16("COMM.DRV: inicom is16550", (uint16_t)port->is16550);
|
||||
|
||||
// Hook ISR
|
||||
if (hookIsr(port) != 0) {
|
||||
dbgStr("COMM.DRV: inicom hookIsr FAIL\r\n");
|
||||
freeBuffers(port);
|
||||
return IE_HARDWARE;
|
||||
}
|
||||
dbgStr("COMM.DRV: inicom ISR hooked\r\n");
|
||||
|
||||
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
|
||||
_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);
|
||||
|
||||
// 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
|
||||
_outp(port->baseAddr + UART_IER, IER_RDA | IER_LSI | IER_MSI);
|
||||
|
||||
dbgInt16("COMM.DRV: inicom OK, ret", commId);
|
||||
return commId;
|
||||
}
|
||||
|
||||
|
|
@ -833,6 +1109,8 @@ void FAR PASCAL _export reactivateOpenCommPorts(void)
|
|||
PortStateT *port;
|
||||
uint8_t mcr;
|
||||
|
||||
dbgStr("COMM.DRV: reactivate\r\n");
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
port = &ports[i];
|
||||
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)
|
||||
{
|
||||
dbgInt16("COMM.DRV: readCommStr Id", commId);
|
||||
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.
|
||||
// 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 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;
|
||||
int16_t bytesRead;
|
||||
|
||||
dbgInt16("COMM.DRV: reccom Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1002,29 +1283,47 @@ int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len)
|
|||
_enable();
|
||||
}
|
||||
|
||||
// Sync ComDEB queue counts
|
||||
port->comDeb.qInCount = port->rxCount;
|
||||
port->comDeb.qInTail = port->rxTail;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 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 commId;
|
||||
PortStateT *port;
|
||||
|
||||
dbgStr("COMM.DRV: setcom enter\r\n");
|
||||
|
||||
if (!dcb) {
|
||||
dbgStr("COMM.DRV: setcom NULL dcb\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
dbgStr("COMM.DRV: setcom IE_BADID\r\n");
|
||||
return IE_BADID;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
dbgStr("COMM.DRV: setcom port not open\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1053,8 +1352,14 @@ int16_t FAR PASCAL _export setcom(DCB FAR *dcb)
|
|||
port->xonLim = dcb->XonLim;
|
||||
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();
|
||||
|
||||
dbgStr("COMM.DRV: setcom OK\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1062,22 +1367,25 @@ int16_t FAR PASCAL _export setcom(DCB FAR *dcb)
|
|||
// -----------------------------------------------------------------------
|
||||
// setque - Resize RX/TX buffers (ordinal 3)
|
||||
//
|
||||
// The stock driver ignores the buffer pointers passed by the caller
|
||||
// and manages its own. We do the same: free old buffers, alloc new.
|
||||
// Caller passes port ID and requested RX/TX buffer sizes.
|
||||
// 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;
|
||||
|
||||
(void)rxBufArg;
|
||||
(void)txBufArg;
|
||||
dbgInt16("COMM.DRV: setque Id", commId);
|
||||
dbgInt16("COMM.DRV: setque rxSz", rxSz);
|
||||
dbgInt16("COMM.DRV: setque txSz", txSz);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
dbgStr("COMM.DRV: setque bad id\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
dbgStr("COMM.DRV: setque port not open\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1085,10 +1393,21 @@ int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBufArg, int16_t rx
|
|||
freeBuffers(port);
|
||||
if (initBuffers(port, (uint16_t)rxSz, (uint16_t)txSz) != 0) {
|
||||
_enable();
|
||||
dbgStr("COMM.DRV: setque IE_MEMORY\r\n");
|
||||
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();
|
||||
|
||||
dbgStr("COMM.DRV: setque OK\r\n");
|
||||
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;
|
||||
int16_t bytesWritten;
|
||||
|
||||
dbgInt16("COMM.DRV: sndcom Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1134,6 +1455,10 @@ int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len)
|
|||
}
|
||||
_enable();
|
||||
|
||||
// Sync ComDEB queue counts
|
||||
port->comDeb.qOutCount = port->txCount;
|
||||
port->comDeb.qOutHead = port->txHead;
|
||||
|
||||
// Prime transmitter if we wrote anything
|
||||
if (bytesWritten > 0 && !port->txStopped) {
|
||||
primeTx(port);
|
||||
|
|
@ -1154,12 +1479,16 @@ int16_t FAR PASCAL _export stacom(int16_t commId, COMSTAT FAR *stat)
|
|||
PortStateT *port;
|
||||
int16_t errors;
|
||||
|
||||
dbgInt16("COMM.DRV: stacom Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
dbgStr("COMM.DRV: stacom bad id\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
dbgStr("COMM.DRV: stacom not open\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1177,6 +1506,7 @@ int16_t FAR PASCAL _export stacom(int16_t commId, COMSTAT FAR *stat)
|
|||
stat->cbOutQue = port->txCount;
|
||||
}
|
||||
|
||||
dbgHex16("COMM.DRV: stacom errors", (uint16_t)errors);
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
|
@ -1192,6 +1522,8 @@ void FAR PASCAL _export suspendOpenCommPorts(void)
|
|||
int16_t i;
|
||||
PortStateT *port;
|
||||
|
||||
dbgStr("COMM.DRV: suspend\r\n");
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
port = &ports[i];
|
||||
if (!port->isOpen) {
|
||||
|
|
@ -1221,12 +1553,15 @@ int16_t FAR PASCAL _export trmcom(int16_t commId)
|
|||
{
|
||||
PortStateT *port;
|
||||
|
||||
dbgInt16("COMM.DRV: trmcom Id", commId);
|
||||
|
||||
if (commId < 0 || commId >= MAX_PORTS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
port = &ports[commId];
|
||||
if (!port->isOpen) {
|
||||
dbgStr("COMM.DRV: trmcom port not open\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1252,6 +1587,8 @@ int16_t FAR PASCAL _export trmcom(int16_t commId)
|
|||
|
||||
port->isOpen = FALSE;
|
||||
|
||||
dbgHex16("COMM.DRV: trmcom isrHits", isrHitCount);
|
||||
dbgStr("COMM.DRV: trmcom OK\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1268,6 +1605,8 @@ int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lp
|
|||
|
||||
ghInstance = hInstance;
|
||||
|
||||
dbgStr("COMM.DRV: LibMain\r\n");
|
||||
|
||||
if (wHeapSize > 0) {
|
||||
UnlockData(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ EXPORTS
|
|||
CSETBRK @13
|
||||
CCLRBRK @14
|
||||
GETDCB @15
|
||||
WEP @16 RESIDENTNAME
|
||||
SUSPENDOPENCOMMPORTS @17
|
||||
REACTIVATEOPENCOMMPORTS @18
|
||||
COMMWRITESTRING @19
|
||||
READCOMMSTRING @20
|
||||
ENABLENOTIFICATION @100
|
||||
COMMNOTIFYWNDPROC @101
|
||||
|
|
|
|||
107
drv/commdrv.h
107
drv/commdrv.h
|
|
@ -8,6 +8,7 @@
|
|||
#define COMMDRV_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 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
|
||||
// of raw values. Values >= 0xFF00 are indices, not raw rates.
|
||||
// -----------------------------------------------------------------------
|
||||
// Most CBR_* constants defined by windows.h; add any missing ones
|
||||
#ifndef CBR_110
|
||||
#define CBR_110 0xFF10
|
||||
#define CBR_300 0xFF11
|
||||
|
|
@ -176,10 +178,20 @@ typedef signed char int8_t;
|
|||
#define CBR_2400 0xFF14
|
||||
#define CBR_4800 0xFF15
|
||||
#define CBR_9600 0xFF16
|
||||
#endif
|
||||
#ifndef CBR_14400
|
||||
#define CBR_14400 0xFF17
|
||||
#endif
|
||||
#ifndef CBR_19200
|
||||
#define CBR_19200 0xFF18
|
||||
#endif
|
||||
#ifndef CBR_38400
|
||||
#define CBR_38400 0xFF1B
|
||||
#endif
|
||||
#ifndef CBR_56000
|
||||
#define CBR_56000 0xFF1F
|
||||
#endif
|
||||
#ifndef CBR_115200
|
||||
#define CBR_115200 0xFF24
|
||||
#endif
|
||||
|
||||
|
|
@ -189,15 +201,29 @@ typedef signed char int8_t;
|
|||
#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
|
||||
#define CE_OVERRUN 0x0002 // Hardware overrun
|
||||
#define CE_RXPARITY 0x0004 // Parity error
|
||||
#define CE_FRAME 0x0008 // Framing error
|
||||
#define CE_BREAK 0x0010 // Break detected
|
||||
#define CE_TXFULL 0x0020 // TX Queue is full
|
||||
#define CE_MODE 0x8000 // Requested mode unsupported
|
||||
#ifndef CE_RXOVER
|
||||
#define CE_RXOVER 0x0001
|
||||
#endif
|
||||
#ifndef CE_OVERRUN
|
||||
#define CE_OVERRUN 0x0002
|
||||
#endif
|
||||
#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)
|
||||
|
|
@ -237,7 +263,10 @@ typedef signed char int8_t;
|
|||
#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
|
||||
#define SETXON 1
|
||||
|
|
@ -247,9 +276,9 @@ typedef signed char int8_t;
|
|||
#define SETDTR 5
|
||||
#define CLRDTR 6
|
||||
#define RESETDEV 7
|
||||
#define SETBREAK 8
|
||||
#define CLRBREAK 9
|
||||
#endif
|
||||
#define ESC_SETBREAK 8
|
||||
#define ESC_CLRBREAK 9
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Flush queue selectors
|
||||
|
|
@ -257,10 +286,48 @@ typedef signed char int8_t;
|
|||
#define FLUSH_RX 0
|
||||
#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
|
||||
// -----------------------------------------------------------------------
|
||||
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
|
||||
uint8_t irq; // IRQ number (3 or 4)
|
||||
int16_t commId; // Port ID (0=COM1, 1=COM2, ...)
|
||||
|
|
@ -305,8 +372,6 @@ typedef struct {
|
|||
uint16_t errorFlags; // CE_* error flags (sticky until read)
|
||||
|
||||
// Event notification
|
||||
uint16_t evtMask; // Event enable mask
|
||||
uint16_t evtWord; // Accumulated events
|
||||
HWND hwndNotify; // Window for WM_COMMNOTIFY
|
||||
int16_t rxNotifyThresh; // CN_RECEIVE threshold (-1=disabled)
|
||||
int16_t txNotifyThresh; // CN_TRANSMIT threshold (-1=disabled)
|
||||
|
|
@ -328,14 +393,23 @@ typedef struct {
|
|||
// -----------------------------------------------------------------------
|
||||
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)
|
||||
//
|
||||
// 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 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 sndcom(int16_t commId, void FAR *buf, int16_t len);
|
||||
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 csetbrk(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 reactivateOpenCommPorts(void);
|
||||
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 enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh);
|
||||
int FAR PASCAL _export commNotifyWndProc(void);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// ISR prototypes (isr.c)
|
||||
|
|
|
|||
45
drv/isr.c
45
drv/isr.c
|
|
@ -106,12 +106,12 @@ static void checkNotify(PortStateT *port)
|
|||
}
|
||||
|
||||
// CN_EVENT: any event bits accumulated
|
||||
if (port->evtWord & port->evtMask) {
|
||||
if (port->comDeb.evtWord & port->comDeb.evtMask) {
|
||||
notifyBits |= CN_EVENT;
|
||||
}
|
||||
|
||||
if (notifyBits) {
|
||||
PostMessage(port->hwndNotify, WM_COMMNOTIFY, (WPARAM)port->commId, (LPARAM)notifyBits);
|
||||
if (notifyBits && pfnPostMessage) {
|
||||
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) {
|
||||
port->errorFlags |= CE_BREAK;
|
||||
port->evtWord |= EV_BREAK;
|
||||
port->comDeb.evtWord |= EV_BREAK;
|
||||
}
|
||||
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);
|
||||
|
||||
// Update ComDEB MSR shadow (offset 35) for third-party code
|
||||
port->comDeb.msrShadow = msr;
|
||||
|
||||
if (msr & MSR_DCTS) {
|
||||
port->evtWord |= EV_CTS;
|
||||
port->comDeb.evtWord |= EV_CTS;
|
||||
|
||||
// RTS/CTS flow control: stop/start TX based on CTS
|
||||
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||
|
|
@ -167,15 +170,15 @@ static void handleMsr(PortStateT *port)
|
|||
}
|
||||
|
||||
if (msr & MSR_DDSR) {
|
||||
port->evtWord |= EV_DSR;
|
||||
port->comDeb.evtWord |= EV_DSR;
|
||||
}
|
||||
|
||||
if (msr & MSR_TERI) {
|
||||
port->evtWord |= EV_RING;
|
||||
port->comDeb.evtWord |= EV_RING;
|
||||
}
|
||||
|
||||
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
|
||||
port->evtWord |= EV_RXCHAR;
|
||||
port->comDeb.evtWord |= EV_RXCHAR;
|
||||
|
||||
// Read LSR again for next byte
|
||||
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||
|
|
@ -270,7 +273,7 @@ static void handleTx(PortStateT *port)
|
|||
// Nothing to send -- disable THRE interrupt
|
||||
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) & ~IER_THRE));
|
||||
// TX empty event
|
||||
port->evtWord |= EV_TXEMPTY;
|
||||
port->comDeb.evtWord |= EV_TXEMPTY;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -478,6 +489,12 @@ void _far _interrupt isr3(void)
|
|||
{
|
||||
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++) {
|
||||
if (ports[i].isOpen && ports[i].irq == 3) {
|
||||
isrDispatch(&ports[i]);
|
||||
|
|
@ -496,6 +513,12 @@ void _far _interrupt isr4(void)
|
|||
{
|
||||
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++) {
|
||||
if (ports[i].isOpen && ports[i].irq == 4) {
|
||||
isrDispatch(&ports[i]);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ LINK = link
|
|||
# -ASw Small model, SS!=DS (Windows DLL)
|
||||
# -Gsw No stack probes, Windows prolog/epilog
|
||||
# -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
|
||||
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp1 -Ze
|
||||
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp2 -Ze
|
||||
|
||||
# Linker flags:
|
||||
# /NOD No default libraries
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ OBJS = mscomm.obj serial.obj
|
|||
# -----------------------------------------------------------------------
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) mscomm.def mscomm.res
|
||||
$(TARGET): $(OBJS) mscomm.def mscomm.res vbapi.lib
|
||||
$(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), mscomm.def
|
||||
$(RC) mscomm.res $(TARGET)
|
||||
|
||||
|
|
|
|||
14
vbx/mscomm.h
14
vbx/mscomm.h
|
|
@ -5,6 +5,13 @@
|
|||
#ifndef MSCOMM_H
|
||||
#define MSCOMM_H
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Resource IDs
|
||||
// -----------------------------------------------------------------------
|
||||
#define IDB_MSCOMM 8000
|
||||
|
||||
#ifndef RC_INVOKED
|
||||
|
||||
#include "vbapi.h"
|
||||
#include "serial.h"
|
||||
|
||||
|
|
@ -21,11 +28,6 @@ typedef signed char int8_t;
|
|||
#define _STDINT_DEFINED
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Resource IDs
|
||||
// -----------------------------------------------------------------------
|
||||
#define IDB_MSCOMM 8000
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Property indices (position in npproplist array)
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -138,4 +140,6 @@ typedef struct {
|
|||
// -----------------------------------------------------------------------
|
||||
extern HANDLE ghInstance;
|
||||
|
||||
#endif // RC_INVOKED
|
||||
|
||||
#endif // MSCOMM_H
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// -----------------------------------------------------------------------
|
||||
int16_t serialClose(int16_t commId);
|
||||
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 serialFlush(int16_t commId, int16_t queue);
|
||||
UINT serialGetEventMask(int16_t commId, UINT mask);
|
||||
|
|
|
|||
|
|
@ -37,13 +37,17 @@ typedef signed char int8_t;
|
|||
|
||||
// -----------------------------------------------------------------------
|
||||
// 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_CLRDTR CLRDTR
|
||||
#define SERIAL_SETRTS SETRTS
|
||||
#define SERIAL_CLRRTS CLRRTS
|
||||
#define SERIAL_SETBREAK SETBREAK
|
||||
#define SERIAL_CLRBREAK CLRBREAK
|
||||
#define SERIAL_SETBREAK 8
|
||||
#define SERIAL_CLRBREAK 9
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Function prototypes
|
||||
|
|
|
|||
12
vbx/vbapi.h
12
vbx/vbapi.h
|
|
@ -17,17 +17,15 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// Core types
|
||||
// -----------------------------------------------------------------------
|
||||
#ifndef USHORT
|
||||
typedef unsigned short USHORT;
|
||||
#endif
|
||||
|
||||
typedef WORD HCTL; // Handle to a VBX control instance
|
||||
typedef WORD HSZ; // Handle to a VB-managed string
|
||||
typedef LONG HLSTR; // Handle to a VB long string
|
||||
typedef USHORT ERR; // Error code
|
||||
|
||||
// Segment type for MSVC 1.52
|
||||
#ifndef _SEGMENT_DEFINED
|
||||
typedef unsigned short _segment;
|
||||
#define _SEGMENT_DEFINED
|
||||
#endif
|
||||
|
||||
// Flag type
|
||||
typedef DWORD FL;
|
||||
|
||||
|
|
@ -196,7 +194,7 @@ struct tagMODEL {
|
|||
FL fl; // MODEL_* flags
|
||||
PCTLPROC pctlproc; // Control procedure
|
||||
USHORT fsClassStyle; // Window class style bits
|
||||
USHORT flWndStyle; // Window style bits
|
||||
FL flWndStyle; // Window style bits
|
||||
USHORT cbCtlExtra; // Bytes of per-instance data
|
||||
USHORT idBmpPalette; // Toolbox bitmap resource ID
|
||||
PSTR npszDefCtlName; // Default control name (near)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue