/************************************************************************* * * * $Id: tx0_stddev.c 2063 2009-02-25 07:37:57Z hharte $ * * * * Copyright (c) 2009-2012 Howard M. Harte. * * Based on pdp1_stddev.c, Copyright (c) 1993-2006, Robert M. Supnik * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * * Except as contained in this notice, the name of Howard M. Harte shall * * not be used in advertising or otherwise to promote the sale, use or * * other dealings in this Software without prior written authorization * * of Howard M. Harte. * * * * Module Description: * * TX-0 Standard Devices * * * * Environment: * * User mode only * * * *************************************************************************/ /* petr paper tape reader ptp paper tape punch tti keyboard tto teleprinter Note: PTP timeout must be >10X faster than TTY output timeout for Macro to work correctly! */ #include "tx0_defs.h" #define FLEXO_STOP 061 /* stop code */ #define FLEXO_UC 071 #define FLEXO_LC 075 #define UC_V 6 /* upper case */ #define UC (1 << UC_V) #define BOTH (1 << (UC_V + 1)) /* both cases */ #define CW (1 << (UC_V + 2)) /* char waiting */ #define TT_WIDTH 077 #define UNIT_V_ASCII (UNIT_V_UF + 0) /* ASCII/binary mode */ #define UNIT_ASCII (1 << UNIT_V_ASCII) #define PETR_LEADER 20 /* ASCII leader chars */ #define TRACE_PRINT(dev, level, args) if(dev.dctrl & level) { \ printf args; \ } int32 petr_state = 0; int32 petr_wait = 0; int32 petr_stopioe = 0; int32 petr_uc = 0; /* upper/lower case */ int32 petr_hold = 0; /* holding buffer */ int32 petr_leader = PETR_LEADER; /* leader count */ int32 ptp_stopioe = 0; int32 tti_hold = 0; /* tti hold buf */ int32 tty_buf = 0; /* tty buffer */ int32 tty_uc = 0; /* tty uc/lc */ int32 tto_sbs = 0; extern int32 ios, iosta; extern int32 PF, IR, PC, TA; extern int32 M[]; t_stat petr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat petr_reset (DEVICE *dptr); t_stat ptp_reset (DEVICE *dptr); t_stat tty_reset (DEVICE *dptr); t_stat petr_boot (int32 unitno, DEVICE *dptr); t_stat petr_attach (UNIT *uptr, char *cptr); /* Character translation tables */ int32 flexo_to_ascii[128] = { /*00*/ 0, 0, 'e', '8', 0, '|', 'a', '3', /* lower case */ /*10*/ ' ', '=', 's', '4', 'i', '+', 'u', '2', /*20*/ 0, '.', 'd', '5', 'r', '1', 'j', '7', /*30*/ 'n', ',', 'f', '6', 'c', '-', 'k', 0, /*40*/ 't', 0, 'z', '\b','l', '\t','w', 0, /*50*/ 'h', '\r','y', 0, 'p', 0, 'q', 0, /*60*/ 'o', '*', 'b', 0, 'g', 0, '9', 0, /*70*/ 'm', 0, 'x', 0, 'v', 0, '0', 0, /*00*/ 0, 0, 'E', '8', 0, '_', 'A', '3', /* upper case */ /*10*/ ' ', ':', 'S', '4', 'I', '/', 'U', '2', /*20*/ 0, ')', 'D', '5', 'R', '1', 'J', '7', /*30*/ 'N', '(', 'F', '6', 'C', '-', 'K', 0, /*40*/ 'T', 0, 'Z', '\b','L', '\t','W', 0, /*50*/ 'H', '\r','Y', 0, 'P', 0, 'Q', 0, /*60*/ 'O', '*', 'B', 0, 'G', 0, '9', 0, /*70*/ 'M', 0, 'X', 0, 'V', 0, '0', 0, }; int32 ascii_to_flexo[128] = { /*00*/ 0, 0, 0, BOTH+061, 0, 0, 0, 0, /* STOP mapped to ^C */ /*10*/ BOTH+043, BOTH+045, 0, 0, 0, BOTH+051, 0, 0, /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, /*30*/ 0, 0, 0, BOTH+020, 0, 0, 0, 0, /* Color Shift mapped to ESC */ /*40*/ BOTH+010, 0, 0, 0, 0, 0, 0, 0, /* " ", */ /*50*/ UC+021, UC+031, 021, 015, 031, UC+035, UC+011, UC+015, /* ()*+,-./ */ /*60*/ 076, 025, 017, 007, 013, 023, 033, 027, /* 0-7 */ /*70*/ 003, 066, 0, 0, 0, 011, 0, 0, /* 89:;<=>? */ /*00*/ 040, UC+006, UC+062, UC+034, UC+022, UC+002, UC+032, UC+064, /* A-G */ /*10*/ UC+050, UC+014, UC+026, UC+036, UC+044, UC+070, UC+030, UC+060, /* H-O */ /*20*/ UC+054, UC+056, UC+024, UC+012, 040, 016, 074, 046, /* P-W */ /*30*/ UC+072, UC+052, UC+042, 0, 0, 0, 0, UC+005, /* X-Z, */ /*40*/ 00, 006, 062, 034, 022, 002, 032, 064, /* a-g */ /*50*/ 050, 014, 026, 036, 044, 070, 030, 060, /* h-o */ /*60*/ 054, 056, 024, 012, 040, 016, 074, 046, /* p-w */ /*70*/ 072, 052, 042, 0, 005, 0, UC+035, BOTH+077 /* x-z, */ }; /* PETR data structures petr_dev PETR device descriptor petr_unit PETR unit petr_reg PETR register list */ UNIT petr_unit = { UDATA (&petr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG petr_reg[] = { { ORDATA (BUF, petr_unit.buf, 18) }, { FLDATA (UC, petr_uc, UC_V) }, { FLDATA (DONE, iosta, IOS_V_PETR) }, { ORDATA (HOLD, petr_hold, 9), REG_HRO }, { ORDATA (STATE, petr_state, 5), REG_HRO }, { FLDATA (WAIT, petr_wait, 0), REG_HRO }, { DRDATA (POS, petr_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, petr_unit.wait, 24), PV_LEFT }, { DRDATA (LEADER, petr_leader, 6), REG_HRO }, { FLDATA (STOP_IOE, petr_stopioe, 0) }, { NULL } }; MTAB petr_mod[] = { { UNIT_ASCII, UNIT_ASCII, "ASCII", "ASCII", NULL }, { UNIT_ASCII, 0, "FLEXO", "FLEXO", NULL }, { 0 } }; /* Debug flags */ #define ERROR_MSG (1 << 0) #define TRACE_MSG (1 << 1) #define VERBOSE_MSG (1 << 2) /* Debug Flags */ static DEBTAB petr_dt[] = { { "ERROR", ERROR_MSG }, { "TRACE", TRACE_MSG }, { "VERBOSE",VERBOSE_MSG }, { NULL, 0 } }; DEVICE petr_dev = { "PETR", &petr_unit, petr_reg, petr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &petr_reset, &petr_boot, &petr_attach, NULL, NULL, DEV_DEBUG, (ERROR_MSG), petr_dt, NULL }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit ptp_reg PTP register list */ UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { FLDATA (DONE, iosta, IOS_V_PTP) }, { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, ptp_stopioe, 0) }, { NULL } }; MTAB ptp_mod[] = { { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, (ERROR_MSG|TRACE_MSG), petr_dt, NULL }; /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit tti_reg TTI register list */ UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; REG tti_reg[] = { { ORDATA (BUF, tty_buf, 6) }, { FLDATA (UC, tty_uc, UC_V) }, { ORDATA (HOLD, tti_hold, 9), REG_HRO }, { FLDATA (DONE, iosta, IOS_V_TTI) }, { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, { NULL } }; MTAB tti_mod[] = { { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tty_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, (ERROR_MSG|TRACE_MSG), petr_dt, NULL }; /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit tto_reg TTO register list */ UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT * 10 }; REG tto_reg[] = { { ORDATA (BUF, tty_buf, 6) }, { FLDATA (UC, tty_uc, UC_V) }, { FLDATA (DONE, iosta, IOS_V_TTO) }, { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tty_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, (ERROR_MSG|TRACE_MSG), petr_dt, NULL }; /* Paper tape reader: IOT routine. Points to note: - RPA (but not RPB) complements the reader clutch control. Thus, if the reader is running, RPA will stop it. - The status bit indicates data in the reader buffer that has not been transfered to IR. It is cleared by any RB->IR operation, including RRB and the completion pulse. - A reader error on a wait mode operation could hang the simulator. IOH is set; any retry (without RESET) will be NOP'd. Accordingly, the PETR service routine clears IOH on any error during a rpa/rpb i. */ int32 petr (int32 inst, int32 dev, int32 dat) { int32 tmpAC = 0; int i = 0; t_stat result; ios = 1; for (i=0;i> 0) << 17; /* bit 0 */ tmpAC |= ((petr_unit.buf & 002) >> 1) << 14; /* bit 3 */ tmpAC |= ((petr_unit.buf & 004) >> 2) << 11; /* bit 6 */ tmpAC |= ((petr_unit.buf & 010) >> 3) << 8; /* bit 9 */ tmpAC |= ((petr_unit.buf & 020) >> 4) << 5; /* bit 12 */ tmpAC |= ((petr_unit.buf & 040) >> 5) << 2; /* bit 15 */ if (i < (inst-1)) { uint32 bit0 = (tmpAC & 1) << 17; TRACE_PRINT(petr_dev, TRACE_MSG, ("PETR read [%04x=0x%02x] %03o\n", petr_unit.pos-1, petr_unit.buf, petr_unit.buf)); tmpAC >>= 1; tmpAC |= bit0; } else { TRACE_PRINT(petr_dev, TRACE_MSG, ("PETR read [%04x=0x%02x] %03o, tmpAC=%06o\n", petr_unit.pos-1, petr_unit.buf, petr_unit.buf, tmpAC)); } } return tmpAC; /* sim_activate (&petr_unit, petr_unit.wait); */ /* start reader */ } /* Unit service */ t_stat petr_svc (UNIT *uptr) { int32 temp; if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ ios = 0; return SCPE_OK; } if ((temp = getc (uptr->fileref)) != EOF) { /* no, get raw char */ uptr->pos = uptr->pos + 1; /* if not eof, count */ } if (temp == EOF) { /* end of file? */ if (feof (uptr->fileref)) { ios = 0; return SCPE_IOERR; } else perror ("PETR I/O error"); clearerr (uptr->fileref); ios = 0; return SCPE_IOERR; } uptr->buf = temp; ios = 0; return SCPE_OK; } /* Reset routine */ t_stat petr_reset (DEVICE *dptr) { petr_state = 0; /* clear state */ petr_wait = 0; petr_hold = 0; petr_uc = 0; petr_unit.buf = 0; iosta = iosta & ~IOS_PETR; /* clear flag */ sim_cancel (&petr_unit); /* deactivate unit */ return SCPE_OK; } /* Attach routine */ t_stat petr_attach (UNIT *uptr, char *cptr) { petr_leader = PETR_LEADER; /* set up leader */ return attach_unit (uptr, cptr); } /* Bootstrap routine */ extern t_stat cpu_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); extern UNIT cpu_unit; //#define SANITY_CHECK_TAPE /* Switches the CPU to READIN mode and starts execution. */ t_stat petr_boot (int32 unitno, DEVICE *dptr) { t_stat reason = SCPE_OK; #ifdef SANITY_CHECK_TAPE int32 AC, MBR, MAR, IR = 0; int32 blkcnt, chksum = 0, fa, la; int32 addr, tdata; #endif /* SANITY_CHECK_TAPE */ /* Switch to READIN mode. */ cpu_set_mode(&cpu_unit, UNIT_MODE_READIN, NULL, NULL); #ifdef SANITY_CHECK_TAPE for(;(IR != 2) && (IR != 1);) { AC = petr(3,0,0); /* Read three chars from tape into AC */ MAR = AC & AMASK; /* Set memory address */ IR = AC >> 16; if (!MEM_ADDR_OK(MAR)) { TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: Tape address out of range.\n")); reason = SCPE_FMT; } switch (IR) { case 00: /* Storage (sto x) */ case 03: /* Storage (opr x) */ MBR = petr(3,0,0); /* Read three characters from tape. */ TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: sto @%06o = %06o\n", MAR, MBR)); printf("[%06o] = %06o\n", MAR, MBR); break; case 02: /* Transfer Control (trn x) Start Execution */ PC = MAR; reason = SCPE_OK; /* let SIMH start execution. */ TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: trn %06o (Start Execution)\n", PC)); reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); break; case 01: /* Transfer (add x) - Halt */ PC = MAR; reason = SCPE_STOP; /* let SIMH halt. */ TRACE_PRINT(petr_dev, ERROR_MSG, ("READIN: add %06o (Halt)\n", PC)); reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL); break; default: reason = SCPE_IERR; break; } } blkcnt = 0; while (1) { chksum = 0; fa = petr(3,0,0); /* Read three characters from tape. */ if ((fa & 0400000) || (fa & 0200000)) { break; } chksum += fa; if (chksum > 0777777) { chksum +=1; } chksum &= 0777777; la = petr(3,0,0); /* Read three characters from tape. */ chksum += la; if (chksum > 0777777) { chksum +=1; } chksum &= 0777777; la = (~la) & 0177777; printf("First Address=%06o, Last Address=%06o\n", fa, la); for(addr = fa; addr <= la; addr++) { tdata = petr(3,0,0); /* Read three characters from tape. */ chksum += tdata; if (chksum > 0777777) { chksum +=1; } chksum &= 0777777; } chksum = (~chksum) & 0777777; tdata = petr(3,0,0); if (chksum != tdata) { reason = SCPE_FMT; } printf("Block %d: Calculated checksum=%06o, real checksum=%06o, %s\n", blkcnt, chksum, tdata, chksum == tdata ? "OK" : "BAD Checksum!"); blkcnt++; } fseek (petr_dev.units[0].fileref, 0, SEEK_SET); #endif /* SANITY_CHECK_TAPE */ /* Start Execution */ return (reason); } /* Paper tape punch: IOT routine */ int32 ptp (int32 inst, int32 dev, int32 dat) { iosta = iosta & ~IOS_PTP; /* clear flag */ ptp_unit.buf = dat & 0177; ptp_svc (&ptp_unit); /* sim_activate (&ptp_unit, ptp_unit.wait); */ /* start unit */ return dat; } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { ios = 1; /* restart */ iosta = iosta | IOS_PTP; /* set flag */ if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return SCPE_UNATT; if (putc (uptr->buf, uptr->fileref) == EOF) { /* I/O error? */ perror ("PTP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } uptr->pos = uptr->pos + 1; return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; /* clear state */ iosta = iosta & ~IOS_PTP; /* clear flag */ sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Typewriter IOT routines */ int32 tti (int32 inst, int32 dev, int32 dat) { iosta = iosta & ~IOS_TTI; /* clear flag */ return tty_buf & 077; } int32 tto (int32 inst, int32 dev, int32 dat) { tty_buf = dat & TT_WIDTH; /* load buffer */ ios = 0; tto_svc(&tto_unit); /* sim_activate (&tto_unit, tto_unit.wait); */ /* activate unit */ return dat; } /* Unit service routines */ t_stat tti_svc (UNIT *uptr) { int32 in = 0, temp = 0; sim_activate (uptr, uptr->wait); /* continue poll */ if (tti_hold & CW) { /* char waiting? */ tty_buf = tti_hold & TT_WIDTH; /* return char */ tti_hold = 0; /* not waiting */ } else { if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; if (temp & SCPE_BREAK) return SCPE_OK; /* ignore break */ temp = temp & 0177; if (temp == 0177) temp = '\b'; /* rubout? bs */ sim_putchar (temp); /* echo */ if (temp == '\r') sim_putchar ('\n'); /* cr? add nl */ in = ascii_to_flexo[temp]; /* translate char */ if (in == 0) return SCPE_OK; /* no xlation? */ if ((in & BOTH) || ((in & UC) == (tty_uc & UC))) { tty_buf = in & TT_WIDTH; } else { /* must shift */ tty_uc = in & UC; /* new case */ tty_buf = tty_uc? FLEXO_UC: FLEXO_LC; tti_hold = in | CW; /* set 2nd waiting */ } } iosta = iosta | IOS_TTI; /* set flag */ TRACE_PRINT(tti_dev, TRACE_MSG, ("TTI read ASCII: %02x / FLEXO=%03o\n", temp, tty_buf)); uptr->pos = uptr->pos + 1; return SCPE_OK; } t_stat tto_svc (UNIT *uptr) { int32 c = 0; t_stat r; if (tty_buf == FLEXO_UC) tty_uc = UC; /* upper case? */ else if (tty_buf == FLEXO_LC) tty_uc = 0; /* lower case? */ else { c = flexo_to_ascii[tty_buf | tty_uc]; /* translate */ if (c && ((r = sim_putchar_s (c)) != SCPE_OK)) { /* output; error? */ sim_activate (uptr, uptr->wait); /* retry */ return ((r == SCPE_STALL)? SCPE_OK: r); } } iosta = iosta | IOS_TTO; /* set flag */ uptr->pos = uptr->pos + 1; if (c == '\r') { /* cr? add lf */ sim_putchar ('\n'); uptr->pos = uptr->pos + 1; } return SCPE_OK; } /* Reset routine */ t_stat tty_reset (DEVICE *dptr) { tty_buf = 0; /* clear buffer */ tty_uc = 0; /* clear case */ tti_hold = 0; /* clear hold buf */ iosta = (iosta & ~IOS_TTI) | IOS_TTO; /* clear flag */ sim_activate (&tti_unit, tti_unit.wait); /* activate keyboard */ sim_cancel (&tto_unit); /* stop printer */ return SCPE_OK; }