/* Copyright (C) Magna Carta Software, Inc. 1988-1990. All Rights Reserved. Z80SIO04.C -- BISYNC transmission using the Z-80 SIO CAPABILITY: Dumb terminal program. Message transmission. If XDEBUG=1 diagnostic are written directly to the color monitor (change SCREEN to 0XB0000000 if using a mono. mode). COMPATIBILITY: TURBOC v1.5+, POWERC 1.2+, MSC v5.0+/QUICK C, WATCOM C. */ #include #include #include #include #include #include #include #include /* MANIFEST CONSTANTS AND MACROS */ #define MENU ALT_M /* key for command summary */ #define AST 0X300 /* base address of AST CC-232 */ /* BISYNC SPECIFICS */ #define EBCDIC 1 #define RESPONSE_TIMEOUT 1 /* max. wait for reply (ms.) */ #define MAX_BLOCKLEN 2 /* max attempts for reply */ #define MAX_TRIES 3 /* max attempts for reply */ #define HEADER 4 /* use headers on front of msg. */ /* TYPE DEFINITIONS */ typedef struct bisync { WORD mode; /* 0 = non-transparent, or TRANPARENT */ WORD max_blocklen; /* maximum length of a block */ WORD max_tries; /* max. attempts to try requests */ WORD t_tx; /* timeout for non-receipt of SYNC (1 secs) */ WORD t_rx; /* timeout for response to message (3 secs) */ WORD t_disconnect; /* timeout for response to message (20 secs) */ WORD t_continue; /* timeout for a TTD (2 secs) */ WORD f_hdr; /* use headers at front of msgs. */ WORD padchar; /* padding character */ WORD syn; /* SYN character */ WORD eot; /* EOT character */ WORD enq; /* ENQ character */ WORD etb; /* ETB character */ char ack0[2]; /* ACK0 string sequence ASCII: DLE 0 */ char ack1[2]; /* ACK1 string sequence ASCII: DLE 1 */ char wack[2]; /* Wait before TX affirmative ack. ASCII: DLE ; */ char rvi[2]; /* Reverse interrupt ASCII: DLE */ char ttd[2]; /* Temporay text delay ASCII: STX ENQ */ } BISYNC; /* GLOBAL VARIABLES */ BYTE ztxbuf[2048]; /* TRANSMIT BUFFER FOR Z-80 SIO */ BYTE zrxbuf[2048]; /* RECEIVE BUFFER FOR Z-80 SIO */ int vers = 4; BISYNC * b; WORD f_crc; extern short etoa1[], atoe1[]; /* FUNCTION PROTOTYPES */ void bisync_ack(COMM_PORT *p, WORD num); short bisync_send_msg(COMM_PORT *p, BISYNC *b, const char *hdr, const char *s); short ctl_poll(COMM_PORT *p, short cu, short du, char *msg); void do_data_menu(COMM_PORT * p_port); long do_speed_menu(COMM_PORT * p_port); short menu(COMM_PORT *p); void term(COMM_PORT *p); void INTERRUPT_ uz80_bisync_txint(void); BISYNC * init_bisync(WORD cset); short deinit_bisync(BISYNC *b); #include "menus.c" #include "sioport.c" COMM_PORT * sio0 = &z80; /* ------------------------ PROGRAM BEGINS HERE --------------------------- */ int main(void) { z80.uart.uz80sio = uz80; bios_ed(7, 0); /* Z-80 SIO INITIALIZATION */ u2.uz80sio = uz80; if (c_open(&z80) == PORT_ALREADY_OPEN) { fprintf(stderr, "\aError: Cannot open port1"); mspause(1000); return (EOF); } uz80_set_reg(&z80, WR0, UZ80_TX_CRC_RESET); uz80_set_reg(&z80, WR2, 0); /* interrupt vector */ /* uz80_mod_reg(&z80, WR3, UZ80_AUTO_ENABLE, UZ80_AUTO_ENABLE); */ uz80_mod_reg(&z80, WR3, UZ80_SYNC_INHIBIT, UZ80_SYNC_INHIBIT); uz80_mod_reg(&z80, WR4, UZ80_STOPBITS_MASK, UZ80_STOPBITS0); uz80_mod_reg(&z80, WR4, UZ80_SYNC_MASK, UZ80_BISYNC); uz80_mod_reg(&z80, WR4, UZ80_CLOCK_MASK, X1); uz80_mod_reg(&z80, WR3, UZ80_RX_DATABITS_MASK, UZ80_RX_DATA8); uz80_set_reg(&z80, WR6, 0X32); uz80_set_reg(&z80, WR7, 0X32); /* STATUS EFFECTS VECTOR, EXTERNAL INT. ENABLE, TX INT. ENABLE */ uz80_mod_reg(&z80, WR1, 0X7, 7); uz80_mod_reg(&z80, WR5, UZ80_CRC16_MASK, UZ80_CRC16_MASK); uz80_mod_reg(&z80, WR5, UZ80_TX_ENABLE, UZ80_TX_ENABLE); /* INSTALL THE INTERRUPT HANDLERS */ uz80_install_ipr(&z80, UZ80_RX_ALL, NULL, zrxbuf, sizeof(zrxbuf)); uz80_install_ipr(&z80, UZ80_TX_ALL, NULL, ztxbuf, sizeof(ztxbuf)); uz80_install_ipr(&z80, UZ80_ES, NULL, NULL, 0); install_isr(&z80, 2, (void far *) uz80_bisync_txint); b = init_bisync(EBCDIC); printf("CCT Z-80 SIO Version %d: Press Alt-M for a list of commands\n", vers); term(&z80); printf("\nEnd of CCT Z-80 SIO%d\n", vers); set_rts(&z80, LOW); set_dtr(&z80, LOW); uz80_mod_reg(&z80, WR5, UZ80_TX_ENABLE, 0); deinit_bisync(b); deinit_port(&z80); return (0); } /* TERM -- The terminal emulation routine. Simply polls the COM port and the keyboard alternately for characters. */ void term(COMM_PORT *p_port) { short c, ret; /* must be signed to detect a -1 return */ /* char msg[] = "\x40\x40\x7f\x7f\x2D"; */ char response[180]; /* THESE LINES CONTROL THE RECEIVED DATA FORMAT */ set_rx_xlat(p_port, XLAT, TRUE); set_rx_xlat(p_port, EOL, FALSE); set_rx_xlat(p_port, LOCAL_ECHO, ON); p_port->a_rx_xlat = &etoa1[0]; /* THESE LINES CONTROL THE TRANSMITTED DATA FORMAT */ set_tx_xlat(p_port, XLAT, TRUE); for (;;) { /* CHECK SERIAL PORT FOR BYTE */ #if 0 if (ret < 0) while ((c = c_getxc(p_port)) != EOF) { if (c == 0XFF) continue; cprintf("%c", c); } #endif /* CHECK KEYBOARD FOR A KEY PRESS */ if ( (c = inkey()) != EOF) { if (c == MENU) { if (menu(p_port) == 1) break; } else c_putc(p_port, c); } mspause(500); ret = ctl_poll(p_port, 0X40, 0X7F, response); printf("Ret=%d\n", ret); #if 0 c_putn(p_port, strlen(msg), (char huge *) msg); #endif } } /* CTL_POLL -- Poll the Tranzit controller. */ short ctl_poll(COMM_PORT *p, short cu, short du, char *msg) { char s[9]; short ch, i, ret = 0; for (i=0; i < 3; i++) s[i] = (char) b->syn; s[3] = (char) cu; s[4] = (char) cu; s[5] = (char) du; s[6] = (char) du; s[7] = '\x2D'; s[8] = '\x0'; c_putn(p, 8L, (char huge *) s); ch = c_waitc(p, EOF, 2000); if ((WORD) ch == b->eot) (WORD) ret = b->eot; if (ch == EOF) ret = EOF; if (!ret) { if (ch == STX) { ch = c_waitc(p, EOF, 1000); if ((WORD) ch == b->enq) { ret = EOF; } if (!ret) { *msg++ = (char) ch; while (TRUE) { ch = c_waitc(p, EOF, 1000); if (ch == EOF) break; else *msg++ = (char) ch; } *msg++ = '\0'; if (f_crc) ret = -3; else bisync_ack(p, 0); } } else ret = -2; } return (ret); } void bisync_ack(COMM_PORT *p, WORD num) { char s[9]; short i; for (i=0; i < 3; i++) s[i] = (char) b->syn; s[3] = (char) DLE; s[4] = '\0'; if (!num) strcat(s, b->ack0); else strcat(s, b->ack1); c_putn(p, 7L, (char huge *) s); } /* BISYNC_SEND_MSG -- Send a BISYNC message. This function may be used by either the CONTROL STATION or the TRIBUTARY. It The message is: a) STX b) text c) ETX The CRC is sent automatically by the interrupt handler. Return value: 0 -- message sent and acknowledged OK; EOF -- NAK after maximum number of tries; */ short bisync_send_msg(COMM_PORT *p, BISYNC *b, const char *hdr, const char *s) { short ret = 0; WORD i = 0; do { c_putc(p, STX); ret = (short) c_putn(p, (unsigned long) strlen(s), (char huge *) s); c_putc(p, ETX); ret = c_waits(p, b->ack0, b->t_rx); i++; #if 1 printf("\nret=%d", ret); #endif } while (ret != 0 && i < b->max_tries); return (ret); } BISYNC *init_bisync(WORD cset) { BISYNC *b; b = (BISYNC *) calloc(1, sizeof(struct bisync)); if (b != NULL) { if (cset == ASCII) { strcpy(b->ack0, "\x10\x30"); /* DLE 0 */ strcpy(b->ack1, "\x10\x31"); /* DLE 1 */ strcpy(b->wack, "\x10\x3B"); /* DLE ; */ strcpy(b->rvi, "\x10\x3C"); /* DLE < */ strcpy(b->ttd, "\x2\x5"); /* STX ENQ */ b->syn = SYN; b->enq = ENQ; b->eot = EOT; b->etb = ETB; } else if (cset == EBCDIC) { strcpy(b->ack0, "\x10\x70"); /* DLE 0 */ strcpy(b->ack1, "\x10\x61"); /* DLE 1 */ strcpy(b->wack, "\x10\x6B"); /* DLE ; */ strcpy(b->rvi, "\x10\x7C"); /* DLE @ */ strcpy(b->ttd, "\x2\x5"); /* STX ENQ */ b->syn = 0X32; b->enq = 0X2D; b->eot = 0X37; b->etb = 0X26; } else { free(b); b = NULL; } } b->max_blocklen = 254; b->f_hdr = FALSE; b->max_tries = 3; return (b); } short deinit_bisync(BISYNC *b) { free(b); return (0); } short set_bysync(BISYNC *b, short item, short value) { short ret = 0; switch(item) { case HEADER: /* send header at start of msg. */ b->f_hdr = value; break; case MAX_BLOCKLEN: b->max_blocklen = value; /* max. block length in msg. */ break; case MAX_TRIES: b->max_tries = value; /* max. tries of message */ break; case RESPONSE_TIMEOUT: b->t_rx = value; /* wait for msg. response (ms.) */ break; default: ret = EOF; } return (ret); } #define DATA_FORMAT 'D' /* key to set speed */ #define DATA_SPEED 'S' /* key to set speed */ #define EXIT 'Q' /* key to exit from main */ #define SEND_STR 'T' short menu(COMM_PORT *p_port) { short c, retval = 0, DONE = FALSE; static char *menus[] = { "\tD. Data Format", "\tS. Data Transfer Rate.", "\tT. Transmit a string", "\tQ. EXIT from COMM.", NULL }; char **p_menu; c = !EXIT; while (!DONE) { puts("\n\n"); for (p_menu = menus; *p_menu != NULL; p_menu++) printf("%s\n", *p_menu); printf("\n\t\t Enter selection (CR to quit menu) : "); if ( (c = getch()) == CR) break; /* return to term */ c = toupper(c); switch (c) { case EXIT: retval = 1; DONE = TRUE; break; case DATA_FORMAT: do_data_menu(p_port); break; case DATA_SPEED: do_speed_menu(p_port); break; case SEND_STR: bisync_send_msg(p_port, b, NULL, "\x40\x40\x7F\x7F\x2D"); DONE = TRUE; break; default: puts("Invalid choice\n\007"); DONE = TRUE; break; } } puts("\nExiting menu"); return (retval); /* will be zero except if EXIT */ } #if XDEBUG #define SCREEN ((WORD far *) 0XB8000800L) WORD far * s = SCREEN; WORD scol = 1; #endif /* UZ80_BYSYNC_INT -- Interrupt handler for the Z-80 SIO/DART to perform BISYNC. Enables RX, TX, ES interrupts For use under the IBM architecture. */ void INTERRUPT_ uz80_bisync_txint(void) { static WORD ch, f_eom; static BYTE c, rx_lastchar, tx_lastchar, rr1; static volatile BYTE far *b_ptr; outp(z80.ubase_addr, 2); /* toggle the reg. ptr. to RR2 */ c = (BYTE) (inp(z80.ubase_addr) & 0XE); /* TX BUFFER EMPTY */ if (c == 0) { if (z80.txbuftail == z80.txbufhead) { z80.f_txbusy = FALSE; /* IF END OF MESSAGE. RESET EOM LATCH IN ORDER TO SEND CRC */ if (tx_lastchar == ETX || tx_lastchar == ETB || tx_lastchar == US) { uz80_set_reg(&z80, WR0, UZ80_TX_EOM_RESET); uz80_mod_reg(&z80, WR3, UZ80_HUNT_ENTER, UZ80_HUNT_ENTER); } /* Nothing to send so reset TX interrupt pending */ uz80_set_reg(&z80, WR0, UZ80_TX_RESET); } else { /* IF START OF MESSAGE, INCLUDE IN CRC CALCULATION */ if (tx_lastchar == STX || tx_lastchar == SOH) uz80_mod_reg(&z80, WR5, UZ80_TX_CRC_ENABLE, UZ80_TX_CRC_ENABLE); outp(z80.udata_reg_addr, *z80.txbuftail); /* send byte */ tx_lastchar = *z80.txbuftail++; if (z80.txbuftail > z80.txbufend) z80.txbuftail = z80.txbuf; #if XDEBUG *s++ = (0XF << 8) | '.'; #endif } } /* EXTERNAL STATUS CHANGE (DCD, CTS, or SYNC) */ if (c == 2) { ch = inp(z80.ubase_addr); /* read RR0 */ /* TX UNDERRUN */ if (ch & 0X40) { #if XDEBUG *s++ = (scol << 8) | 't'; #endif } #if XDEBUG /* SYNC/HUNT TRANSITION */ if (ch & 0X10) *s++ = (scol << 8) | 'h'; /* HUNT */ else { *s++ = (scol << 8) | 's'; /* SYNC */ /* *s++ = (++scol << 8) | ch; */ } #endif /* CTS TRANSITION */ if (!(ch & 0X20)) { /* z80.f_txbusy |= */ } uz80_set_reg(&z80, WR0, UZ80_ES_RESET); } /* RX CHARACTER AVAILABLE */ if (c == 4) { if (inp(z80.ubase_addr) & 1) { /* IF a char is ready get it */ b_ptr = z80.rxbufhead + 1; if (b_ptr > z80.rxbufend) b_ptr = z80.rxbuf; if (!(b_ptr == z80.rxbuftail)) { /* buffer is full */ if (f_eom) { uz80_mod_reg(&z80, WR3, UZ80_RX_CRC_ENABLE, 0); f_eom = FALSE; } ch = (BYTE) inp(z80.udata_reg_addr); /* START OF MSG. -- ENABLE RX CRC */ if (ch == SOH || ch == STX) { uz80_mod_reg(&z80, WR3, UZ80_RX_CRC_ENABLE, UZ80_RX_CRC_ENABLE); } else if (ch == b->etb || ch == ETX) { /* END OF MSG./BLOCK -- TURN OFF CRC ACCUMUILATOR */ outp(z80.ubase_addr, 1); /* toggle the reg. ptr. to RR1 */ if (inp(z80.ubase_addr) & 0X40) f_crc = TRUE; f_eom = TRUE; } else if (ch != 0XFF) { *z80.rxbufhead = (BYTE) ch; z80.rxbufhead = b_ptr; } } } } /* SPECIAL RECEIVE CONDITION (Parity error, RX Overrrun, CRC Error */ else if (c == 6) { outp(z80.ubase_addr, 1); /* read register 1 for int type */ rr1 = (BYTE) (inp(z80.ubase_addr) & 0XF0); if (rr1 & 0X10) { /* parity error */ #if XDEBUG *s++ = (0XF << 8) | 'p'; #endif uz80_set_reg(&z80, WR0, UZ80_ER_RESET); } if (rr1 & 0X20) { /* receiver overrun error */ #if XDEBUG *s++ = (0XF << 8) | 'o'; #endif uz80_set_reg(&z80, WR0, UZ80_ER_RESET); if (inp(AST+3) & 1) inp(AST+3); /* IF a char is ready get it */ } else if (rr1 & 0X40) { /* receiver framing error */ #if XDEBUG *s++ = (0XF << 8) | 'f'; #endif uz80_set_reg(&z80, WR0, UZ80_ER_RESET); if (inp(AST+3) & 1) inp(AST+1); /* IF a char is ready get it */ } } #if XDEBUG if (s > SCREEN + 2048) { s = SCREEN; scol++; scol %= 0X10; } #endif inp(AST+0XF); disable(); outp(EOINT, 0X20); /* Send EOI to 8259 */ }