/* Copyright (C) Magna Carta Software, Inc. 1990-1991. All Rights Reserved. Usage Rights: You are free to use this code as you wish, subject to any restrictions placed on its use by DigiBoard, Inc. If you modify any of this code, you are required to remove Magna Carta's copyright and any reference to Magna Carta Software, Inc. Technical Support: No technical support is available from Magna Carta Software, Inc., except to registered users of one or more versions of "C Communications Toolkit". Uploaded to the DigiBoard BBS by Magna Carta Software, Inc. DIGIXI0.C -- this file; DIGIXI0.H -- header for this file; COMPAT.H -- compiler compatibility macros; This code is from Magna Carta Software's 'C Communications Toolkit'. For information, call (214) 226-6909, fax (214) 226-0386, or write to: Magna Carta Software, Inc. P.O. Box 475594, Garland, TX 75047 USA. Contents: Low-level code for the DigiBoard DigiCHANNEL COM/Xi family of products (i.e. the COM/4i & COM/8i). This code is adapted from the DigiBoard, Inc. code in M232.DOC with the following changes: -- Supports more compilers (see list below); -- Reformated; -- Some bugs fixed (bit operators were used instead of logical operators); Usage: When using C Communications Toolkit, these routines are not called directly, but from DIGIXI1.C. This creates a device-independent interface that enables the user to switch, at runtime, from a standard serial port to a DigiCHANNEL COM/Xi card. When not using CCT, these functions are a straight replacement for the code in M232.DOC. Changes made by Magna Carta are preceded by a comment on the preceeding line that begins with the initials 'MC'. Compilers supported: Turbo C v2.0, TC++ v1.0, BC++ v1.0+; Microsoft C v5.1+, QC v2.0+; Mix Power C v2.0.0+; Watcom C v7.0+; Intel C Code Builder 386/486 v1.00+ */ #include #include #include #include #if defined(CCT) #include #else #define FAR_ far /* USING SOME VERSION OF MICROSOFT/QUICK C, OR WATCOM C */ #if (defined(_MSC_VER) || defined(__WATCOMC__)) /* The following macro creates a far pointer at a user-specified address Under MSC 5.0 ONLY, put this macro in comments and follow the instructions below to use the equivalent function. */ #if defined(_MSC_VER) #define MK_FP(seg,ofs) ((void far *)((((unsigned long)(seg)) << 16) | (ofs))) #if 0 /* MSC 5.0 ONLY! This function replaces the above macro due to a bug in the compiler. The function is in COMPAT.C and must be uncommented and compiled into the CWT library. */ void far *MK_FP(unsigned pseg, unsigned poff); #endif #endif /* The following 4 macros emulate BASIC's "peek" and "poke" routines. They are used internally. */ #define poke(a,b,c) (*((int far*)MK_FP((a),(b))) = (int)(c)) #define pokeb(a,b,c) (*((char far*)MK_FP((a),(b))) = (char)(c)) #define peek(a,b) (*((int far*)MK_FP((a),(b)))) #define peekb(a,b) (*((char far*)MK_FP((a),(b)))) #endif #endif /********************************************************** Error Display Routine for COM/Xi and M232 Functions **********************************************************/ /********************* Error Messages *********************/ static char undefmsg[] = "Undefined Error"; char *errmsg[] = { "Successful", /* [-0] */ "General Failure", /* [-1] */ "Time-out Error", /* [-2] */ "File Error", /* [-3] */ "FEP is Not 'WARM'", /* [-4] */ "Host to FEP Mailbox Busy", /* [-5] */ "FEP to Host Mailbox Busy", /* [-6] */ "Not Enough Memory", /* [-7] */ "FEP EPROM Version too low", /* [-8] */ "Command Queue Full" /* [-9] */ "Rx Buffer Empty", /* [-10] */ "Tx Buffer Full", /* [-11] */ "Rx has less than requested", /* [-12] */ "Not enough room in Tx Buffer", /* [-13] */ undefmsg /* [-14] */ }; WORD errtblsize = sizeof (errmsg) / sizeof (char **); /***************************** Special Error Messages *****************************/ char *xerrmsg[] = { "Framing, Parity, and Overrun Error", /* [-242] */ undefmsg, /* [-243] */ "Parity and Overrun Error", /* [-244] */ undefmsg, /* [-245] */ "Framing and Overrun Error", /* [-246] */ undefmsg, /* [-247] */ "Overrun Error", /* [-248] */ undefmsg, /* [-249] */ "Framing and Parity Error", /* [-250] */ undefmsg, /* [-251] */ "Parity Error", /* [-252] */ undefmsg, /* [-253] */ "Framing Error", /* [-254] */ undefmsg /* [-255] */ }; WORD xerrtblsize = sizeof (xerrmsg) / sizeof (char **); /***************************************** short FCM program to run on COM/Xi returns packed BCD version of EPROM for EPROM versions older than 3.0 at offset 0X0ffc Must be first program loaded on FEP Created by running BIN2C on version.fcm *****************************************/ BYTE version_FCM[] = { 0Xaa,0X55,0X01,0Xda,0Xfc,0X06,0X1e,0Xb8,0X00,0Xf0,0X8e,0Xd8, 0X33,0Xc0,0X8e,0Xc0,0Xbe,0Xfd,0Xff,0Xac,0X24,0X0f,0Xc0,0Xe0, 0X04,0X86,0Xe0,0X46,0Xac,0X24,0X0f,0X0a,0Xc4,0Xbf,0Xfc,0X0f, 0Xaa,0X1f,0X0e,0X07,0X33,0Xff,0X33,0Xc0,0Xb9,0X2c,0X00,0Xf3, 0Xaa,0X07,0Xcb,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00 } ; WORD verFCMsize = sizeof (version_FCM); /* RESETCOMXI -- resets selected board waits for 'WARM' Return value: SUCCESS (0) -- OK; TMOERROR (-2) -- Timeout: Reset failed within loop time; */ WORD resetcomxi(WORD port, WORD seg) { WORD a, b, c; outp(port, RESET_XI); /* reset card */ for (a = 0; a <= 0XFFF; a++) { /* wait for propagation */ if ((inp(port) & RESET_XI) == RESET_XI) break; /* exit loop if reset looped back */ if (a == 0XFFF) return (TMOERROR); } outp(port, CLR_I_R); /* clear reset */ b = (inp(port) & 0X0F); /* wait for Post to complete */ while (b != 0X0F) { /* PostTest Complete ? */ b = (inp(port) & 0X0F); a = 0; if (b == 0X0F) break; /* exit loop if complete */ while (b == (inp(port) & 0X0F)) { /* wait for each step of Post */ a++; for (c = 0; c != 0XFFFF; c++) { /* inner loop for each test delay */ if (b != (inp(port) & 0X0F)) break; /* exit loop on change of Post test */ if (b == 0X0F) break; /* or Post complete */ } if (a == 0XFF) return (TMOERROR); /* did sub test fail ? */ } } for (a = 0; a <= 0XFF; a++) { /* wait a little for 'WARM' */ /* MC -- The line below is not compatible with MSC V5.1 */ /* if ((peek(seg, 0X0FF8) == 'WA') & (peek(seg, 0XFFA) == 'RM')) */ if ((peek(seg, 0X0FF8) == (('A'<< 8) | 'W')) && (peek(seg, 0XFFA) == (('M' << 8) | 'R'))) return (SUCCESS); } return (TMOERROR); } /* EXCFCM -- start execution of a loaded module on selected board Return value: SUCCESS (0) -- OK; BUSYHML (-5) -- FEP busy (0h not written to "mailbox"); */ WORD excfcm(WORD port, WORD seg) { if (peekb(seg, 0X0420) != 0X00) /* is mailbox empty ? */ return (BUSYHML); /* return error if board busy */ pokeb(seg, 0X0420, EXCBOOT); /* stuff command */ outp(port, CLR_I_R); outp(port, INT_XI); /* generate rising edge */ return (SUCCESS); /* return success */ /* let caller check if it worked */ /* since we don't know how long to wait */ /* or if module will respond it's loaded */ } /* EXCM232 -- start execution of EPROM M232 with requested buffer size on selected board Return value: SUCCESS (0) -- OK; NOTWARM (-4) -- Module not loaded; BADVERSN (-8) -- ? BUSYHML (-5) -- FEP busy (0h not written to "mailbox"); TMOERROR (-2) -- Module did not start executing; */ WORD excM232(WORD port, WORD seg, int size) { WORD a; if (peek(seg, 0X0FF0) != 0X55AA) return (NOTWARM); /* has back door been used ? */ /* MC -- The line below is not compatible with MSC V5.1 */ /* if (peek(seg, 0X0FF8) != 'WA') return (NOTWARM);*/ /* is it still warm ? */ if (peek(seg, 0X0FF8) != (('A' << 8) | 'W')) return (NOTWARM); /* is it still warm ? */ /* MC -- The line below is not compatible with MSC V5.1 */ /* if (peek(seg, 0X0FFA) != 'RM') return (NOTWARM); */ if (peek(seg, 0X0FFA) != (('M' << 8) | 'R')) return (NOTWARM); if (peek(seg, 0X041A) != 0X0100) return (NOTWARM); /* has a module been loaded ? */ if (peekb(seg, 0X0FFC) < '0') return (BADVERSN); /* does EPROM contain M232 ? */ if (peek(seg, 0X0420) != 0X00) return (BUSYHML); /* ok to send a command ? */ pokeb(seg, 0X0420, EXCM232); /* stuff command */ pokeb(seg, 0X0421, size); /* stuff buffer size select */ outp(port, CLR_I_R); outp(port, INT_XI); /* generate rising edge */ for (a = 0; a <= 0XFFFF; a++) { /* wait for acknowledge */ /* MC -- The line below is not compatible with MSC V5.1 */ /* if ((peek(seg, 0X1000) == 'OK') & (peekb(seg, 0X0420) == 0X00)) */ if ((peek(seg, 0X1000) == (('K' << 8) | 'O')) & (peekb(seg, 0X0420) == 0X00)) return (SUCCESS); } return (TMOERROR); } /* GWINDOW -- returns current mapping of movable window on selected board Return value: The number (0 - 15) of the window currently mapped (in the 16k memory accessable from the host); */ WORD gwindow(WORD port) { return (inp(port) & 0X0F); /* return window bits only (0-3) */ } /* SWINDOW -- invoke remapping of movable window on selected board. I.e. Select the window (0 - 15) accessable by the host; This is an interrupt-invoked command. Return vlaue: SUCCESS (0) -- OK; BUSYHML (-5) -- FEP busy (0h not written to "mailbox"); TMOERROR (-2) -- Time out. Window did not get mapped; */ WORD swindow(WORD port, WORD seg, int window) { WORD a; /* IS IT ALREADY SET ? IF SO, DON'T BOTHER CHANGING */ if ((inp(port) & 0X0F) == (window & 0X0F)) return (SUCCESS); /* IS MAILBOX EMPTY ? */ if (peekb(seg, 0X0420) != 0X00) return (BUSYHML); pokeb(seg, 0X420, SETWIND); /* stuff command */ pokeb(seg, 0X421, (window & 0X0F)); /* and window wanted */ outp(port, CLR_I_R); outp(port, INT_XI); /* generate rising edge */ a = 0; /* WAIT FOR WINDOW CHANGE */ while ((inp(port) & 0X0F) != (window & 0X0F)) { a++; if (a == 0XFFFF) return (TMOERROR); /* waited long enough ? */ } return (SUCCESS); } /* GWARMVER -- get EPROM version of board. Return value: >0 -- OK (packed BCD); NOTWARM (-4) -- Module not loaded; BUSYHML (-5) -- FEP busy (0h not written to "mailbox"); TMOERROR (-2) -- Module did not return a value; */ WORD gWARMver(WORD port, WORD seg) { WORD a; /* BITWISE 'AND' SHOULD BE A LOGICAL 'AND' */ /* MC -- The line below is not compatible with MSC V5.1 */ /* if ((peek(seg, WARMMSG) == 'WA') & (peek(seg, WARMMSG + 2) == 'RM')) { */ if ((peek(seg, WARMMSG) == (('W' << 8) | 'A')) & (peek(seg, WARMMSG + 2) == (('R' << 8) | 'M'))) { /* Does EPROM contain BCD Version ? Yes, return packed BCD version */ if (peekb(seg, EPROMVR) >= '0') return (peekb(seg, EPROMVR)); } else return (NOTWARM); /* not warm so return error */ /* HAS BACK DOOR BEEN USED ? */ if (peek(seg, 0X0FF0) != 0X55AA) return (NOTWARM); /* HAS A MODULE BEEN LOADED ? */ if (peek(seg, NEXTFCM) != 0X0100) return (NOTWARM); /* DOES EPROM CONTAIN BCD VERSION ? YES, RETURN PACKED BCD VERSION */ if (peekb(seg, EPROMVR) >= '0') return (peekb(seg, 0X0FFC)); /* OK TO SEND A COMMAND ? */ if (peek(seg, CMDMAIL) != 0X00) return (BUSYHML); for (a = 0; a <= verFCMsize; a++) { /* transfer FCM program */ pokeb(seg, (0X1000 + a), version_FCM[a]); } pokeb(seg, CMDMAIL, EXCBOOT); outp(port, CLR_I_R); outp(port, INT_XI); /* generate rising edge */ for (a = 0; a <= 0XFFFF; a++) { /* wait for acknowledge */ if ((peek(seg, 0X1000) == 0) & (peekb(seg, 0X0420) == 0X00)) return (peekb(seg, EPROMVR)); /* return packed BCD version */ } return (TMOERROR); } /* LOADFCM -- load fcm file to selected COM/Xi. Return value: SUCCESS (0) -- OK; GENERROR (-1) -- Time out. Window did not get mapped; TMOERROR (-2) -- Time out. Window did not get mapped; FILERROR (-3) -- Error opening file; NOMEMLFT (-7) -- Program too big for available memory; */ WORD loadfcm(WORD port, WORD seg, char *fl) { WORD off, maxo, win, maxw; short status, ch; FILE *fp; maxo = ((peek(seg, MEMSIZE) & 0X01ff) << 7); /* start of invalid memory */ maxw = ((peek(seg, MEMSIZE) & 0X01f0) >> 4); /* first invalid window */ off = ((peek(seg, NEXTFCM) & 0X03c0) << 4); /* starting offset */ win = ((peek(seg, NEXTFCM) & 0X3c00) >> 6); /* starting window */ #if !defined(COMM_H_INCLUDED) fp = fopen(fl, "rb"); /* open binary file for reading */ #else ret = cct_open_(x, x->xf->fspec, "rb"); #endif if (fp == 0) return (FILERROR); /* did it open OK ? */ /* START IN LOWER WINDOW ? */ if (win == 0) while (off < 0X4000) { /* for rest of lower window */ if ((ch = getc(fp)) == EOF) return (SUCCESS); /* End of File ? */ pokeb(seg, off, ch); off++; if ((win == maxw - 1) & (off == maxo)) return(NOMEMLFT); } if (win == 0) win++; /* next window if start was lower */ if (win == maxw) return (NOMEMLFT); /* we ran out of memory */ status = swindow(port, seg, win); /* select the right window */ if (status != SUCCESS) { if (status != BUSYHML) return(TMOERROR); else { status = 0; while (swindow(port, seg, win) != SUCCESS) { /* retry window swap */ status++; if (status >= 4) return (GENERROR); /* exit after 5 retries */ } } } off = (off | 0X4000); /* make sure we use upper window */ while ((ch = getc(fp)) != EOF) { /* continue for the rest of the file */ pokeb(seg, off, ch); off++; if ((win == maxw - 1) & (off == maxo)) return(NOMEMLFT); if (off == 0X8000) { /* end of this window ? */ off = 0X4000; /* restart at start of window */ win++; /* next window */ if (win == maxw) return (NOMEMLFT); status = swindow(port, seg, win); if (status != SUCCESS) { if (status != BUSYHML) return(TMOERROR); else { status = 0; while (swindow(port, seg, win) != SUCCESS) { status++; if (status >= 4) return (GENERROR); } } } } } return (SUCCESS); } /* WRTBCMD -- write four byte command string to selected board's command queue. Update the command queue pointers. Return value: SUCCESS (0) -- OK; CMDQFUL (-9) -- Command buffer full; */ WORD wrtbcmd(WORD seg, WORD cmd, WORD chn, WORD p1, WORD p2) { WORD a; a = ctbl(seg)->CMDHD; /* get pointer for next command */ pokeb(seg, a, cmd); /* transfer command */ a++; pokeb(seg, a, chn); /* and parameters */ a++; pokeb(seg, a, p1); /* to command queue */ a++; pokeb(seg, a, p2); a++; /* need to wrap pointer ? wrap pointer */ if (a > ctbl(seg)->CMDMAX) a = ctbl(seg)->CMDSTRT; if (a != ctbl(seg)->CMDTL) { /* is queue full ? */ ctbl(seg)->CMDHD = a; /* save new pointer */ return (SUCCESS); } else return (CMDQFUL); /* queue is full don't adjust pointer */ } /* WRTWCMD -- writes two byte + one word command string to selected board's command queue. Return value: SUCCESS (0) -- OK; CMDQFUL (-9) -- Command buffer full; */ WORD wrtwcmd(WORD seg, char cmd, char chn, WORD p1) { WORD a; a = ctbl(seg)->CMDHD; /* get pointer for next command */ pokeb(seg, a, cmd); /* transfer command */ a++; pokeb(seg, a, chn); /* and parameters */ a++; poke(seg, a, p1); /* to command queue */ a++; a++; /* need to wrap pointer ? wrap pointer */ if (a > ctbl(seg)->CMDMAX) a = ctbl(seg)->CMDSTRT; if (a != ctbl(seg)->CMDTL) { /* is queue full ? */ ctbl(seg)->CMDHD = a; /* save new pointer */ return (SUCCESS); } else return (CMDQFUL); /* queue is full don't adjust pointer */ } /* GRXCNT -- return number of characters in selected Rx buffer of selected board. */ WORD grxcnt(WORD seg, char chn) { register WORD h; h = btbl(seg)[chn].RXHD; if (h != btbl(seg)[chn].RXTL) { /* is buffer empty ? */ /* POSSIBLE WRAP IN POINTERS ? */ if (h > btbl(seg)[chn].RXTL) return ((h - btbl(seg)[chn].RXTL)/2); /* YES FIGURE OUT FOR WRAP */ else return (((btbl(seg)[chn].RXMAX - btbl(seg)[chn].RXTL) + (h - btbl(seg)[chn].RXSTRT))/2); } else return (0); /* return empty count */ } /* GTXFREE -- return free space in selected Tx buffer of selected board */ WORD gtxfree(WORD seg, char chn) { register WORD h; h = btbl(seg)[chn].TXTL; if (h != btbl(seg)[chn].TXHD) { /* anything in buffer ? */ if (h < btbl(seg)[chn].TXHD) /* need to figure for wrap ? */ return ((btbl(seg)[chn].TXMAX - btbl(seg)[chn].TXSTRT) - (btbl(seg)[chn].TXHD - h)); else return ((h - btbl(seg)[chn].TXHD) - 1); /* No return space between */ } else return (btbl(seg)[chn].TXMAX - btbl(seg)[chn].TXSTRT); /* return buffer size -1 */ } /* RDCH -- return character from selected Rx buffer Return value: GENERROR (-1) -- Time out. Window did not get mapped; RXEMPTY (-10) -- Receiver empty (nothing received); */ WORD rdch(WORD port, WORD seg, WORD chn) { WORD a, b; a = btbl(seg)[chn].BWIND; if (a != (inp(port) & 0X0f)) { if (swindow(port, seg, a)) return (GENERROR); } a = btbl(seg)[chn].RXTL; if (a == btbl(seg)[chn].RXHD) return (RXEMPTY); b = peek(seg, a); a++; a++; if (a > btbl(seg)[chn].RXMAX) a = btbl(seg)[chn].RXSTRT; btbl(seg)[chn].RXTL = a; return (b); } /* RDCHST -- return character and its status from selected Rx buffer Return value: GENERROR (-1) -- Time out. Window did not get mapped; RXEMPTY (-10) -- Receiver empty (nothing received); */ WORD rdchst(WORD port, WORD seg, char chn) { WORD a, b; a = btbl(seg)[chn].BWIND; if (a != (inp(port) & 0X0f)) { if (swindow(port, seg, a)) return (GENERROR); } a = btbl(seg)[chn].RXTL; if (a == btbl(seg)[chn].RXHD) return (RXEMPTY); b = peek(seg, a); a++; a++; if (a > btbl(seg)[chn].RXMAX) a = btbl(seg)[chn].RXSTRT; btbl(seg)[chn].RXTL = a; return (b); } /* RDSTR -- Read a string from the selected RX buffer. Return value: GENERROR (-1) -- Time out. Window did not get mapped; TMOERROR (-2) -- Time out. Window did not get mapped; BUSYHML (-5) -- FEP busy (0h not written to "mailbox"); RXEMPTY (-10) -- Receiver empty (nothing received); */ WORD rdstr(WORD port, WORD seg, char chn, char *s, int len) { WORD a, b, c; a = btbl(seg)[chn].RXTL; /* any thing to read ? */ if (a == btbl(seg)[chn].RXHD) return (RXEMPTY); /* No return empty */ b = btbl(seg)[chn].BWIND; if (b != (inp(port) & 0X0f)) /* is right window selected ? */ if ((c = swindow(port, seg, b)) != SUCCESS) /* try to select it */ return (c); /* return why if error */ b = 0; while (b != len) { /* for number of requested */ if (a == btbl(seg)[chn].RXHD) break; /* did buffer empty ? */ s[b] = peekb(seg, a); /* do transfer */ a++; /* advance pointer */ a++; /* skip status */ b++; /* advance count */ /* need to wrap pointer ? Yes wrap pointer */ if (a > btbl(seg)[chn].RXMAX) a = btbl(seg)[chn].RXSTRT; } btbl(seg)[chn].RXTL = a; /* did we empty it ? flag it empty */ if (a == btbl(seg)[chn].RXHD) btbl(seg)[chn].HFLSH = (BYTE) -1; s[b + 1] = '\0'; return (b); } /* RDSTRST -- Read a string from the selected RX buffer. Return value: GENERROR (-1) -- Time out. Window did not get mapped; TMOERROR (-2) -- Time out. Window did not get mapped; BUSYHML (-5) -- FEP busy (0h not written to "mailbox"); RXEMPTY (-10) -- Receiver empty (nothing received); */ WORD rdstrst(WORD port, WORD seg, char chn, short *s, int len) { WORD a, b, c; a = btbl(seg)[chn].RXTL; /* any thing to read ? */ if (a == btbl(seg)[chn].RXHD) return (RXEMPTY); /* No return empty */ b = btbl(seg)[chn].BWIND; if (b != (inp(port) & 0X0f)) /* is right window selected ? */ /* try to select it return why if error */ if ((c = swindow(port, seg, b)) != SUCCESS) return (c); b = 0; while (b != len) { /* for number of requested */ if (a == btbl(seg)[chn].RXHD) break; /* did buffer empty ? */ s[b] = peek(seg, a); /* do transfer */ a++; /* advance pointer */ a++; /* skip status */ b++; /* advance count */ /* need to wrap pointer ? Yes wrap pointer */ if (a > btbl(seg)[chn].RXMAX) a = btbl(seg)[chn].RXSTRT; } btbl(seg)[chn].RXTL = a; /* did we empty it ? flag it empty */ if (a == btbl(seg)[chn].RXHD) btbl(seg)[chn].HFLSH = (BYTE) -1; s[b + 1] = 0; return (b); } /* WRTCH -- writes a character in selected channel's buffer of selected board Return value: SUCCESS (0) -- OK; GENERROR (-1) -- Could not map designated window; TXTOFUL (-13) -- TX buffer already full (refuse new data); */ WORD wrtch(WORD port, WORD seg, WORD chn, WORD c) { WORD a; a = btbl(seg)[chn].BWIND; if (a != (inp(port) & 0X0f)) { if (swindow(port, seg, a)) return (GENERROR); } a = btbl(seg)[chn].TXHD; pokeb(seg, a, c); a++; if (a > btbl(seg)[chn].TXMAX) a = btbl(seg)[chn].TXSTRT; if (a != btbl(seg)[chn].TXTL) { btbl(seg)[chn].TXHD = a; return (SUCCESS); } else return (TXTOFUL); } /* WRTSTR -- write string to selected channel of selected board Return value: SUCCESS (0) -- OK; GENERROR (-1) -- Could not map designated window; TXTOFUL (-13) -- TX buffer already full (refuse new data); */ WORD wrtstr(WORD port, WORD seg, char chn, char *s, int len) { WORD a, b; a = btbl(seg)[chn].BWIND; if (a != (inp(port) & 0X0f)) if (swindow(port, seg, a)) return (GENERROR); b = 0; a = btbl(seg)[chn].TXHD; while (b < len) { pokeb(seg, a, s[b]); a++; b++; if (a > btbl(seg)[chn].TXMAX) a = btbl(seg)[chn].TXSTRT; if (a == btbl(seg)[chn].TXTL) return (TXTOFUL); } btbl(seg)[chn].TXHD = a; return (SUCCESS); } #if (!defined(CTC) && !defined(CTF)) /* PRINTERR -- display reason for error by indexing on error code */ void printerr(WORD errorcode) { if ((errorcode < 0) | (errorcode > 32767)) /* is it positive ? */ errorcode = ~errorcode + 1; /* make it positive */ if (errorcode >= errtblsize) { /* is it in normal table ? */ if ((errorcode >= 242) & (errorcode <= 255)) printf("%s", xerrmsg[errorcode]); /* use special table */ else printf("Undefined Error Code %d", errorcode); } else printf("%s", errmsg[errorcode]); /* use normal table */ } #endif