home *** CD-ROM | disk | FTP | other *** search
- /*
- * port.c: hterm serial port control
- *
- * Author: HIRANO Satoshi
- * (C) 1989 Halca Computer Science Laboratory TM
- * University of Tokyo
- *
- * 2.2 89/05/16 Halca.Hirano V2.2 distribution
- * 2.3 89/06/17 Halca.Hirano remove #else
- * set 8251 from port98.asm
- * 2.4 89/07/20 Halca.Hirano add sending kanji conversion (Thank you ohta@ricoh)
- * 2.5 89/07/26 Halca.Hirano add 7 bit space parity
- * 2.6 89/07/28 Halca.Hirano support multiple port
- * ---- V2.3.-1 distribution ----
- * 2.7 89/09/15 Halca.Hirano fix upload kanji bug, add RTS/CTS flow control
- * ---- V2.4.0 distribution ----
- * 2.8 89/11/30 mik@uop add NEC kanji code
- * 2.9 89/11/30 Halca.Hirano send kanji in/out code only at code changing timing
- * 3.0 89/12/06 Halca.Hirano drop ER/RS if requested at exit
- * 3.1 90/06/24 Halca.Hirano change GetCPUClock smater
- * 3.2 90/06/27 Halca.Hirano
- * I use 0x22 rather than 0xaa for reseting of i8251
- * before issue reset command on PC98 machines to avoid
- * generating illegal output character.
- *
- * $Header: port.cv 1.12 90/07/04 01:04:50 hirano Exp $
- */
-
- #include "option.h"
- #include <stdio.h>
- #include "config.h"
- #include "hterm.h"
- #include "default.h"
- #include "global.h"
-
- #ifdef PC98
- static u_char CPUClock; /* PC9801 CPU clock, it takes 8 or 10 (MHz) */
- #endif /* PC98 */
- static u_char oldMask; /* original interrupt mask */
- static int oldPortNo;
- u_char onMask; /* receive on interrupt mask */
- u_char offMask; /* receive off interrupt mask */
- u_short portAddress; /* port address */
- bool sendingKanji; /* sending kanji flag */
- void (INTERRUPT FAR *interruptHandler)();/* hterm interrupt handler */
- void (INTERRUPT FAR *oldHandler)(); /* original interrupt handler */
-
- /*
- * serial port device address and vectors
- */
- struct {
- u_short address; /* port address */
- u_short vector; /* vector number */
- u_char mask; /* IMR mask value */
- } portInfo[] =
- {{ADDR_PORT_COM1, PORT_VECTOR_COM1, INT_MASK_COM1},
- {ADDR_PORT_COM2, PORT_VECTOR_COM2, INT_MASK_COM2},
- {ADDR_PORT_COM3, PORT_VECTOR_COM3, INT_MASK_COM3},
- {ADDR_PORT_COM4, PORT_VECTOR_COM4, INT_MASK_COM4}};
-
- /*
- * baudrate to internal baudrate expression conversion table
- */
- struct s_baudtable {
- u_short Baud, Num;
- } baudtable[BAUD_NUM_MAX+1] = {
- {300, 1}, {1200, 2}, {2400, 3}, {4800, 4}, {9600, 5},
- {19200, 6}, {38400, 7}, {0, 0}};
-
- #if defined IBMPC || defined J3100
- /*
- * IBMPC parity/stop bit register masks
- */
- struct {
- u_char parity; /* hardware register bits */
- u_char mask; /* character mask bits */
- } parityTable[] = {
- /* NO_PAR_8 */ {0x03, 0xff},
- /* EV_PAR_8 */ {0x1b, 0xff},
- /* NO_PAR_7 */ {0x02, 0x7f},
- /* EV_PAR_7 */ {0x1a, 0x7f},
- /* SP_PAR_7 */ {0x03, 0x7f}};
- #endif /* IBMPC || J3100 */
-
- #ifdef PC98
- /*
- * PC9801 baudrate to timer load value table for 10Mhz machine
- */
- u_short baudToTimer5Mhz10Mhz[BAUD_NUM_MAX] =
- /* 300 1200 2400 4800 9600 19200 38400 */
- {0x80, 0x20, 0x10, 0x20, 0x10, 0x8, 0x4};
- /*
- * PC9801 baudrate to timer load value table for 10Mhz machine
- * we can use up to 9600 baud on 8/12 MHz machine
- */
- u_short baudToTimer8Mhz12Mhz[BAUD_NUM_MAX] =
- /* 300 1200 2400 4800 9600 19200 38400 */
- {0x68, 0x1a, 0x0d, 0x1a, 0x0d, 0x0d, 0x0d};
-
- /*
- * PC9801 parity/stop bit register masks
- */
- struct {
- u_char parity; /* hardware register bits */
- u_char mask; /* character mask bits */
- } parityTable[] = {
- /* NO_PAR_8 */ {0x0c, 0xff},
- /* EV_PAR_8 */ {0x3c, 0xff},
- /* NO_PAR_7 */ {0x08, 0x7f},
- /* EV_PAR_7 */ {0x38, 0x7f},
- /* SP_PAR_7 */ {0x0c, 0x7f}};
- #endif
-
-
- void outPortC(unsigned char c);
- void getCPUClock(void);
-
- /*
- ** void portInit()
- *
- * initialize port device variables and hardware
- */
- void portInit()
- {
- baudrate = DEFAULT_BAUDRATE;
- xonXoff = DEFAULT_FLOWCTRL;
- paritybit = DEFAULT_PARITY;
- stopbit = DEFAULT_STOP_BIT;
- oldPortNo = portNo = DEFAULT_PORT_NO;
- dropER = DEFAULT_KEEP_DROP_LINE;
- online = YES; /* online */
- downLoading = NO; /* no down load mode */
- maskFlag = NO; /* no mask mode */
- sendingKanji = NO; /* sending ascii */
- cMask = 0xff; /* mask 0xff in/out chars */
- oldHandler = 0;
- #ifdef PC98
- getCPUClock(); /* get CPU clock */
- #endif
- portReInit();
- }
-
- /*
- ** void portReInit()
- *
- * re-initialize interrupt vector and hardware
- *
- * NOTE: This function should be called after portEnd()
- */
- void portReInit()
- {
- oldMask = inpWait(IMR);
- initPortBuffer(); /* iniz port buffer */
- interruptHandler = getPortInterruptHandlerAddress();
- oldHandler = GET_DOSVECT(portInfo[portNo].vector);
- }
-
- /*
- ** void portSetup()
- *
- * re-initialize hardware (parity etc.) as user's preference
- */
- void portSetup()
- {
- maskFlag = NO; /* clear XOffed flag */
- kstate = 0; /* kanji state 0 */
- sendingKanji = NO; /* not sending kanji */
-
- retry:
- if ((baud = baudNum(baudrate)) == -1) { /* calc baudrate */
- sprintf(tmpBuf, msg_baudrate, (long)baudrate);
- conPrint(tmpBuf);
- conPrint("set to 9600\r\n");
- #ifdef PC98
- conPrint(msg_pc98baud);
- #endif /* PC98 */
- baudrate = 9600;
- goto retry;
- }
- parity = parcalc(paritybit, stopbit); /* set parity */
- setPort(portNo); /* set port number and address */
- }
-
- /*
- ** void portEnd()
- *
- * de-initialize port device; restore vector, restore interrupt mask
- */
- void portEnd()
- {
- outpWait(IMR, oldMask);
- if (oldHandler)
- SET_DOSVECT(portInfo[portNo].vector, oldHandler);
- oldHandler = 0;
- }
-
- /*
- ** void outPort(c)
- *
- * send a char to the remote system with kanji conversion
- *
- * in:
- * c word to send
- * if kanji, xxyy
- * else 00xx
- * NOTE: xx00 should never come here!
- */
- void outPort(c)
- register u_short c;
- {
- register u_char cu, cl;
-
- if (downLoading) {
- outPortC(c);
- return;
- }
-
- #ifdef KANJI
- cu = c >> 8; cl = c & 0xff;
- if (!cu) {
- /*
- * if not kanji
- */
- if (sendingKanji) {
- /*
- * send 'end of kanji' code
- */
- sendingKanji = NO;
- switch (kanjiCode) {
- case OJIS:
- case NJIS:
- outPortC(ESC); outPortC('('); outPortC('J');
- break;
- #ifdef NEC_KANJI
- case NECKANJI:
- outPortC(SUB); outPortC('q');
- break;
- #endif /* NEC_KANJI */
- }
- }
- if (isKana(cl)) {
- /*
- * if hankaku kana
- */
- switch (kanjiCode) {
- case SJIS:
- break;
- case OJIS:
- case NJIS:
- #ifdef NEC_KANJI
- case NECKANJI:
- #endif /* NEC_KANJI */
- outPortC(SO);
- outPortC(c & 0x7f);
- cl = SI;
- break;
- case EUC:
- outPortC(SS2);
- break;
- }
- /* kana second char is out by next outPortC() */
- }
- outPortC(cl); /* else ASCII */
- return;
- }
- /*
- * kanji
- */
- if (sendingKanji == NO) {
- /*
- * send kanji in code
- */
- sendingKanji = YES;
- switch (kanjiCode) {
- case OJIS:
- case NJIS:
- outPortC(ESC); outPortC('$');
- outPortC(kanjiCode == NJIS ? 'B': '@');
- break;
- #ifdef NEC_KANJI
- case NECKANJI:
- outPortC(SUB); outPortC('p');
- break;
- #endif /* NEC_KANJI */
- }
- }
- /*
- * send kanji body
- */
- switch (kanjiCode) {
- case SJIS:
- c = JIStoSJIS(cu, cl);
- outPortC(c >> 8);
- outPortC(c);
- break;
- case OJIS:
- case NJIS:
- #ifdef NEC_KANJI
- case NECKANJI:
- #endif /* NEC_KANJI */
- outPortC((c >> 8) & 0x7f);
- outPortC(c & 0x7f);
- break;
- case EUC:
- outPortC(cu | 0x80);
- outPortC(cl | 0x80);
- break;
- }
- #else
- outPortC(c);
- #endif /* KANJI */
- }
-
- /*
- ** static void outPortC(c)
- *
- * send a byte to host with newline, echo back control
- */
- static void outPortC(c)
- u_char c;
- {
- if (online) {
- rowOutPort(c & cMask);
- if (!downLoading && newline && c == CR)
- rowOutPort(LF);
- }
- if (echoMode || !online)
- decodeAnsi(c);
- }
-
- /*
- ** void rowOutPort(c)
- *
- * lowest level send a char to host with time out without code conversion
- */
- void rowOutPort(c)
- u_char c;
- {
- register u_char status;
-
- portTimer = timerLoadValue;
- while (timerLoadValue - portTimer < TX_TIMEOUT) {
- status = inpWait(portAddress+RS_STATUS);
- if ((status & TX_READY) == TX_READY) {
- outp(portAddress+RS_DATA, c);
- return;
- }
- /* CAUTION!
- * MSC5.1 is brain damaged for volatile variable treatment.
- * timerValue is volatile. but optimizer eliminates
- * condition check of while statement.
- */
- nullFunction();
- }
- conPrint("\n\rTransmit time out (CS low)\n\r");
- }
-
- /*
- ** void outString(char *s)
- *
- * send string to the remote system
- */
- void outString(s)
- register char *s;
- {
- while (*s)
- outPort(*s++);
- }
-
- /*
- ** void rowOutPortBuffer(char *buf, int len)
- *
- * send buffer to the remote system
- */
- void rowOutPortBuffer(buf, len)
- char *buf;
- int len;
- {
- register int bp;
-
- for (bp = 0; bp < len; bp++)
- rowOutPort(buf[bp] & cMask);
- }
-
- /*
- ** void outESC(char *s)
- *
- * output escape sequence
- */
- void outESC(s)
- char *s;
- {
- outPort(ESC);
- outString(s);
- }
-
- /*
- ** int baudNum(register u_short baudrate)
- *
- * calc from baudrate to internal number
- */
- int baudNum(baudrate)
- register u_short baudrate;
- {
- register struct s_baudtable *p;
-
- #ifdef PC98
- getCPUClock(); /* this is needed for call from options() */
- #endif
- if (baudrate == 0)
- return(-1);
- #ifdef PC98
- if (CPUClock == 8 && baudrate > 9600)
- return(-1); /* we can use up to 9600 baud with 8/16Mhz clock machine */
- #endif /* PC98 */
- for (p = baudtable; p -> Baud != 0; p++)
- if (p -> Baud == baudrate) return(p -> Num);
- return(-1);
- }
-
- /*
- ** u_short numBaud(num)
- *
- * calc from internal number to baudrate
- */
- u_short numBaud(num)
- register unsigned int num;
- {
- register struct s_baudtable *p;
-
- if (num == 0)
- return(0);
- #ifdef PC98
- if (CPUClock == 8 && num > 5)
- return(0); /* we can use up to 9600 baud with 8/16Mhz clock machine */
- #endif /* PC98 */
- for (p = baudtable; p -> Baud != 0; p++)
- if (p -> Num == num) return(p -> Baud);
- return(0);
- }
-
- /*
- ** void setPort(num)
- *
- * switch port; set port address from number 'num'
- */
- void setPort(num)
- int num;
- {
- if (num > MAX_PORT)
- return;
- portNo = num;
- portAddress = portInfo[portNo].address;
- /*
- * restore old vector and mask
- */
- outpWait(IMR, oldMask); /* mask (really?) old port */
- SET_DOSVECT(portInfo[oldPortNo].vector, oldHandler);
- /*
- * get new port vector and mask
- */
- oldMask = inpWait(IMR);
- oldHandler = GET_DOSVECT(portInfo[portNo].vector);
- /*
- * set new vector
- */
- onMask = ~portInfo[portNo].mask; /* for interrupt handler */
- offMask = portInfo[portNo].mask; /* for interrupt handler */
- outpWait(IMR, oldMask | offMask); /* disable interrupt */
- SET_DOSVECT(portInfo[portNo].vector, interruptHandler);
- /*
- * re-initialize device
- */
- initPortDevice(baud, parity); /* iniz baudrate and parity */
- oldPortNo = portNo;
- }
-
- /*
- ** void flushInputBuffer
- *
- * clear input buffer
- */
- void flushInputBuffer()
- {
- while (getSerial() != -1)
- ;
- }
-
- /*
- ** void shortWait()
- *
- * do nothing but wait for a while
- */
- void shortWait()
- {
- nullFunction();
- nullFunction();
- }
-
- /*
- ** void nullFunction()
- *
- * do nothing but wait for a while
- * don't remove this; this function is used for null loop
- */
- void nullFunction()
- {
- }
-
- /*
- ** int inpWait(addr)
- *
- * I/O input with wait
- */
- int inpWait(addr)
- u_short addr;
- {
- int x;
-
- x = inp(addr);
- shortWait();
- return(x);
- }
-
- /*
- ** void outpWait(addr, value)
- *
- * I/O output with wait
- */
- void outpWait(addr, value)
- u_short addr;
- int value;
- {
- outp(addr,value);
- shortWait();
- }
-
- #ifdef KANJI
- /*
- ** u_short SJIStoJIS(byte_1, byte_2)
- *
- * convert SJIS to JIS
- */
- u_short SJIStoJIS(byte_1, byte_2)
- register u_short byte_1, byte_2;
- {
- u_short c;
-
- byte_1 -= (byte_1 >= 0xa0) ? 0xc1 : 0x81;
- if (byte_2 >= 0x9f) {
- c = (byte_1 << 9) + 0x2200;
- c |= byte_2 - 0x7e;
- } else {
- c = (byte_1 << 9) + 0x2100;
- c |= byte_2 - ((byte_2 <= 0x7e) ? 0x1f : 0x20);
- }
- return(c);
- }
-
- /*
- ** u_short JIStoSJIS(h, l)
- *
- * convert JIS to SJIS
- */
- u_short JIStoSJIS(h, l)
- register u_char h, l;
- {
- if (!(h & 1)) l += (0x7f - 0x21);
- l += (0x40 - 0x21);
- if (l >= 0x7f) ++l;
- h -= 0x21;
- h >>= 1; /* h /= 2; */
- h += 0x81;
- if (h > 0x9f) h += (0xe0 - 0xa0);
- return((h<<8)|l);
- }
-
- #endif /* KANJI */
-
- /*
- *
- ** machine dependent part
- *
- */
-
- #if defined IBMPC || defined J3100
- /*
- ** u_char parcalc(paritybit, stopbit)
- *
- * calclate parity bit register mask from parity and stop bit
- */
- u_char parcalc(paritybit, stopbit)
- u_char paritybit, stopbit;
- {
- register u_char parity = 0;
-
- parity = parityTable[paritybit].parity;
- cMask = parityTable[paritybit].mask;
- switch (stopbit) {
- case STOP_BIT_1: parity |= 0x00; break; /* 1 stop bit */
- case STOP_BIT_2: parity |= 0x04; break; /* 2 stop bit */
- }
- return(parity);
- }
-
- /*
- ** void initPortDevice(baud, parity)
- *
- * iniz hardware baudrate and parity and assert RS/ER
- */
- void initPortDevice(baud, parity)
- u_char baud, parity;
- {
- u_short baudbit;
-
- spl7();
- initPortBuffer(); /* iniz port buffer */
- /* If you don't want to change anything except baudrate & parity,
- * int14 BIOS call isn't suitable in this case.
- * Because it inactivates the interrupt facility in UART.
- */
- /*
- baudbit = (u_short)(921600L/(u_long)(numBaud(baud)));
- // 921600 stands for 14.7456MHz/16 //
- */
- baudbit = (u_short)(115200L/(u_long)(numBaud(baud)));
- /* 115200 stands for 1.8432MHz/16 */
-
- inpWait(portAddress+THR); /* null read receive buffer register */
- inpWait(portAddress+THR); /* null read receive buffer register */
- /*
- * You may set DLAB bit (0x80) of LCR register to modify dividing
- * rate of UART. When the bit is set, you can access two 8 bit
- * register whose address is the same as those of RSDAT or IIR
- */
- outpWait(portAddress+LCR, LCR_DLAB);/* set DLAB bit to store the div rate to UART */
- outpWait(portAddress+DIVL, baudbit & 0xff);
- outpWait(portAddress+DIVH, baudbit >> 8);
- /*
- * the argument "parity" follows the same format as LCR register
- */
- outpWait(portAddress+LCR, parity); /* reset DLAB bit & set modes */
- ER_RS_On(); /* assert ER/RS */
- outpWait(portAddress+IER, 1); /* enable receive interrupt */
- /*
- * enable interrupt
- */
- outpWait(IMR, inpWait(IMR) & onMask);
- splx();
- }
-
- /*
- ** void sendBreak(shortLong)
- *
- * send break code to the remote system
- */
- void sendBreak(shortLong)
- int shortLong;
- {
- int i, outer;
-
- outpWait(portAddress+LCR, parity | LCR_SBRK);/* assert break */
- /*
- * we should wait for more one char when short break
- * NOTE: this code is really ABOUT!
- */
- if (shortLong == 1)
- outer = 2; /* short 2 msec */
- else
- outer = 100; /* long 100 msec */
- for (; outer > 0; --outer)
- for (i = 2000; i > 0; --i) /* 1msec counter */
- nullFunction();
- outpWait(portAddress+LCR, parity); /* negate break */
- }
-
- /*
- ** void ER_RS_On()
- *
- * assert ER/RS line
- */
- void ER_RS_On()
- {
- outpWait(portAddress+MCR, 0x0b); /* RS on (bit1), ER on (bit0) */
- }
-
- /*
- ** void ER_RS_Off()
- *
- * drop ER/RS line, disable input/output
- */
- void ER_RS_Off()
- {
- outpWait(portAddress+MCR, 0x08); /* RS off (bit1), ER off (bit0) */
- }
- #endif /* IBMPC || J3100 */
-
- #ifdef PC98
- /*
- ** u_char parcalc(paritybit, stopbit)
- *
- * calclate parity bit register mask from parity and stop bit
- */
- u_char parcalc(paritybit, stopbit)
- u_char paritybit, stopbit;
- {
- register u_char parity = 3;
-
- parity |= parityTable[paritybit].parity;
- cMask = parityTable[paritybit].mask;
- switch (stopbit) {
- case STOP_BIT_1: parity |= 0x40; break; /* 1 stop bit */
- case STOP_BIT_2: parity |= 0xc0; break; /* 2 stop bit */
- }
- return(parity);
- }
-
- /*
- ** void initPortDevice(baud, parity)
- *
- * iniz hardware baudrate and parity and assert RS/ER
- */
- void initPortDevice(baud, parity)
- u_char baud, parity;
- {
- u_char devider;
- u_short baudTimer;
-
- spl7();
- initPortBuffer(); /* iniz port buffer */
- if (CPUClock == 10)
- baudTimer = baudToTimer5Mhz10Mhz[baud-1];
- else
- baudTimer = baudToTimer8Mhz12Mhz[baud-1];
- devider = (baud >= 4) ? 0xfe : 0xff;
- /* x16 x32 */
-
- inpWait(portAddress+RS_DATA); /* null read data receive register */
- inpWait(portAddress+RS_DATA); /* null read data receive register */
- /*
- * set timer control word for serial 1
- * counter #2, load, mode = 3, not BCD
- */
- outpWait(TIMER_MODE, 0xb6);
- outpWait(TIMER_COUNTER2, baudTimer & 0xff);/* set counter #2 */
- outpWait(TIMER_COUNTER2, baudTimer / 256); /* set counter #2 */
-
- /*
- * 8251 command/status register
- * bit comment
- * ---- ------------
- * 7 EnterHunt
- * 6 Internal Reset
- * 5 RTS
- * 4 Error Reset
- * 3 Send Break
- * 2 Rx Enable
- * 1 DTR
- * 0 Tx Enable
- *
- * 8251 mode register
- * bit comment
- * ---- ------------
- * 76 stop bit
- * 5 even parity
- * 4 parity enable
- * 32 charactor length
- * 10 baud rate devider
- */
- /*
- * reset i8251;
- * command register := 0x22 -> 0x40 -> parity
- * While it is recommended to use 0xaa instead of 0x22 in manual,
- * 0xaa generates illegal output character.
- *
- */
- outpWait(portAddress+RS_STATUS, 0x22); /* RS on, ER on, but RxTx disable */
- outpWait(portAddress+RS_STATUS, 0x40); /* reset 8251 */
- outpWait(portAddress+RS_STATUS, parity & devider); /* set parity */
- ER_RS_On(); /* assert ER, RTS, RxEnable, TxEnable */
- /*
- * set interrupt mask register
- */
- /*
- * enable interrupt
- */
- outpWait(PORT_C, 1); /* enable RxRdy interrupt for serial 1 */
- outpWait(IMR, inpWait(IMR) & onMask);
- splx();
- }
-
- /*
- ** void sendBreak(shortLong)
- *
- * send break code to the remote system
- */
- void sendBreak(shortLong)
- int shortLong;
- {
- int i, outer;
-
- outpWait(portAddress+RS_STATUS, 0x3f); /* assert break */
- /*
- * we should wait for more one char when short break
- * NOTE: this code is really ABOUT!
- */
- if (shortLong == 1)
- outer = 2; /* short 2 msec */
- else
- outer = 100; /* long 100 msec */
- for (; outer > 0; --outer)
- for (i = 2000; i > 0; --i) /* 1msec counter */
- nullFunction();
- outpWait(portAddress+RS_STATUS, 0x37); /* negate break */
- }
-
- /*
- ** void ER_RS_On()
- *
- * assert ER/RS line
- */
- void ER_RS_On()
- {
- /* RS off (bit5), ER off (bit1), Rx/Tx disable(bit 2,0) */
- outpWait(portAddress+RS_STATUS, 0x37);
- }
-
- /*
- ** void ER_RS_Off()
- *
- * drop ER/RS line, disable input/output
- */
- void ER_RS_Off()
- {
- /* RS off (bit5), ER off (bit1), Rx/Tx disable(bit 2,0) */
- outpWait(portAddress+RS_STATUS, 0x00);
- }
-
- /*
- ** void getCPUClock()
- *
- * get PC9801 CPU clock int variable 'CPUClock'
- */
- void getCPUClock()
- {
- #ifdef PC98XA
- int c = peekMemory(0, CPU_CLOCK); /* get CPU clock */
- switch (c & 0xc0) {
- case 0: case 1:
- CPUClock = 10; /* 10Mhz, 5MHz... */
- case 2: case 3:
- CPUClock = 8; /* 8Mhz, 12Mhz... */
- }
- #else
- /* Thank you Nishimura@Giken */
- if (inp(PRINTER_PORT) & 0x20)
- CPUClock = 8;
- else
- CPUClock = 10;
- #endif
- }
- #endif /* PC98 */
-
-