/* Copyright (C) Magna Carta Software, Inc. 1990, 1991. All Rights Reserved Z80SIO.C -- Routines specific to the Zilog Z80SIO and Z80DART. */ #define CCT_DEVELOPMENT #include #include #if (defined(MSC) || defined(__WATCOMC__) || defined(__HIGHC__)) #include #endif /* MSC v6.0A bugs force sub-optimization of this file To see the problem, try $(C_OPT) and comment the pragma below, instead of $(C_OPTR) in small model */ #if defined(MSC) #pragma optimize("e", off) #endif /* UZ80_DEINIT -- When finished -- close the port */ short uz80_deinit_(COMM_PORT *p) { uz80_deinstall_ipr_(p, RECEIVE); uz80_deinstall_ipr_(p, TRANSMIT); uz80_set_(p, RTS, LOW); uz80_set_(p, DTR, LOW); return (0); } /* UZ80_GET -- General purpose parameter retriever for the INS8250 family of UARTs. Return values: Success: The value of the UART register that sets stopbits; EOF : Invalid parameter; */ short uz80_get_(COMM_PORT *p, short cmd) { short ret = EOF; if (cmd >= WR0 && cmd <= WR7) uz80_get_wr_(p, cmd); else if (cmd >= RR0 && cmd <= RR2) uz80_get_rr_(p, cmd); else switch (cmd) { /* DATA FORMAT */ case DATABITS: ret = uz80_get_databits_(p, RX); break; case PARITY: ret = uz80_get_wr_(p, WR4) & UZ80_PARITY_MASK; break; case SPEED: ret = (WORD) p->speed; break; case STOPBITS: ret = uz80_get_stopbits_(p); break; /* RS-232 */ case CTS: ret = (uz80_get_rr_(p, RR0) & UZ80_CTS_MASK) >> 5; break; case DCD: ret = (uz80_get_rr_(p, RR0) & UZ80_DCD_MASK) >> 3; break; case DSR: ret = EOF; break; case DTR: ret = (uz80_get_wr_(p, WR5) & UZ80_DTR_MASK) >> 7; break; case RI: ret = (uz80_get_rr_(p, RR0) & UZ80_RI_MASK) >> 4; break; case RTS: ret = (uz80_get_wr_(p, WR5) & UZ80_RTS_MASK) >> 1; break; default: ret = EOF; } return (ret); } /* UZ80_GET_DATABITS -- Read the current setting of the number of databits for either transmit or receive. */ short uz80_get_databits_(COMM_PORT *p, short direction) { short ret = EOF; if (direction == TX) ret = uz80_get_wr_(p, WR5) & UZ80_TX_DATABITS_MASK; else if (direction == RX) ret = uz80_get_wr_(p, WR3) & UZ80_RX_DATABITS_MASK; if (ret == UZ80_TX_DATA5 || ret == UZ80_RX_DATA5) ret = DATABITS5; else if (ret == UZ80_TX_DATA6 || ret == UZ80_RX_DATA6) ret = DATABITS6; else if (ret == UZ80_TX_DATA7 || ret == UZ80_RX_DATA7) ret = DATABITS7; else if (ret == UZ80_TX_DATA8 || ret == UZ80_RX_DATA8) ret = DATABITS8; else ret = EOF; return (ret); } /* UZ80_GET_RR -- Read the specified Z-80 SIO read register. Arguments: COMM_PORT *p -- pointer to the comm. port. WORD reg -- the name or number of the register to read. */ short uz80_get_rr_(COMM_PORT *p, WORD reg) { reg &= 0X7F; /* clear the high bit in case RRx is being used */ if (!reg) outp(p->ubase_addr, UZ80_ES_RESET); if (reg) outp(p->ubase_addr, reg); return(inp(p->ubase_addr)); } /* UZ80_GET_RX_XLAT -- Query the setting of each of the input translation variables used in c_getc() and c_gets(). */ short uz80_get_rx_xlat_(COMM_PORT *p, short item) { short ret = 0; switch (item) { case XLAT: ret = isrxxlat(p); break; /* OFF or ON */ case LOCAL_ECHO: ret = isrxecholocal(p); break; /* OFF or ON */ case REMOTE_ECHO: ret = isrxechoremote(p); break;/* OFF or ON */ case PRINTER_ECHO: ret = isrxechoprn(p); break; /* OFF or ON */ case CAPTURE_BUFFER_ECHO: ret = isrxechobuf(p); break; /* OFF or ON */ /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: ret = p->rx_eol; break; case CASE: ret = p->rx_case_convert; break; case ASCII_ONLY: ret = p->rx_ascii_only; break; case INTERBYTE_DELAY: ret = p->rx_ibdelay; break; case FLOWCTL: ret = p->flowctl & RX; break; /* OFF or ON */ case XONCHAR: ret = p->xonchar; break; /* ASCII value */ case XOFFCHAR: ret = p->xoffchar; break; /* ASCII value */ case ABORT_KEY: ret = p->abort_key; break; default: ret = EOF; break; } return (ret); } /* UZ80_GET_SPEED -- Get the data rate (bps) for the specified port. */ long uz80_get_speed_(COMM_PORT *p) { return (p->speed); } /* UZ80_GET_STOPBITS -- Read the current setting of the number of stopbits. */ short uz80_get_stopbits_(COMM_PORT *p) { short ret; ret = (uz80_get_wr_(p, WR4) & UZ80_STOPBITS_MASK) >> 2; if (ret == 2) ret = STOPBITS15; else if (ret == 3) ret = STOPBITS2; return (ret); } /* UZ80_GET_TX_XLAT -- Return the value of various transmit data translation parameters. */ short uz80_get_tx_xlat_(COMM_PORT *p, short item) { short ret = 0; switch (item) { case LOCAL_ECHO: ret = istxecholocal(p); break; /* OFF or ON */ case REMOTE_ECHO: ret = istxechoremote(p);break; /* OFF or ON */ case PRINTER_ECHO: ret = istxechoprn(p); break; /* OFF or ON */ case CAPTURE_BUFFER_ECHO: ret = istxechobuf(p); break; /* OFF or ON */ case XLAT: ret = istxxlat(p); break; /* OFF or ON */ /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: ret = p->tx_eol; break; case CASE: ret = p->tx_case_convert; break; case ASCII_ONLY: ret = p->tx_ascii_only; break; /* INTERBYTE AND INTERLINE DELAYS (in ms.). */ case INTERBYTE_DELAY: ret = p->tx_ibdelay; break; case INTERLINE_DELAY: ret = p->tx_ildelay; break; /* CHARACTER TO TRIGGER INTERLINE DELAY */ case INTERLINE_DELAY_CHAR: ret = p->tx_ildelay_ch; break; case FLOWCTL: ret = p->flowctl & TX; break; /* OFF or ON */ case TX_DELAY: ret = p->tx_delay; break; case TX_DELAYCHAR: ret = p->tx_delaych; break; case XONCHAR: ret = p->xonchar; break; /* ASCII value */ case XOFFCHAR: ret = p->xoffchar; break; /* ASCII value */ /* ASCII CODE OF KEY TO ABORT XOFF */ case ABORT_KEY: ret = p->abort_key; break; default: ret = EOF; break; } return (ret); } /* UZ80_GET_WR -- Read the specified Z-80 SIO write register. Arguments: COMM_PORT *p -- pointer to the comm. port. WORD reg -- the name or number of the register to write to. */ short uz80_get_wr_(COMM_PORT *p, short reg) { return (p->uart.uz80sio.vwr[reg]); } /* UZ80_INIT -- Initialize the Zilog Z80SIO/DART. */ short uz80_init(COMM_PORT *p, WORD cha_addr, WORD addr, long speed, WORD databits, WORD parity, WORD stopbits) { short i; if (p->open) return (PORT_OPEN); /* INITIALIZE DATA IN THE COMM_PORT STRUCTURE */ p->utype = Z80SIO; p->ubase_addr = addr; p->udata_reg_addr = addr - 2; p->rxdatabits = databits; p->txdatabits = databits; p->parity = parity; p->stopbits = stopbits; p->speed = speed; p->dtr = HIGH; p->rts = HIGH; p->abort_key = ESCAPE; p->xoffchar = DC3; p->xonchar = DC1; /* INITIALIZE FUNCTION POINTERS IN THE COMM_PORT STRUCTURE */ p->set = uz80_set_; p->get = uz80_get_; p->get_rx_xlat = uz80_get_rx_xlat_; p->set_rx_xlat = uz80_set_rx_xlat_; p->get_tx_xlat = uz80_get_tx_xlat_; p->set_tx_xlat = uz80_set_tx_xlat_; p->set_speed = uz80_set_speed_; p->get_speed = uz80_get_speed_; p->rxstat = uz80_rxstat_; p->txstat = p->txstat_ = uz80_txstat_; p->c_read = uz80_read_; p->c_write = p->c_write_ = (short (*)(COMM_PORT *, WORD)) uz80_write_; p->install_ipr = uz80_install_ipr_; p->deinstall_ipr = uz80_deinstall_ipr_; p->deinit_port = uz80_deinit_; p->enable_comm_int = uz80_enable_comm_int_; p->disable_comm_int = uz80_disable_comm_int_; /* INITIALIZE ISR PTRS. */ for (i=0; i < 4; i++) p->a_ipr[i] = (void FAR_ *) com_dummy_; /* WE MUST RESET CH. A TO STABILIZE INTERRUPT LOGIC */ outp(cha_addr, UZ80_CH_RESET); /* CHANNEL RESET */ uz80_set_reg_(p, WR0, UZ80_CH_RESET); /* SET THE Z80 REFERENCE CLOCK SPEED */ uz80_mod_reg_(p, WR4, UZ80_CLOCK_MASK, p->uart.uz80sio.ref_clk_speed); /* SET THE REMAINING WR4 REGISTERS (MUST DO THIS BEFORE WR1, WR3,WR5 */ uz80_set_stopbits_(p, p->stopbits); uz80_set_parity_(p, p->parity); /* ENABLE RECEIVE */ uz80_mod_reg_(p, WR3, UZ80_RX_ENABLE, UZ80_RX_ENABLE); /* ENABLE TRANSMIT */ uz80_mod_reg_(p, WR5, UZ80_TX_ENABLE, UZ80_TX_ENABLE); /* OPEN THE PORT */ return (c_open_(p)); } /* UZ80_MOD_REG -- Modify a Z-80 SIO write register. */ short uz80_mod_reg_(COMM_PORT *p, WORD reg, WORD mask, WORD value) { int regval; if (reg) { regval = p->uart.uz80sio.vwr[reg]; regval &= ~mask; /* AND with mask to clear selected bits */ regval |= value; /* OR with value to set selected bits */ uz80_set_reg_(p, reg, regval); } else outp(p->ubase_addr, value); return (regval); } /* UZ80_READ -- read a byte from the data register of the UART of the specified serial port. Return: the byte read. */ short uz80_read_(COMM_PORT *p) { BYTE c; c = (BYTE) inp(p->udata_reg_addr); if (p->rxdatabits == 7) c &= 0X7F; else if (p->rxdatabits == 6) c &= 0X3F; else if (p->rxdatabits == 5) c &= 0X1F; return (c); } /* UZ80_RXSTAT -- report whether a character is ready in the receive buffer Return: FALSE (NULL) if no byte is ready. Else TRUE. */ short uz80_rxstat_(COMM_PORT *p) { return( inp(p->ubase_addr) & UZ80_RX_MASK); } /* UZ80_SEND_BREAK -- Send a break. */ short uz80_send_break_(COMM_PORT *p) { int reg; reg = p->uart.uz80sio.vwr[5]; reg |= UZ80_BREAK_MASK; p->uart.uz80sio.vwr[5] = (BYTE) reg; (*p->set)(p, p->ubase_addr, reg); mspause(300); reg &= ~UZ80_BREAK_MASK; p->uart.uz80sio.vwr[5] = (BYTE) reg; (*p->set)(p, p->ubase_addr, reg); return (0); } /* UZ80_SET -- General purpose command dispatcher for the Z-80 SIO family of USARTs. Return values: 0 -- Success; EOF -- Invalid parameter; */ short uz80_set_(COMM_PORT *p, short cmd, short value) { short ret = 0; if (cmd >= WR0 && cmd <= WR7) ret = uz80_mod_reg_(p, cmd, 0XFF, value); else switch (cmd) { /* DATA FORMAT */ case DATABITS: ret = uz80_set_databits_(p, value, TX); ret = uz80_set_databits_(p, value, RX); break; case PARITY: ret = uz80_set_parity_(p, value); break; case SPEED: ret = uz80_set_speed_(p, (long) value); break; case STOPBITS: ret = uz80_set_stopbits_(p, value); break; case BREAK: uz80_send_break_(p); break; /* RS-232 */ case DTR: uz80_mod_reg_(p, WR5, UZ80_DTR_MASK, value << 7); break; case RTS: uz80_mod_reg_(p, WR5, UZ80_RTS_MASK, value << 1); break; default: ret = EOF; } return (ret); } /* UZ80_SET_CLOCK -- Set the Z80 reference clock at the Z80SIO chip to the desired speed. */ void uz80_set_clock(COMM_PORT *p, WORD speed) { uz80_mod_reg_(p, WR4, UZ80_CLOCK_MASK, speed); } /* UZ80_SET_DATABITS -- Set the current number of databits for transmit or receive. */ short uz80_set_databits_(COMM_PORT *p, short value, short direction) { if (direction == RX) { switch (value) { case 5: value = 0; break; case 6: value = 0X80; break; case 7: value = 0X40; break; case 8: value = 0XC0; break; default: return (EOF); } return(uz80_mod_reg_(p, WR3, UZ80_RX_DATABITS_MASK, value)); } else { switch (value) { case 5: value = 0; break; case 6: value = 0X40; break; case 7: value = 0X20; break; case 8: value = 0X60; break; default: return (EOF); } return (uz80_mod_reg_(p, WR5, UZ80_TX_DATABITS_MASK, value)); } } /* UZ80_SET_PARITY -- Set the parity for the designated serial port to the designated value. The type of UART is obtained from the COMM_PORT structure. Return values: Success: The value of the UART register that sets parity; -1 : Invalid UART type; -2 : Invalid parity value specified; */ short uz80_set_parity_(COMM_PORT *p, short value) { switch (value) { case PARITY_NONE: value = UZ80_PARITY_NONE; break; case PARITY_EVEN: value = UZ80_PARITY_EVEN; break; case PARITY_ODD: value = UZ80_PARITY_ODD; break; default: return (0); } uz80_mod_reg_(p, WR4, UZ80_PARITY_MASK, value); return (0); } /* UZ80_SET_REG -- Write to the specified UART register. Also update the Z80SIO/Z80DART virtual register. Arguments: COMM_PORT *p -- pointer to the comm. port. unsigned reg -- the name or number of the register to write to. int value -- value to write to the register. */ void uz80_set_reg_(COMM_PORT *p, short reg, short value) { p->uart.uz80sio.vwr[reg] = (BYTE) value; if (reg) outp(p->ubase_addr, reg | UZ80_ES_RESET); outp(p->ubase_addr, value); } /* UZ80_SET_RX_XLAT -- Specify the type of input translation used by c_getc() and c_gets(). */ short uz80_set_rx_xlat_(COMM_PORT *p, short item, short value) { short ret = 0; switch (item) { case LOCAL_ECHO: case REMOTE_ECHO: case PRINTER_ECHO: case CAPTURE_BUFFER_ECHO: if (value) p->echo |= item; else p->echo &= ~item; break; /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: p->rx_eol = value; break; case CASE: /* UPPER, LOWER, FALSE */ p->rx_case_convert = value; break; case ASCII_ONLY: p->rx_ascii_only = value; break; case INTERBYTE_DELAY: p->rx_ibdelay = value; break; case TRAILINGBYTE_DELAY: p->rx_tbdelay = value; break; case FLOWCTL: if (value) p->flowctl |= (value >> 4); else p->flowctl &= ~RX; break; case FLOWHIGH: p->flowhigh = value; break; case XONCHAR: p->xonchar = value; break; /* ASCII value */ case XOFFCHAR: p->xoffchar = value; break; /* ASCII value */ /* SCAN CODE/CHARACTER CODE OF KEY TO ABORT */ case ABORT_KEY: p->abort_key = value; break; case RX_BUFFER_HWM: if ((value >= 0 && value <=100) && (p->rx_bufsiz/100 * value > p->lowwater)) p->highwater = p->rx_bufsiz/100 * value; break; case RX_BUFFER_LWM: if ((value >= 0 && value <=100) && (p->rx_bufsiz/100 * value <= p->highwater)) p->lowwater = p->rx_bufsiz/100 * value; break; default: ret = EOF; break; } if (!ret) { if (value) p->rx_xlat = 1; else p->rx_xlat = isrxxlat(p); } return (ret); } /* UZ80_SET_SPEED -- Set the data rate (bps) at the reference clock for the Zilog Z80SIO. Note: The clock is not incorporated in the chip and this function may not work with all configurations. This function was written for the AST CC-232C and AST CC-232E 3270 emulation boards in X16 clock mode. */ short uz80_set_speed_(COMM_PORT *p, long value) { WORD val; /* CALCULATE THE BAUD RATE -- ASYNCHRONOUS MODE */ switch ((short) value) { case 50: val = 0; break; case 75: val = 1; break; case 110: val = 2; break; case 135: val = 3; break; /* actually 134.5 */ case 150: val = 4; break; case 300: val = 5; break; case 600: val = 6; break; case 1200: val = 7; break; case 1800: val = 8; break; case 2000: val = 9; break; case 2400: val = 10; break; case 3600: val = 11; break; case 4800: val = 12; break; case 7200: val = 13; break; case 9600: val = 14; break; case 19200: val = 15; break; default: return (EOF); } outp(p->uart.uz80sio.clock_reg_addr, val); p->speed = value; return (0); } /* UZ80_SET_STOPBITS -- Set the number of stopbits for the designated serial port to the designated value. Return values: Success: The value of the UART register that sets stopbits; -1 : Invalid UART type; -2 : Invalid number of stopbits specified; */ short uz80_set_stopbits_(COMM_PORT *p, short value) { switch (value) { case STOPBITS0: value = UZ80_STOPBITS0; break; case STOPBITS1: value = UZ80_STOPBITS1; break; case STOPBITS15: value = UZ80_STOPBITS15; break; case STOPBITS2: value = UZ80_STOPBITS2; break; default: return (-2); } return (uz80_mod_reg_(p, WR4, UZ80_STOPBITS_MASK, value)); } /* UZ80_SET_TX_XLAT -- Assign values to data transformation parameters. */ short uz80_set_tx_xlat_(COMM_PORT *p, short item, short value) { short ret = 0; switch (item) { case LOCAL_ECHO: case REMOTE_ECHO: case PRINTER_ECHO: case CAPTURE_BUFFER_ECHO: if (value) p->echo |= (item << 4); else p->echo &= ~(item << 4); break; /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: p->tx_eol = value; break; case CASE: /* UPPER, LOWER, NORMAL */ p->tx_case_convert = value; break; case ASCII_ONLY: p->tx_ascii_only = value; break; case INTERBYTE_DELAY: p->tx_ibdelay = value; break; case TX_DELAY: p->tx_delay = value; break; case TX_DELAYCHAR: p->tx_delaych = value; break; case INTERLINE_DELAY: p->tx_ildelay = value; break; case INTERLINE_DELAY_CHAR: p->tx_ildelay_ch = value; break; case FLOWCTL: if (value) p->flowctl |= value; else p->flowctl &= 0X8F; break; case XONCHAR: /* value to be considered an XON */ if (value == XON_ALL) p->xonchar = 0; /* any character */ else p->xonchar = value; /* a specific char. */ break; case XOFFCHAR: p->xoffchar = value; ret = 0; break; /* ASCII value */ /* SCAN CODE/CHARACTER CODE OF KEY TO ABORT XOFF */ case ABORT_KEY: p->abort_key = value; break; default: ret = EOF; break; } if (!ret) { if (value) p->tx_xlat = 1; else p->tx_xlat = istxxlat(p); } return (ret); } /* UZ80_TXSTAT -- report space in the transmit buffer of the USART. Return Value: 1 -- space available; 0 -- no space available; */ short uz80_txstat_(COMM_PORT *p) { return( inp(p->ubase_addr) & UZ80_TX_MASK); } /* UZ80_WRITE -- write a byte to the data register of the UART of the specified serial port. */ void uz80_write_(COMM_PORT *p, WORD c) { outp(p->udata_reg_addr, c); }