WinComm/drv/lpt.c
2026-10-13 14:05:57 -05:00

221 lines
6.8 KiB
C

// Part of KPCOMM, Copyright (c) 2026 Scott Duensing, under the MIT License.
// See LICENSE.md for license information.
// SPDX-License-Identifier: MIT
//
// lpt.c - Parallel port support for KPCOMM.DRV
//
// The stock COMM.DRV also serves LPT1-LPT3 (cid 0x80-0x82), and Windows
// routes direct-to-port printing through it. Output is polled through
// BIOS INT 17h, which Windows reflects to the real-mode BIOS in both
// standard and enhanced mode. Printers have no input, events, or line
// settings; those calls succeed without doing anything.
#include "commdrv.h"
#define BIOS_PRINTER_INT 0x17
#define PRN_WRITE 0x00 // AH: print the character in AL
#define PRN_INIT 0x01 // AH: initialize (reset) the printer
#define PRN_TIMEOUT 0x01 // Status: timed out
#define PRN_IO_ERROR 0x08 // Status: I/O error
#define PRN_SELECTED 0x10 // Status: printer selected (online)
#define PRN_OUT_OF_PAPER 0x20 // Status: out of paper
#define PRN_FAILED (PRN_TIMEOUT | PRN_IO_ERROR | PRN_OUT_OF_PAPER)
#define LPT1_IRQ 7 // Conventional IRQs reported by GETBASEIRQ
#define LPTN_IRQ 5
// -----------------------------------------------------------------------
// Prototypes (public functions are declared in commdrv.h)
// -----------------------------------------------------------------------
static uint8_t biosPrinter(uint8_t func, uint8_t data, uint16_t index);
static uint16_t lptBase(uint16_t index);
static uint16_t lptErrors(uint8_t status);
static LptStateT lpts[MAX_LPTS];
// -----------------------------------------------------------------------
// biosPrinter - Call BIOS INT 17h; returns the printer status byte
// -----------------------------------------------------------------------
static uint8_t biosPrinter(uint8_t func, uint8_t data, uint16_t index) {
uint8_t status;
_asm {
mov ah, func
mov al, data
mov dx, index
int BIOS_PRINTER_INT
mov status, ah
}
return status;
}
// -----------------------------------------------------------------------
// getOpenLpt - Parallel port state for an open LPT cid, or NULL
// -----------------------------------------------------------------------
LptStateT *getOpenLpt(int16_t commId) {
LptStateT *lpt;
if (!isLptId(commId)) {
return NULL;
}
lpt = &lpts[commId & LPT_INDEX_MASK];
return lpt->isOpen ? lpt : NULL;
}
// -----------------------------------------------------------------------
// isLptId - True if commId names LPT1-LPT3
// -----------------------------------------------------------------------
bool isLptId(int16_t commId) {
return ((uint16_t)commId & ~LPT_INDEX_MASK) == LPT_ID_FLAG && ((uint16_t)commId & LPT_INDEX_MASK) < MAX_LPTS;
}
// -----------------------------------------------------------------------
// lptBase - I/O address of a parallel port from the BIOS (0 = none)
// -----------------------------------------------------------------------
static uint16_t lptBase(uint16_t index) {
return readBiosWord(BDA_LPT_BASES + index * sizeof(uint16_t));
}
// -----------------------------------------------------------------------
// lptBaseIrq - GETBASEIRQ for a parallel port
// -----------------------------------------------------------------------
int32_t lptBaseIrq(int16_t commId) {
uint16_t index;
uint16_t base;
if (!isLptId(commId)) {
return BASEIRQ_NO_PORT;
}
index = commId & LPT_INDEX_MASK;
base = lptBase(index);
if (base == 0) {
return BASEIRQ_NO_PORT;
}
return MAKELONG(base, (index == 0) ? LPT1_IRQ : LPTN_IRQ);
}
// -----------------------------------------------------------------------
// lptClose - Close a parallel port (trmcom); CE_MODE if it was not open
// -----------------------------------------------------------------------
int16_t lptClose(int16_t commId) {
LptStateT *lpt;
lpt = getOpenLpt(commId);
if (!lpt) {
return (int16_t)CE_MODE;
}
lpt->isOpen = false;
return 0;
}
// -----------------------------------------------------------------------
// lptCloseAll - Close every parallel port (WEP)
// -----------------------------------------------------------------------
void lptCloseAll(void) {
uint16_t i;
for (i = 0; i < MAX_LPTS; i++) {
lpts[i].isOpen = false;
}
}
// -----------------------------------------------------------------------
// lptErrors - CE_* flags for a failing printer status
// -----------------------------------------------------------------------
static uint16_t lptErrors(uint8_t status) {
uint16_t errors;
errors = 0;
if (status & PRN_TIMEOUT) {
errors |= CE_PTO;
}
if (status & PRN_IO_ERROR) {
errors |= CE_IOE;
}
if (!(status & PRN_SELECTED)) {
errors |= CE_DNS;
}
if (status & PRN_OUT_OF_PAPER) {
errors |= CE_OOP;
}
return errors;
}
// -----------------------------------------------------------------------
// lptMaxId - GETMAXLPT: highest LPT cid the BIOS reports (LPT1 if none)
// -----------------------------------------------------------------------
int16_t lptMaxId(void) {
uint16_t count;
count = 0;
while (count < MAX_LPTS && lptBase(count) != 0) {
count++;
}
return (int16_t)(LPT_ID_FLAG + ((count > 0) ? count - 1 : 0));
}
// -----------------------------------------------------------------------
// lptOpen - Open a parallel port (inicom); returns 0 or IE_*
// -----------------------------------------------------------------------
int16_t lptOpen(const DCB FAR *dcb) {
LptStateT *lpt;
uint16_t index;
if (!isLptId(dcb->Id)) {
return IE_BADID;
}
index = dcb->Id & LPT_INDEX_MASK;
lpt = &lpts[index];
if (lpt->isOpen) {
return IE_OPEN;
}
if (lptBase(index) == 0) {
return IE_HARDWARE;
}
_fmemset(lpt, 0, sizeof(LptStateT));
_fmemcpy(&lpt->dcb, dcb, sizeof(DCB));
lpt->isOpen = true;
return 0;
}
// -----------------------------------------------------------------------
// lptReset - Pulse the printer's reset line (RESETDEV)
// -----------------------------------------------------------------------
void lptReset(LptStateT *lpt) {
(void)biosPrinter(PRN_INIT, 0, (uint16_t)(lpt - lpts));
}
// -----------------------------------------------------------------------
// lptWrite - Print up to len bytes; returns the number accepted
//
// Stops at the first failure and records it for stacom.
// -----------------------------------------------------------------------
int16_t lptWrite(LptStateT *lpt, const uint8_t FAR *buf, int16_t len) {
uint16_t index;
int16_t i;
uint8_t status;
index = (uint16_t)(lpt - lpts);
for (i = 0; i < len; i++) {
status = biosPrinter(PRN_WRITE, buf[i], index);
if (status & PRN_FAILED) {
lpt->errorFlags |= lptErrors(status);
break;
}
}
return i;
}