#include "linc_defs.h" /* Data bits, 110 baud rate. */ #define BIT_TIME 1120 /* Sample rate to find the start bit edge. */ #define START_TIME (BIT_TIME / 5) /* After finding the edge, wait until the middle of the first data bit. */ #define FIRST_TIME (BIT_TIME + (BIT_TIME - START_TIME) / 2) #define R (*(uint16 *)cpu_reg[5].loc) /* Debug */ #define DBG 0001 #define DBG_BIT 0002 #define DATA u3 /* Character being assembled. */ #define STATE u4 /* 0 for start bit, 1 for stop bit, otherwise data. */ #define PREVIOUS u5 /* Previous level seen. */ /* When a start bit is found, the state is set to 10 and then decremented for each bit that is processed. */ #define STATE_START 0 #define STATE_STOP 1 /* STATE_DATA 2-9 */ #define STATE_FIRST 10 /* Function declaration. */ static t_stat tty_svc(UNIT *uptr); static t_stat tty_attach(UNIT *uptr, CONST char *cptr); static t_stat tty_detach(UNIT *uptr); static UNIT tty_unit = { UDATA(&tty_svc, UNIT_IDLE | UNIT_ATTABLE, 0) }; static DEBTAB tty_deb[] = { { "DBG", DBG }, { "BIT", DBG_BIT }, { NULL, 0 } }; DEVICE tty_dev = { "TTY", &tty_unit, NULL, NULL, 1, 8, 12, 1, 8, 12, NULL, NULL, NULL, NULL, &tty_attach, &tty_detach, NULL, DEV_DISABLE | DEV_DEBUG, 0, tty_deb, NULL, NULL, NULL, NULL, NULL, NULL }; static void tty_output(UNIT *uptr) { uint8 ch = uptr->DATA; sim_debug(DBG, &tty_dev, "Character %03o '%c'\n", ch, ch & 0177); fputc(ch & 0177, uptr->fileref); fflush(uptr->fileref); } static t_stat tty_svc(UNIT *uptr) { switch (uptr->STATE) { case STATE_START: if (uptr->PREVIOUS == 0 || (R & 1) == 1) { /* Keep looking for start bit. */ uptr->PREVIOUS = R & 1; sim_activate(uptr, START_TIME); return SCPE_OK; } sim_debug(DBG_BIT, &tty_dev, "Start bit edge found.\n"); uptr->STATE = STATE_FIRST; uptr->DATA = 0; /* Wait until the middle of the first data bit. Since the edge was just seen, this is a little longer than the time between data bits. */ sim_activate(uptr, FIRST_TIME); break; default: sim_debug(DBG_BIT, &tty_dev, "Data bit %d is %d\n", STATE_FIRST - 1 - uptr->STATE, R & 1); uptr->DATA >>= 1; uptr->DATA |= (R & 1) << 7; sim_activate(uptr, BIT_TIME); break; case STATE_STOP: sim_debug(DBG_BIT, &tty_dev, "Stop bit is %d\n", R & 1); if (R & 1) tty_output(uptr); else sim_debug(DBG, &tty_dev, "Framing error.\n"); uptr->PREVIOUS = R & 1; /* Look for next start bit. */ sim_activate(uptr, START_TIME); break; } /* Decrease the state counter, first through the data bits, then the stop bit, and finally the start bit. */ uptr->STATE--; return SCPE_OK; } static t_stat tty_attach(UNIT *uptr, CONST char *cptr) { t_stat stat = attach_unit(uptr, cptr); if (stat != SCPE_OK) return stat; uptr->STATE = 0; uptr->PREVIOUS = 0; sim_activate(uptr, 1); return SCPE_OK; } static t_stat tty_detach(UNIT *uptr) { if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; if (sim_is_active(uptr)) sim_cancel(uptr); return detach_unit(uptr); }