diff --git a/ICL1900/div.c b/ICL1900/div.c deleted file mode 100644 index 960f32e..0000000 --- a/ICL1900/div.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include - -#define N 24 -#define B0 (1<> 22) & 3; - RT = (RT >> (6 * (3 - m))) & 077; - if (Mem_read(RB, &RS, 1)) { - goto intr; - } - m = (RB >> 22) & 3; - m = 6 * (3 - m); - RS &= ~(077 << m); - RS |= (RT & 077) << m; - if (Mem_write(RB, &RS, 1)) { - goto intr; - } - RA += 020000000; - m = (RA & BM1) != 0; - RA = ((RA + m) & M22) | (RA & CMASK); - RB += 020000000; - m = (RB & BM1) != 0; - RB = ((RB + m) & M22) | (RB & CMASK); - RK = (RK - 1) & 0777; - } while (RK != 0); - XR[RX] = RA; - XR[(RX+1)&07] = RB; - break; + if (CPU_TYPE < TYPE_B1) + goto voluntary; + RK = RB; + RB = XR[(RX+1) & 07]; + do { + if (Mem_read(RA, &RT, 1)) { + goto intr; + } + m = (RA >> 22) & 3; + RT = (RT >> (6 * (3 - m))) & 077; + if (Mem_read(RB, &RS, 1)) { + goto intr; + } + m = (RB >> 22) & 3; + m = 6 * (3 - m); + RS &= ~(077 << m); + RS |= (RT & 077) << m; + if (Mem_write(RB, &RS, 1)) { + goto intr; + } + RA += 020000000; + m = (RA & BM1) != 0; + RA = ((RA + m) & M22) | (RA & CMASK); + RB += 020000000; + m = (RB & BM1) != 0; + RB = ((RB + m) & M22) | (RB & CMASK); + RK = (RK - 1) & 0777; + } while (RK != 0); + XR[RX] = RA; + XR[(RX+1)&07] = RB; + break; /* Not on A*/ case OP_SMO: /* Supplementary Modifier - BC */ - if (OPIP) { /* Error */ - SR64 |= B1; - goto intr; - } - if (Mem_read(RS, &RP, 1)) { - goto intr; - } - PIP = 1; - break; + if (CPU_TYPE < TYPE_B1) + goto voluntary; + if (OPIP) { /* Error */ + SR64 |= B1; + goto intr; + } + if (Mem_read(RS, &RP, 1)) { + goto intr; + } + PIP = 1; + break; - case OP_NULL: /* No Operation */ - if (!exe_mode && RX == 7 && (Mode & 7) > 0 && (Mode & 7) < 5) - SR64 |= B2; - break; + case OP_NULL: /* No Operation */ + if (!exe_mode && RX == 7 && (Mode & 7) > 0 && (Mode & 7) < 5) + SR64 |= B2; + break; - case OP_LDCT: /* Load Count */ - RA = CNTMSK & (RB << 15); - XR[RX] = RA; - break; + case OP_LDCT: /* Load Count */ + RA = CNTMSK & (RB << 15); + XR[RX] = RA; + break; case OP_MODE: /* Set Mode */ -#if 0 /* Stevenage Machines */ - if (exe_mode && RX == 1) - temp = RB; /* Set interrupt enable mode */ - else -/* On Stevenage Machines 002 -> 020 Add Datum */ -/* On Stevenage Machines 004 -> 000 unused */ -/* On Stevenage Machines 010 -> 000 unused */ -/* On Stevenage Machines 020 -> Extended store mode */ -/* On Stevenage Machines 040 -> 000 unused */ -/* On Stevenage Machines 0100 -> Carry */ -#endif - if (exe_mode) - Mode = RB & 076; - Zero = RB & 1; - adrmask = (Mode & AM22) ? M22 : M15; - break; + /* Stevenage Machines */ + if ((cpu_flags & SV) != 0 && exe_mode) { + if (RX == 0) { + /* Remap modes settings */ + Mode = 0; + if (RB & 02) + Mode |= DATUM; + if (RB & 020) + Mode |= AM22; + if (RB & 0100) + Mode |= EJM; + if (RB & 0200) + BCarry = 1; + } else if (RX == 1) { + temp = RB; /* Set interrupt enable mode */ + } + } else if (exe_mode) + Mode = RB & 076; + Zero = RB & 1; + adrmask = (Mode & AM22) ? M22 : M15; + break; - case OP_MOVE: /* Copy N words */ - RK = RB; - RA &= adrmask; - RB = XR[(RX+1) & 07] & adrmask; - do { - if (Mem_read(RA, &RT, 1)) { - goto intr; - } - if (Mem_write(RB, &RT, 1)) { - goto intr; - } - RA++; - RB++; - RK = (RK - 1) & 0777; - } while (RK != 0); - break; + case OP_MOVE: /* Copy N words */ + if (CPU_TYPE < TYPE_B1) + goto voluntary; + RK = RB; + RA &= adrmask; + RB = XR[(RX+1) & 07] & adrmask; + do { + if (Mem_read(RA, &RT, 1)) { + goto intr; + } + if (Mem_write(RB, &RT, 1)) { + goto intr; + } + RA++; + RB++; + RK = (RK - 1) & 0777; + } while (RK != 0); + break; - case OP_SUM: /* Sum N words */ - RK = RB; - RA = 0; - RB = XR[(RX+1) & 07] & adrmask; - do { - if (Mem_read(RB, &RT, 1)) { - goto intr; - } - RA = (RA + RT) & FMASK; - RB++; - RK = (RK - 1) & 0777; - } while (RK != 0); - XR[RX] = RA; - break; + case OP_SUM: /* Sum N words */ + if (CPU_TYPE < TYPE_B1) + goto voluntary; + RK = RB; + RB = XR[(RX+1) & 07] & adrmask; + RA = 0; + do { + if (Mem_read(RB, &RT, 1)) { + goto intr; + } + RA = (RA + RT) & FMASK; + RB++; + RK = (RK - 1) & 0777; + } while (RK != 0); + XR[RX] = RA; + break; /* B or C with Floating Point */ case OP_FLOAT: /* Convert Fixed to Float +FP */ + if ((cpu_flags & FLOAT) == 0) + goto voluntary; if (Mem_read(RS, &RA, 1)) { goto intr; } @@ -1513,6 +1616,8 @@ norm1: RX = 0; goto fn; case OP_FIX: /* Convert Float to Fixed +FP */ + if ((cpu_flags & FLOAT) == 0) + goto voluntary; RA = faccl; RB = facch & MMASK; e1 = 279 - (facch & M9); @@ -1557,6 +1662,8 @@ norm1: case OP_FAD: /* Floating Point Add +FP */ case OP_FSB: /* Floating Point Subtract +FP */ + if ((cpu_flags & FLOAT) == 0) + goto voluntary; if (Mem_read(RS, &RA, 1)) { /* Read lower */ goto intr; } @@ -1713,6 +1820,8 @@ fexp: break; case OP_FMPY: /* Floating Point Multiply +FP */ + if ((cpu_flags & FLOAT) == 0) + goto voluntary; if (Mem_read(RS, &RA, 1)) { /* Read lower */ goto intr; } @@ -1847,6 +1956,8 @@ fexp: goto fn; case OP_FDVD: /* Floating Point Divide +FP */ + if ((cpu_flags & FLOAT) == 0) + goto voluntary; if (Mem_read(RS, &RA, 1)) { /* Read lower */ goto intr; } @@ -1969,6 +2080,8 @@ fexp: goto fn; case OP_LFP: /* Load Floating Point +FP */ + if ((cpu_flags & FLOAT) == 0) + goto voluntary; if (RX & 1) { faccl = facch = fovr = 0; //fprintf(stderr, "LFPZ: %08o %08o %o\n\r", faccl, facch, RX); @@ -1989,6 +2102,8 @@ fexp: case OP_SFP: /* Store Floating Point +FP */ //fprintf(stderr, "SFP: %08o %08o %o\n\r", faccl, facch, fovr); + if ((cpu_flags & FLOAT) == 0) + goto voluntary; if (Mem_write(RB, &faccl, 1)) { goto intr; } @@ -2022,27 +2137,23 @@ fexp: if (exe_mode) { RA = 0; switch(RB) { + case 0: /* Time of day clock */ break; case 1: RA = SR1; break; - case 2: ctyi_cmd(0, &RA); break; - case 3: ctyo_cmd(0, &RA); break; case 64: RA = SR64; SR64 &= 003777777; break; case 65: RA = SR65; break; + default: if (RB < 64) + chan_nsi_status(RB, &RA); + break; } XR[RX] = RA; -//fprintf(stderr, "RD SR %o %08o\n\r", RB, RA); break; } /* Fall through */ case 0171: /* Write special register */ if (exe_mode) { //fprintf(stderr, "WR SR %o %08o\n\r", RB, RA); - RT = 0; - switch(RB) { - case 2: ctyi_cmd(RA, &RT); break; - case 3: ctyo_cmd(RA, &RT); break; - } - XR[RX] = RT; - + if (RB < 64) + chan_nsi_cmd(RB, RA); break; } /* Fall through */ @@ -2089,14 +2200,13 @@ fexp: /* Fall through */ case 0174: /* Send control character to peripheral */ if (exe_mode) { - RT = RB; - chan_send_cmd(RB, RA & 077, &RB); + chan_send_cmd(RB, RA & 077, &RT); fprintf(stderr, "CMD %04o %04o %08o\n\r", RT, RB, RA); m = (m == 0) ? 3 : (XR[m] >> 22) & 3; m = 6 * (3 - m); - RB = (RB & 077) << m; + RT = (RT & 077) << m; RA &= ~(077 << m); - RA |= RB; + RA |= RT; XR[RX] = RA; break; } @@ -2170,6 +2280,7 @@ rtc_srv(UNIT * uptr) t = sim_rtcn_calb(rtc_tps, TMR_RTC); sim_activate_after(uptr, 1000000/rtc_tps); + SR64 |= B3; // tmxr_poll = t; return SCPE_OK; } @@ -2247,6 +2358,55 @@ cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc) M[i] = 0; return SCPE_OK; } + +t_stat +cpu_set_model(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + CPUMOD *ptr; + + val >>= UNIT_V_MODEL; + for(ptr = &cpu_modtab[0]; ptr->name != NULL; ptr++) { + if (ptr->mod_num == val) { + cpu_flags = ptr->cpu_flags; + io_flags = ptr->io_flags; + rtc_tps = ptr->ticker; + cpu_unit[0].flags &= ~(UNIT_MODEL|UNIT_FLOAT|UNIT_MULT); + cpu_unit[0].flags |= MODEL(val); + if (cpu_flags & FLOAT) + cpu_unit[0].flags |= UNIT_FLOAT; + if (cpu_flags & MULT) + cpu_unit[0].flags |= UNIT_MULT; + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat +cpu_set_float(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 oval = val; + if (val == 0 && (cpu_flags & (FLOAT_OPT|FLOAT)) == FLOAT) + val = UNIT_FLOAT; + cpu_unit[0].flags &= ~UNIT_FLOAT; + cpu_unit[0].flags |= val; + if (oval != val) + return SCPE_ARG; + return SCPE_OK; +} + +t_stat +cpu_set_mult(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 oval = val; + if (val == 0 && (cpu_flags & (MULT_OPT|MULT)) == MULT) + val = UNIT_MULT; + cpu_unit[0].flags &= ~UNIT_MULT; + cpu_unit[0].flags |= val; + if (oval != val) + return SCPE_ARG; + return SCPE_OK; +} /* Handle execute history */ diff --git a/ICL1900/icl1900_cty.c b/ICL1900/icl1900_cty.c index 73b74cb..a5c7bda 100644 --- a/ICL1900/icl1900_cty.c +++ b/ICL1900/icl1900_cty.c @@ -1,347 +1,345 @@ -/* icl1900_cty.c: ICL1900 front end (console terminal) simulator - - Copyright (c) 2018, Richard Cornwell - - 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 - RICHARD CORNWELL 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 Richard Cornwell shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Richard Cornwell - -*/ - -#include "icl1900_defs.h" - -extern int32 tmxr_poll; -t_stat ctyi_svc (UNIT *uptr); -t_stat ctyo_svc (UNIT *uptr); -t_stat cty_reset (DEVICE *dptr); -t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); -t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); -const char *cty_description (DEVICE *dptr); - -#define CMD u3 -#define STATUS u4 -#define HOLD u5 - -#define START 01 -#define STOP 02 -#define END 000001 -#define BUSY 000040 -#define INPUT 000100 -#define CANCEL 000200 -#define ACCEPT 000400 -#define F1 001000 -#define F2 002000 -#define F3 004000 -#define F4 010000 -#define F5 020000 - -UNIT cty_unit[] = { - { UDATA (&ctyo_svc, TT_MODE_7P, 0), 10000 }, - { UDATA (&ctyi_svc, TT_MODE_7P|UNIT_IDLE, 0), 0 }, - }; - - -MTAB cty_mod[] = { - { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, - { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, - { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, - { TT_MODE, TT_MODE_7P, "7b", "7P", &tty_set_mode }, - { 0 } - }; - -DEVICE cty_dev = { - "CTY", cty_unit, NULL, cty_mod, - 2, 8, 22, 1, 8, 22, - NULL, NULL, &cty_reset, - NULL, NULL, NULL, NULL, DEV_DEBUG, 0, dev_debug, - NULL, NULL, &cty_help, NULL, NULL, &cty_description - }; - -/* - * Commands: - * Bit 0 = start. - * Bit 1 = stop. - * - * Responses: - * Bit 0 = End of Transfer - * Bit 5 = Busy - * Bit 6 = Input Button Pushed. - * Bit 7 = Cancel Button Pushed. - * Bit 8 = Accept Button Pushed. - * Bit 9 = F1 - * Bit 10 = F2 - * Bit 11 = F3 - * Bit 12 = F4 - * Bit 13 = F5 - * - * 00xxxx -> 011xxxx - * 01xxxx -> 010xxxx - * 10xxxx -> 100xxxx - * 11xxxx -> 101xxxx - * - */ -t_stat ctyo_cmd(uint32 cmd, uint32 *resp) { - if ((cmd & START) != 0 && (cty_unit[0].STATUS & BUSY) == 0) - sim_activate(&cty_unit[0], cty_unit[0].wait); - cty_unit[0].CMD |= cmd; - *resp = cty_unit[0].STATUS; - cty_unit[0].STATUS &= BUSY; - chan_clr_done(3); -} - -t_stat ctyi_cmd(uint32 cmd, uint32 *resp) { - cty_unit[1].CMD |= cmd; - *resp = cty_unit[1].STATUS; - cty_unit[1].STATUS &= BUSY; - chan_clr_done(2); -} - -t_stat ctyo_svc (UNIT *uptr) -{ - t_stat r; - uint8 ch; - int eor; - - if (uptr->CMD & START) { - uptr->STATUS &= ~END; - uptr->STATUS |= BUSY; - chan_clr_done(3); - } - if (uptr->CMD & STOP) { - uptr->STATUS |= END; - uptr->STATUS &= ~BUSY; - chan_set_done(3); - } - uptr->CMD = 0; - /* Check if we had a held characteter */ - if (uptr->HOLD != 0) { - if ((r = sim_putchar_s (uptr->HOLD)) == SCPE_STALL) { - r = SCPE_OK; - } else { - if (uptr->HOLD == '\r') - uptr->HOLD = '\n'; - else - uptr->HOLD = 0; - } - sim_activate (uptr, uptr->wait); /* try again */ - return r; - } - - if (uptr->STATUS & BUSY) { - eor = chan_output_char(3, &ch, 0); - switch (ch & 060) { - case 000: ch = 0060 | (ch & 017); break; - case 020: ch = 0040 | (ch & 017); break; - case 040: ch = 0100 | (ch & 017); break; - case 060: ch = 0120 | (ch & 017); break; - } - if (ch == 0137) { - ch = '\r'; - uptr->HOLD = '\n'; - } - ch = sim_tt_outcvt ( ch, TT_GET_MODE (uptr->flags)) ; - if ((r = sim_putchar_s (ch)) == SCPE_STALL) { /* output; error? */ - uptr->HOLD = ch; - r = SCPE_OK; - } - if (eor) { - uptr->STATUS &= ~BUSY; - uptr->STATUS |= END; - chan_set_done(3); - } - sim_activate (uptr, uptr->wait); /* try again */ - } - return SCPE_OK; -} - -t_stat ctyi_svc (UNIT *uptr) -{ - t_stat r; - uint8 ch; - int eor; - - sim_clock_coschedule (uptr, tmxr_poll); - - if (uptr->CMD & START) { - uptr->STATUS &= ~END; - uptr->STATUS |= BUSY; - chan_clr_done(2); - } - if (uptr->CMD & STOP) { - uptr->STATUS |= END; - uptr->STATUS &= ~BUSY; - chan_set_done(2); - } - uptr->CMD = 0; -/* Handle input */ - - r = sim_poll_kbd(); - if (r & SCPE_KFLAG) { - ch = 0177 & sim_tt_inpcvt(r & 0377, TT_GET_MODE (uptr->flags)); - if (uptr->HOLD) { - if (ch >= '1' && ch <= '5') { - chan_set_done(2); - uptr->STATUS |= (F1 << (ch - '1')); - } else { - sim_putchar('\007'); - } - uptr->HOLD = 0; - return SCPE_OK; - } - if ((uptr->STATUS & BUSY) != 0) { - /* Handle end of buffer */ - switch (ch) { - case '\r': - case '\n': - sim_debug(DEBUG_DATA, &cty_dev, ": ent\n"); - sim_putchar('\r'); - sim_putchar('\n'); - uptr->STATUS |= ACCEPT|END; - uptr->STATUS &= ~BUSY; - chan_set_done(2); - break; - - case 033: /* function key n key */ - uptr->HOLD = 1; - break; - - case 030: /* ^X Post input interrupt */ - sim_debug(DEBUG_CMD, &cty_dev, ": inp\n"); - uptr->STATUS |= INPUT|END; - uptr->STATUS &= ~BUSY; - uptr->HOLD = 0; - chan_set_done(2); - break; - - case 03: /* ^C */ - case 025: /* ^U clear line */ - uptr->STATUS |= CANCEL|END; - uptr->STATUS &= ~BUSY; - uptr->HOLD = 0; - chan_set_done(2); - break; - - default: - sim_debug(DEBUG_DATA, &cty_dev, ": key '%c'\n", ch); - if (ch >= 0140) - ch -= 040; - if (ch >= 0100) - ch -= 040; - else if (ch >= 060) - ch -= 060; - else if (ch >= 040) - ch -= 020; - else { - sim_putchar('\007'); - break; - } - eor = chan_input_char(2, &ch, 0); - switch (ch & 060) { - case 000: ch = 0060 | (ch & 017); break; - case 020: ch = 0040 | (ch & 017); break; - case 040: ch = 0100 | (ch & 017); break; - case 060: ch = 0120 | (ch & 017); break; - } - ch = sim_tt_outcvt ( ch, TT_GET_MODE (uptr->flags)) ; - sim_putchar (ch); - if (eor) { - uptr->STATUS &= ~BUSY; - uptr->STATUS |= END; - chan_set_done(2); - } - break; - } - } else { - /* Handle end of buffer */ - switch (ch) { - case 033: /* function key n key */ - uptr->HOLD = 1; - break; - - case 030: /* ^X Post input interrupt */ - sim_debug(DEBUG_CMD, &cty_dev, ": inp\n"); - uptr->STATUS |= INPUT; - uptr->HOLD = 0; - chan_set_done(2); - break; - - case 03: /* ^C */ - case 025: /* ^U clear line */ - uptr->STATUS |= CANCEL; - uptr->HOLD = 0; - chan_set_done(2); - break; - - default: - sim_debug(DEBUG_DATA, &cty_dev, ": key '%c'\n", ch); - sim_putchar('\007'); - } - } - } - - return SCPE_OK; -} - -/* Reset */ - -t_stat cty_reset (DEVICE *dptr) -{ - cty_unit[0].CMD = 0; - cty_unit[1].CMD = 0; - cty_unit[0].STATUS = 0; - cty_unit[1].STATUS = 0; - chan_clr_done(2); - chan_clr_done(3); - sim_clock_coschedule (&cty_unit[1], tmxr_poll); - - return SCPE_OK; -} - - -t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) -{ - cty_unit[0].flags = (cty_unit[0].flags & ~TT_MODE) | val; - cty_unit[1].flags = (cty_unit[1].flags & ~TT_MODE) | val; - return SCPE_OK; -} - -t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) -{ -fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n"); -fprintf (st, " mode input characters output characters\n\n"); -fprintf (st, " UC lower case converted lower case converted to upper case,\n"); -fprintf (st, " to upper case, high-order bit cleared,\n"); -fprintf (st, " high-order bit cleared non-printing characters suppressed\n"); -fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n"); -fprintf (st, " non-printing characters suppressed\n"); -fprintf (st, " 7B high-order bit cleared high-order bit cleared\n"); -fprintf (st, " 8B no changes no changes\n\n"); -fprintf (st, "The default mode is 7P. In addition, each line can be configured to\n"); -fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n"); -fprint_reg_help (st, &cty_dev); -return SCPE_OK; -} - -const char *cty_description (DEVICE *dptr) -{ - return "Console TTY Line"; - -} - +/* icl1900_cty.c: ICL1900 front end (console terminal) simulator + + Copyright (c) 2018, Richard Cornwell + + 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 + RICHARD CORNWELL 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 Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell + +*/ + +#include "icl1900_defs.h" + +extern int32 tmxr_poll; +void cty_cmd(int dev, uint32 cmd); +void cty_status(int dev, uint32 *resp); +t_stat ctyi_svc (UNIT *uptr); +t_stat ctyo_svc (UNIT *uptr); +t_stat cty_reset (DEVICE *dptr); +t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *cty_description (DEVICE *dptr); + +DIB cty_dib = { CHAR_DEV, NULL, cty_cmd, cty_status}; + +#define CMD u3 +#define STATUS u4 +#define HOLD u5 + +#define START 01 +#define STOP 02 +#define END 000001 +#define BUSY 000040 +#define INPUT 000100 +#define CANCEL 000200 +#define ACCEPT 000400 +#define F1 001000 +#define F2 002000 +#define F3 004000 +#define F4 010000 +#define F5 020000 + +UNIT cty_unit[] = { + { UDATA (&ctyo_svc, UNIT_ADDR(3)|TT_MODE_7P, 0), 10000 }, + { UDATA (&ctyi_svc, UNIT_ADDR(2)|TT_MODE_7P|UNIT_IDLE, 0), 0 }, + }; + + +MTAB cty_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7b", "7P", &tty_set_mode }, + { 0 } + }; + +DEVICE cty_dev = { + "CTY", cty_unit, NULL, cty_mod, + 2, 8, 22, 1, 8, 22, + NULL, NULL, &cty_reset, + NULL, NULL, NULL, &cty_dib, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cty_help, NULL, NULL, &cty_description + }; + +/* + * Commands: + * Bit 0 = start. + * Bit 1 = stop. + * + * Responses: + * Bit 0 = End of Transfer + * Bit 5 = Busy + * Bit 6 = Input Button Pushed. + * Bit 7 = Cancel Button Pushed. + * Bit 8 = Accept Button Pushed. + * Bit 9 = F1 + * Bit 10 = F2 + * Bit 11 = F3 + * Bit 12 = F4 + * Bit 13 = F5 + * + * 00xxxx -> 011xxxx + * 01xxxx -> 010xxxx + * 10xxxx -> 100xxxx + * 11xxxx -> 101xxxx + * + */ +void cty_cmd(int dev, uint32 cmd) { + int u = 0; + + if (dev > 3) + return; + if (dev == 2) + u++; + if (cmd & START) { + cty_unit[u].STATUS &= ~END; + cty_unit[u].STATUS |= BUSY; + if (!u) + sim_activate(&cty_unit[u], cty_unit[u].wait); + } + else if (cmd & STOP) { + cty_unit[u].STATUS &= ~BUSY; + } + cty_unit[u].STATUS &= BUSY; + chan_clr_done(GET_UADDR(cty_unit[u].flags)); +} + +void cty_status(int dev, uint32 *resp) { + int u = 0; + + if (dev > 3) + return; + if (dev == 2) + u++; + *resp = cty_unit[u].STATUS; + cty_unit[u].STATUS &= BUSY; + chan_clr_done(GET_UADDR(cty_unit[u].flags)); +} + +t_stat ctyo_svc (UNIT *uptr) +{ + t_stat r; + uint8 ch; + int eor; + + /* Check if we had a held characteter */ + if (uptr->HOLD != 0) { + if ((r = sim_putchar_s (uptr->HOLD)) == SCPE_STALL) { + r = SCPE_OK; + } else { + if (uptr->HOLD == '\r') + uptr->HOLD = '\n'; + else + uptr->HOLD = 0; + } + sim_activate (uptr, uptr->wait); /* try again */ + return r; + } + + if (uptr->STATUS & BUSY) { + eor = chan_output_char(GET_UADDR(uptr->flags), &ch, 0); + switch (ch & 060) { + case 000: ch = 0060 | (ch & 017); break; + case 020: ch = 0040 | (ch & 017); break; + case 040: ch = 0100 | (ch & 017); break; + case 060: ch = 0120 | (ch & 017); break; + } + if (ch == 0137) { + ch = '\r'; + uptr->HOLD = '\n'; + } + ch = sim_tt_outcvt ( ch, TT_GET_MODE (uptr->flags)) ; + if ((r = sim_putchar_s (ch)) == SCPE_STALL) { /* output; error? */ + uptr->HOLD = ch; + r = SCPE_OK; + } + if (eor) { + uptr->STATUS &= ~BUSY; + uptr->STATUS |= END; + chan_set_done(GET_UADDR(uptr->flags)); + } + sim_activate (uptr, uptr->wait); /* try again */ + } + return SCPE_OK; +} + +t_stat ctyi_svc (UNIT *uptr) +{ + t_stat r; + uint8 ch; + int eor; + + sim_clock_coschedule (uptr, tmxr_poll); + +/* Handle input */ + + r = sim_poll_kbd(); + if (r & SCPE_KFLAG) { + ch = 0177 & sim_tt_inpcvt(r & 0377, TT_GET_MODE (uptr->flags)); + if (uptr->HOLD) { + if (ch >= '1' && ch <= '5') { + chan_set_done(GET_UADDR(uptr->flags)); + uptr->STATUS |= (F1 << (ch - '1')); + } else { + sim_putchar('\007'); + } + uptr->HOLD = 0; + return SCPE_OK; + } + if ((uptr->STATUS & BUSY) != 0) { + /* Handle end of buffer */ + switch (ch) { + case '\r': + case '\n': + sim_debug(DEBUG_DATA, &cty_dev, ": ent\n"); + sim_putchar('\r'); + sim_putchar('\n'); + uptr->STATUS |= ACCEPT; + uptr->STATUS &= ~BUSY; + chan_set_done(GET_UADDR(uptr->flags)); + break; + + case 033: /* function key n key */ + uptr->HOLD = 1; + break; + + case 030: /* ^X Post input interrupt */ + sim_debug(DEBUG_CMD, &cty_dev, ": inp\n"); + uptr->STATUS |= INPUT; + uptr->STATUS &= ~BUSY; + uptr->HOLD = 0; + chan_set_done(GET_UADDR(uptr->flags)); + break; + + case 03: /* ^C */ + case 025: /* ^U clear line */ + uptr->STATUS |= CANCEL; + uptr->STATUS &= ~BUSY; + uptr->HOLD = 0; + chan_set_done(GET_UADDR(uptr->flags)); + break; + + default: + sim_debug(DEBUG_DATA, &cty_dev, ": key '%c'\n", ch); + if (ch >= 0140) + ch -= 040; + if (ch >= 0100) + ch -= 040; + else if (ch >= 060) + ch -= 060; + else if (ch >= 040) + ch -= 020; + else { + sim_putchar('\007'); + break; + } + eor = chan_input_char(GET_UADDR(uptr->flags), &ch, 0); + switch (ch & 060) { + case 000: ch = 0060 | (ch & 017); break; + case 020: ch = 0040 | (ch & 017); break; + case 040: ch = 0100 | (ch & 017); break; + case 060: ch = 0120 | (ch & 017); break; + } + ch = sim_tt_outcvt ( ch, TT_GET_MODE (uptr->flags)) ; + sim_putchar (ch); + if (eor) { + uptr->STATUS &= ~BUSY; + uptr->STATUS |= END; + chan_set_done(GET_UADDR(uptr->flags)); + } + break; + } + } else { + /* Handle end of buffer */ + switch (ch) { + case 033: /* function key n key */ + uptr->HOLD = 1; + break; + + case 030: /* ^X Post input interrupt */ + sim_debug(DEBUG_CMD, &cty_dev, ": inp\n"); + uptr->STATUS |= INPUT; + uptr->HOLD = 0; + chan_set_done(GET_UADDR(uptr->flags)); + break; + + case 03: /* ^C */ + case 025: /* ^U clear line */ + uptr->STATUS |= CANCEL; + uptr->HOLD = 0; + chan_set_done(GET_UADDR(uptr->flags)); + break; + + default: + sim_debug(DEBUG_DATA, &cty_dev, ": key '%c'\n", ch); + sim_putchar('\007'); + } + } + } + + return SCPE_OK; +} + +/* Reset */ + +t_stat cty_reset (DEVICE *dptr) +{ + cty_unit[0].CMD = 0; + cty_unit[1].CMD = 0; + cty_unit[0].STATUS = 0; + cty_unit[1].STATUS = 0; + chan_clr_done(GET_UADDR(cty_unit[0].flags)); + chan_clr_done(GET_UADDR(cty_unit[1].flags)); + sim_clock_coschedule (&cty_unit[1], tmxr_poll); + + return SCPE_OK; +} + + +t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + cty_unit[0].flags = (cty_unit[0].flags & ~TT_MODE) | val; + cty_unit[1].flags = (cty_unit[1].flags & ~TT_MODE) | val; + return SCPE_OK; +} + +t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n"); +fprintf (st, " mode input characters output characters\n\n"); +fprintf (st, " UC lower case converted lower case converted to upper case,\n"); +fprintf (st, " to upper case, high-order bit cleared,\n"); +fprintf (st, " high-order bit cleared non-printing characters suppressed\n"); +fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n"); +fprintf (st, " non-printing characters suppressed\n"); +fprintf (st, " 7B high-order bit cleared high-order bit cleared\n"); +fprintf (st, " 8B no changes no changes\n\n"); +fprintf (st, "The default mode is 7P. In addition, each line can be configured to\n"); +fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n"); +fprint_reg_help (st, &cty_dev); +return SCPE_OK; +} + +const char *cty_description (DEVICE *dptr) +{ + return "Console TTY Line"; + +} + diff --git a/ICL1900/icl1900_defs.h b/ICL1900/icl1900_defs.h index ad21bd0..df32a05 100755 --- a/ICL1900/icl1900_defs.h +++ b/ICL1900/icl1900_defs.h @@ -28,6 +28,8 @@ /* Definitions for each supported CPU */ +#define NUM_DEVS_PTR 1 +#define NUM_DEVS_PTP 1 #define NUM_DEVS_CDR 0 #define NUM_DEVS_CDP 0 #define NUM_DEVS_LPR 0 @@ -38,6 +40,7 @@ #define MAXMEMSIZE (4096 * 1024) extern uint32 M[]; /* Main Memory */ +extern uint32 XR[8]; /* Memory */ #define MEMSIZE (cpu_unit[0].capac) /* actual memory size */ @@ -59,15 +62,50 @@ extern DEBTAB dev_debug[]; extern uint32 SR64; extern uint32 SR65; +extern uint32 RC; +extern uint16 cpu_flags; +extern uint8 io_flags; +extern uint8 loading; + /* Returns from device commands */ #define SCPE_BUSY (1) /* Device is active */ #define SCPE_NODEV (2) /* No device exists */ +typedef struct _cpumod +{ + CONST char *name; + uint8 mod_num; /* Model number */ + uint16 cpu_flags; /* Cpu option flags */ + uint8 io_flags; /* I/O type option. */ + uint16 ticker; /* Number of ticker events per second */ +} CPUMOD; + +/* Definitions for cpu flags */ +#define CPU_TYPE (cpu_flags & 7) +#define TYPE_A1 0000 +#define TYPE_A2 0001 +#define TYPE_B1 0002 +#define TYPE_B2 0003 +#define TYPE_C1 0004 +#define TYPE_C2 0005 +#define FLOAT_STD 0010 /* Floating point standard */ +#define FLOAT_OPT 0020 /* Floating point optional */ +#define FLOAT 0040 /* Floating point installed */ +#define STD_FLOAT 0100 /* Std Floating point only */ +#define NORM_OP 0001 +#define MULT_OPT 0200 /* Multiply/Divide optional */ +#define MULT 0400 /* Multiply/Divide installed */ +#define SV 01000 /* Stevenage Machine */ +#define WG 00000 /* West Gorton Machine */ + +/* Definitions for io_flags */ +#define EXT_IO 0001 /* I/O channels at 256 and above */ + /* Symbol tables */ typedef struct _opcode { - const char *name; + CONST char *name; uint8 type; } t_opcode; @@ -192,21 +230,56 @@ t_opcode; #define NMASK 037777400 #define MMASK 037777000 +#define UNIT_V_ADDR 24 +#define UNIT_M_ADDR (077 << UNIT_V_ADDR) +#define GET_UADDR(x) ((UNIT_M_ADDR & (x)) >> UNIT_V_ADDR) +#define UNIT_ADDR(x) (UNIT_M_ADDR & ((x) << UNIT_V_ADDR)) + +/* DIB type flags */ #define CHAR_DEV 0 /* Device transfers via characters */ #define WORD_DEV 1 /* Device transfers via words */ #define SPEC_HES 2 /* Special transfer */ #define LONG_BLK 4 /* Long block device */ +#define MULT_DEV 8 /* Channel in device flags */ struct icl_dib { - uint8 channel; /* Channel number */ - uint8 type; /* Type of device */ - t_stat (*dev_cmd)(uint32 cmd, uint32 *resp); /* Start io on device */ + uint8 type; /* Type of device */ + void (*si_cmd)(int dev, uint32 cmd, uint32 *resp); /* Start io on device */ + void (*nsi_cmd)(int dev, uint32 cmd); /* Start non-standard I/O on device */ + void (*nsi_status)(int dev, uint32 *resp); /* Non-Standard I/O status */ }; typedef struct icl_dib DIB; -/* Hessitation operations */ +/* Common commands */ +#define SEND_Q 020 /* Send status Q */ +#define SEND_P 024 /* Send status P */ +#define SEND_P2 025 /* Send status P2 */ +#define DISCO 036 /* Disconnect device */ + +/* General response code */ +#define DEV_INOP 000 /* Device inoperable */ +#define DEV_REJT 003 /* Command rejected */ +#define DEV_ACCP 005 /* Command accepted */ + +/* P Staus bits */ +#define DEV_OPT 001 /* Device operational */ +#define DEV_WARN 002 /* Device has warning */ +#define DEV_ERROR 004 /* Device has error pending */ + +/* Q Status bits */ +#define DEV_TERM 001 /* Device terminated */ +#define DEV_P_STAT 040 /* No P status */ + +/* Channel controls */ +extern t_stat chan_set_devs(); +extern t_stat set_chan(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat get_chan(FILE *st, UNIT *uptr, int32 v, CONST void *desc); + +/* Hesitation operations */ extern void chan_send_cmd(int dev, uint32 cmd, uint32 *resp); +extern void chan_nsi_cmd(int dev, uint32 cmd); +extern void chan_nsi_status(int dev, uint32 *resp); extern int chan_input_char(int dev, uint8 *data, int eor); extern int chan_output_char(int dev, uint8 *data, int eor); extern int chan_input_word(int dev, uint32 *data, int eor); @@ -214,17 +287,17 @@ extern int chan_output_word(int dev, uint32 *data, int eor); extern void chan_set_done(int dev); extern void chan_clr_done(int dev); -/* Console tty device routines */ -extern t_stat ctyi_cmd(uint32 cmd, uint32 *resp); -extern t_stat ctyo_cmd(uint32 cmd, uint32 *resp); - /* Generic devices common to all */ extern DEVICE cpu_dev; extern UNIT cpu_unit[]; extern REG cpu_reg[]; /* Global device definitions */ +extern DIB ctyi_dib; +extern DIB ctyo_dib; extern DEVICE cty_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; extern DEVICE cdr_dev; extern DEVICE cdp_dev; extern DEVICE lpr_dev; diff --git a/ICL1900/icl1900_stdio.c b/ICL1900/icl1900_stdio.c index b137ed7..5fbf735 100644 --- a/ICL1900/icl1900_stdio.c +++ b/ICL1900/icl1900_stdio.c @@ -1,365 +1,430 @@ -/* icl1900_stdio.c: ICL 1900 standard I/O devices - - Copyright (c) 2018, Richard Cornwell - - 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 - RICHARD CORNWELL 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. - -*/ - -#include "icl1900_defs.h" - -/* Define style bits */ - -#define OUTPUT B0 /* Input/Output flag */ -#define BACKWARD B1 /* Forward/Backword */ -#define WORDCCW B2 /* Character/Word */ -#define MULTICHN B3 /* Single/Multi Channel */ -#define CWRECHARGE B4 /* Recharge CW when empty */ -#define GATHER B5 /* Gather */ - - -DIB *devs[32]; /* Pointer to device DIB entry */ - -/* Fake DIB entries for the CTY devices */ -DIB ctyi_dib = { 2, CHAR_DEV, ctyi_cmd}; -DIB ctyo_dib = { 3, CHAR_DEV, ctyo_cmd}; -DIB nul1_dib = { 22, 0, 0}; -DIB nul2_dib = { 23, 0, 0}; - -uint32 cw0; /* Holding registers for current control word */ -uint32 cw1; - -t_stat -chan_set_devs() -{ - int i; - - /* Clear device table */ - for (i = 0; i < sizeof(devs)/sizeof(DIB *); i++) - devs[i] = NULL; - - devs[22-4] = &nul1_dib; /* Add in hidden channels */ - devs[23-4] = &nul2_dib; - /* Scan all devices and assign to channel */ - for(i = 0; sim_devices[i] != NULL; i++) { - DIB *dibp = (DIB *) sim_devices[i]->ctxt; - int chan; - - /* Check if device a channel device */ - if (dibp == NULL) - continue; - /* If device is disabled, don't add it */ - if (sim_devices[i]->flags & DEV_DIS) - continue; - chan = dibp->channel - 4; - if (chan < 0) - continue; - if (devs[chan] != NULL) { - fprintf(stderr, "Conflict between devices %d %s\n", chan, sim_devices[i]->name); - } else { - devs[chan] = dibp; - } - } - return SCPE_OK; -} - - -/* Set the device onto a given channel */ -t_stat -set_chan(UNIT *uptr, int32 val, CONST char *cptr, void *desc) -{ - DEVICE *dptr; - DIB *dibp; - int newchan; - t_stat r; - - if (cptr == NULL) - return SCPE_ARG; - if (uptr == NULL) - return SCPE_IERR; - dptr = find_dev_from_unit(uptr); - if (dptr == NULL) - return SCPE_IERR; - dibp = (DIB *) dptr->ctxt; - - if (dibp == NULL) - return SCPE_IERR; - newchan = get_uint(cptr, 10, 37, &r); - if (r != SCPE_OK) - return r; - if (newchan < 4) /* Lowest channel is 4 */ - return SCPE_ARG; - newchan -= 4; - if (dibp == devs[dibp->channel-4]) - devs[dibp->channel-4] = NULL; - /* If device is disabled, set to whatever the user wants */ - if (dptr->flags & DEV_DIS) { - dibp->channel = newchan; - return SCPE_OK; - } - - if (devs[newchan] != NULL) { - devs[newchan] = dibp; - dibp->channel = newchan+4; - return SCPE_OK; - } else { - fprintf(stderr, "Device already on channel %d\n", newchan+4); - devs[dibp->channel] = dibp; - } - return SCPE_ARG; -} - -/* Print the channel the device is on */ -t_stat -get_chan(FILE *st, UNIT *uptr, int32 v, CONST void *desc) -{ - DEVICE *dptr; - DIB *dibp; - int chan; - - if (uptr == NULL) - return SCPE_IERR; - dptr = find_dev_from_unit(uptr); - if (dptr == NULL) - return SCPE_IERR; - dibp = (DIB *) dptr->ctxt; - if (dibp == NULL) - return SCPE_IERR; - fprintf(st, "Chan=%d", dibp->channel); - return SCPE_OK; -} - -int -get_ccw(int dev, uint32 *addr, uint8 type) { - int cw_addr = 256+4*dev; -#if 0 - cw0 = M[64+dev]; -#endif - cw0 = M[cw_addr]; - cw1 = M[cw_addr+1]; - *addr = cw1; - if (cw0 & WORDCCW) { - if (cw0 & BACKWARD) - cw1 = ((cw1 + M22) & M22) | (cw1 & CMASK); - else - cw1 = ((cw1 + 1) & M22) | (cw1 & CMASK); - } else { - if (cw0 & BACKWARD) { - if (cw1 & CMASK) { - cw1 -= B1; - } else { - cw1 = ((cw1 - 1) & M22) | CMASK; - } - } else { - if ((cw1 & CMASK) == CMASK) { - cw1 = (cw1 + 1) & M22; - } else { - cw1 += B1; - } - } - } - cw0 = ((cw0 - 1) & M15) | (cw0 & CNTMSK); - if ((cw0 & M15) == 0) { - if ((cw0 & (CWRECHARGE|GATHER)) == (CWRECHARGE)) { - cw0 = M[cw_addr+2]; - cw1 = M[cw_addr+3]; - } else if ((cw0 & GATHER) != 0) { - int a; - if ((cw0 & CWRECHARGE) != 0) - M[cw_addr+3] = M[cw_addr+2]; - a = M[cw_addr+3]; - cw0 = M[a & M22]; - cw1 = M[(a + 1) & M22]; - M[cw_addr+3] = ((a + 2) & M22) | (a & CMASK); - } - M[cw_addr] = cw0; - M[cw_addr+1] = cw1; - return (cw0 & M15) == 0; - } - M[cw_addr] = cw0; - M[cw_addr+1] = cw1; - return 0; -} - -/* Hessitation operations */ -void -chan_send_cmd(int dev, uint32 cmd, uint32 *resp) { - DIB *dibp = NULL; - int d = dev & 077; - - if (dev >= 4 && dev <= 36) - dibp = devs[d - 4]; - else if (dev == 3) - dibp = &ctyo_dib; - else if (dev == 2) - dibp = &ctyi_dib; - - *resp = 0; - if (dibp != NULL && dibp->dev_cmd != NULL) { - t_stat r; - - r = (dibp->dev_cmd)((dev & 07700) | cmd, resp); - } -} - -/* Transfer date between device and memory */ -int -chan_input_char(int dev, uint8 *data, int eor) { - int r; - int c; - DIB *dibp = NULL; - uint32 addr; - uint32 mb; - - - /* Figure out DIB to find out type of device */ - if (dev >= 4 && dev <= 36) - dibp = devs[dev - 4]; - else if (dev == 3) - dibp = &ctyo_dib; - else if (dev == 2) - dibp = &ctyi_dib; - - /* Make sure device assigne here */ - if (dibp == NULL || dibp->dev_cmd == NULL) - return -1; - - /* Check if right type */ - if ((dibp->type & WORD_DEV) != 0) - return -2; - - /* Get address of next character. */ - r = get_ccw(dev, &addr, dibp->type); - c = (addr >> 22) & 3; - c = 6 * (3 - c); - mb = M[addr & M22]; - mb &= ~(077 << c); - mb |= ((uint32)(*data) & 077) << c; - M[addr & M22] = mb; - return r; -} - -int -chan_output_char(int dev, uint8 *data, int eor) { - int r; - int c; - DIB *dibp = NULL; - uint32 addr; - uint32 mb; - - - /* Figure out DIB to find out type of device */ - if (dev >= 4 && dev <= 36) - dibp = devs[dev - 4]; - else if (dev == 3) - dibp = &ctyo_dib; - else if (dev == 2) - dibp = &ctyi_dib; - - /* Make sure device assigne here */ - if (dibp == NULL || dibp->dev_cmd == NULL) - return -1; - - /* Check if right type */ - if ((dibp->type & WORD_DEV) != 0) - return -2; - - /* Get address of next character. */ - r = get_ccw(dev, &addr, dibp->type); - c = (addr >> 22) & 3; - c = 6 * (3 - c); - mb = M[addr & M22]; - *data = (uint8)(mb >> c) & 077; - return r; -} - -int -chan_input_word(int dev, uint32 *data, int eor) { - int r; - DIB *dibp = NULL; - uint32 addr; - - - /* Figure out DIB to find out type of device */ - if (dev >= 4 && dev <= 36) - dibp = devs[dev - 4]; - else if (dev == 3) - dibp = &ctyo_dib; - else if (dev == 2) - dibp = &ctyi_dib; - - /* Make sure device assigne here */ - if (dibp == NULL || dibp->dev_cmd == NULL) - return -1; - - /* Check if right type */ - if ((dibp->type & WORD_DEV) == 0) - return -2; - - /* Get address of next word. */ - r = get_ccw(dev, &addr, dibp->type); - M[addr & M22] = *data; - return r; -} - -int -chan_output_word(int dev, uint32 *data, int eor) { - int r; - DIB *dibp = NULL; - uint32 addr; - - - /* Figure out DIB to find out type of device */ - if (dev >= 4 && dev <= 36) - dibp = devs[dev - 4]; - else if (dev == 3) - dibp = &ctyo_dib; - else if (dev == 2) - dibp = &ctyi_dib; - - /* Make sure device assigne here */ - if (dibp == NULL || dibp->dev_cmd == NULL) - return -1; - - /* Check if right type */ - if ((dibp->type & WORD_DEV) == 0) - return -2; - - /* Get address of next word. */ - r = get_ccw(dev, &addr, dibp->type); - *data = M[addr & M22]; - return r; -} - -void -chan_set_done(int dev) { - if (dev < 22) - SR64 |= B2 >> dev; - else - SR65 |= B1 >> (dev - 24); -} - -void -chan_clr_done(int dev) { - if (dev < 22) - SR64 &= ~(B2 >> dev); - else - SR65 &= ~(B1 >> (dev - 24)); -} - - +/* icl1900_stdio.c: ICL 1900 standard I/O devices + + Copyright (c) 2018, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "icl1900_defs.h" + +/* Define style bits */ + +#define OUTPUT B0 /* Input/Output flag */ +#define BACKWARD B1 /* Forward/Backword */ +#define WORDCCW B2 /* Character/Word */ +#define MULTICHN B3 /* Single/Multi Channel */ +#define CWRECHARGE B4 /* Recharge CW when empty */ +#define GATHER B5 /* Gather */ + + +DIB *devs[64]; /* Pointer to device DIB entry */ + +DIB nul_dib = { 0, NULL, NULL, NULL}; + + +t_stat +chan_set_devs() +{ + int i; + int chan; + + /* Clear device table */ + for (i = 0; i < sizeof(devs)/sizeof(DIB *); i++) + devs[i] = NULL; + + /* Add in hidden channels */ + devs[22] = &nul_dib; + devs[23] = &nul_dib; + /* Scan all devices and assign to channel */ + for(i = 0; sim_devices[i] != NULL; i++) { + DIB *dibp = (DIB *) sim_devices[i]->ctxt; + + /* Check if device a channel device */ + if (dibp == NULL) + continue; + /* If device is disabled, don't add it */ + if (sim_devices[i]->flags & DEV_DIS) + continue; + if (dibp->type & MULT_DEV) { + chan = GET_UADDR(sim_devices[i]->flags); + /* Make sure it is in range */ + if (chan < 2 || chan > 36) + continue; + if (devs[chan] != NULL) { + fprintf(stderr, "Conflict between devices %d %s\n", chan, sim_devices[i]->name); + } else { + devs[chan] = dibp; + } + } else { + UNIT *uptr = sim_devices[i]->units; + uint32 unit; + for (unit = 0; unit < sim_devices[i]->numunits; unit++, uptr++) { + /* If disabled, skip it */ + if (uptr->flags & UNIT_DIS) + continue; + chan = GET_UADDR(uptr->flags); + /* Make sure it is in range */ + if (chan < 2 || chan > 36) + continue; + if (devs[chan] != NULL) { + fprintf(stderr, "Conflict between devices %d %s%d\n", chan, sim_devices[i]->name, unit); + } else { + devs[chan] = dibp; + } + } + } + } + return SCPE_OK; +} + + +/* Set the device onto a given channel */ +t_stat +set_chan(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int new_chan; + int cur_chan; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + + if (dibp == NULL) + return SCPE_IERR; + new_chan = get_uint(cptr, 10, 37, &r); + if (r != SCPE_OK) + return r; + if (new_chan < 4) /* Lowest channel is 4 */ + return SCPE_ARG; + /* Find channel device is current on and remove it */ + if (dibp->type & MULT_DEV) { + cur_chan = GET_UADDR(dptr->flags); + } else { + cur_chan = GET_UADDR(uptr->flags); + } + if (dibp == devs[cur_chan]) + devs[cur_chan] = NULL; + /* If device is disabled, set to whatever the user wants */ + if (dptr->flags & DEV_DIS) { + if (dibp->type & MULT_DEV) { + dptr->flags &= ~UNIT_M_ADDR; + dptr->flags |= UNIT_ADDR(new_chan); + } else { + uptr->flags &= ~UNIT_M_ADDR; + uptr->flags |= UNIT_ADDR(new_chan); + } + return SCPE_OK; + } + + if (devs[new_chan] == NULL) { + if (dibp->type & MULT_DEV) { + dptr->flags &= ~UNIT_M_ADDR; + dptr->flags |= UNIT_ADDR(new_chan); + } else { + uptr->flags &= ~UNIT_M_ADDR; + uptr->flags |= UNIT_ADDR(new_chan); + } + devs[new_chan] = dibp; + return SCPE_OK; + } else { + fprintf(stderr, "Device already on channel %d\n", new_chan); + } + return SCPE_ARG; +} + +/* Print the channel the device is on */ +t_stat +get_chan(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int chan; + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + if (dibp->type & MULT_DEV) { + chan = GET_UADDR(dptr->flags); + } else { + chan = GET_UADDR(uptr->flags); + } + fprintf(st, "DEV=%d", chan); + return SCPE_OK; +} + +int +get_ccw(int dev, uint32 *addr, uint8 type) { + int cw_addr; + uint32 cw0; /* Holding registers for current control word */ + uint32 cw1; + + if (io_flags & EXT_IO) { + cw_addr = 256+4*dev; + cw0 = M[cw_addr]; + cw1 = M[cw_addr+1]; + *addr = cw1; + if (cw0 & WORDCCW) { + if (cw0 & BACKWARD) + cw1 = ((cw1 + M22) & M22) | (cw1 & CMASK); + else + cw1 = ((cw1 + 1) & M22) | (cw1 & CMASK); + } else { + if (cw0 & BACKWARD) { + if (cw1 & CMASK) { + cw1 -= B1; + } else { + cw1 = ((cw1 - 1) & M22) | CMASK; + } + } else { + if ((cw1 & CMASK) == CMASK) { + cw1 = (cw1 + 1) & M22; + } else { + cw1 += B1; + } + } + } + cw0 = ((cw0 - 1) & M15) | (cw0 & CNTMSK); + if ((cw0 & M15) == 0) { + if ((cw0 & (CWRECHARGE|GATHER)) == (CWRECHARGE)) { + cw0 = M[cw_addr+2]; + cw1 = M[cw_addr+3]; + } else if ((cw0 & GATHER) != 0) { + int a; + if ((cw0 & CWRECHARGE) != 0) + M[cw_addr+3] = M[cw_addr+2]; + a = M[cw_addr+3]; + cw0 = M[a & M22]; + cw1 = M[(a + 1) & M22]; + M[cw_addr+3] = ((a + 2) & M22) | (a & CMASK); + } + M[cw_addr] = cw0; + M[cw_addr+1] = cw1; + return (cw0 & M15) == 0; + } + M[cw_addr] = cw0; + M[cw_addr+1] = cw1; + } else { + cw_addr = 64+dev; + cw0 = M[cw_addr]; + if (type & WORD_DEV) { + *addr = cw0 & M15; + cw0 = ((cw0 + 1) & M15) | ((cw0 + CNTMSK) & CNTMSK); + cw1 = cw0 & CNTMSK; + } else { + *addr = cw0 & (CMASK|M15); + if ((cw0 & CMASK) == CMASK) { + cw0 = ((cw0 + 1) & M15) | (cw0 & CHCMSK); + } else { + cw0 += B1; + } + cw1 = (cw0 + CHCMSK) & CHCMSK; + cw0 = ((CMASK|M15) & cw0) | cw1; + } + M[cw_addr] = cw0; + if (cw1 == 0) { + if (type & LONG_BLK) { + cw1 = (M[cw_addr + 64] - 1) & FMASK; + M[cw_addr + 64] = cw1; + } + if (type & SPEC_HES) { + M[cw_addr] = M[cw_addr+1]; + return 0; + } + return (cw1 == 0); + } + } + return 0; +} + + +/* Talk to non-standard interface devices */ +void +chan_nsi_cmd(int dev, uint32 cmd) { + DIB *dibp = devs[dev & 077]; + + if (dibp != NULL && dibp->nsi_cmd != NULL) { + (dibp->nsi_cmd)(dev, cmd); + } +} + +/* Talk to non-standard interface devices */ +void +chan_nsi_status(int dev, uint32 *resp) { + DIB *dibp = devs[dev & 077]; + + *resp = 0; + if (dibp != NULL && dibp->nsi_cmd != NULL) { + (dibp->nsi_status)(dev, resp); + } +} + + +/* Hessitation operations */ +void +chan_send_cmd(int dev, uint32 cmd, uint32 *resp) { + DIB *dibp = devs[dev & 077]; + + *resp = 0; + if (dibp != NULL && dibp->si_cmd != NULL) { + (dibp->si_cmd)(dev, cmd, resp); + } +} + +/* Transfer date between device and memory */ +int +chan_input_char(int dev, uint8 *data, int eor) { + DIB *dibp = devs[dev & 077]; + int r; + int c; + uint32 addr; + uint32 mb; + + /* Make sure device assign here */ + if (dibp == NULL) + return -1; + + /* Check if right type */ + if ((dibp->type & WORD_DEV) != 0) + return -2; + + /* Get address of next character. */ + r = get_ccw(dev, &addr, dibp->type); + c = (addr >> 22) & 3; + c = 6 * (3 - c); + addr &= M22; + if (addr < 8) + mb = XR[addr]; + else + mb = M[addr]; + mb &= ~(077 << c); + mb |= ((uint32)(*data) & 077) << c; + if (addr < 8) + XR[addr] = mb; + else + M[addr] = mb; + return r; +} + +int +chan_output_char(int dev, uint8 *data, int eor) { + DIB *dibp = devs[dev & 077]; + int r; + int c; + uint32 addr; + uint32 mb; + + /* Make sure device assigne here */ + if (dibp == NULL) + return -1; + + /* Check if right type */ + if ((dibp->type & WORD_DEV) != 0) + return -2; + + /* Get address of next character. */ + r = get_ccw(dev, &addr, dibp->type); + c = (addr >> 22) & 3; + c = 6 * (3 - c); + addr &= M22; + if (addr < 8) + mb = XR[addr]; + else + mb = M[addr]; + *data = (uint8)(mb >> c) & 077; + return r; +} + +int +chan_input_word(int dev, uint32 *data, int eor) { + DIB *dibp = devs[dev & 077]; + int r; + uint32 addr; + + /* Make sure device assigne here */ + if (dibp == NULL) + return -1; + + /* Check if right type */ + if ((dibp->type & WORD_DEV) == 0) + return -2; + + /* Get address of next word. */ + r = get_ccw(dev, &addr, dibp->type); + addr &= M22; + if (addr < 8) + XR[addr] = *data; + else + M[addr] = *data; + return r; +} + +int +chan_output_word(int dev, uint32 *data, int eor) { + DIB *dibp = devs[dev & 077]; + int r; + uint32 addr; + + /* Make sure device assigne here */ + if (dibp == NULL) + return -1; + + /* Check if right type */ + if ((dibp->type & WORD_DEV) == 0) + return -2; + + /* Get address of next word. */ + r = get_ccw(dev, &addr, dibp->type); + addr &= M22; + if (addr < 8) + *data = XR[addr]; + else + *data = M[addr]; + return r; +} + +void +chan_set_done(int dev) { + if (dev < 22) + SR64 |= B2 >> dev; + else + SR65 |= B1 >> (dev - 24); +} + +void +chan_clr_done(int dev) { + if (dev < 22) + SR64 &= ~(B2 >> dev); + else + SR65 &= ~(B1 >> (dev - 24)); +} + + diff --git a/ICL1900/icl1900_sys.c b/ICL1900/icl1900_sys.c index 5265c81..0cbc264 100755 --- a/ICL1900/icl1900_sys.c +++ b/ICL1900/icl1900_sys.c @@ -46,7 +46,13 @@ int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, - &cty_dev, + &cty_dev, /* Must be first device after CPU */ +#if NUM_DEVS_PTR > 0 + &ptr_dev, +#endif +#if NUM_DEVS_PTP > 0 + &ptp_dev, +#endif #if NUM_DEVS_CDR > 0 &cdr_dev, #endif @@ -99,7 +105,7 @@ uint8 mem_to_ascii[64] = { /* x0 x1 x2 x3 x4 x5 x6 x7 */ '0', '1', '2', '3', '4', '5', '6', '7', /* 0x */ '8', '9', ':', ';', '<', '=', '>', '?', /* 1x */ - ' ', '!', '"', '#', '{', '%', '&', '\'', /* 2x */ + ' ', '!', '"', '#', '\\', '%', '&', '\'', /* 2x */ '(', ')', '*', '+', ',', '-', '.', '/', /* 3x */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 4x */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 5x */ @@ -132,7 +138,7 @@ const char ascii_to_mem[128] = { /* P Q R S T U V W */ 060, 061, 062, 063, 064, 065, 066, 067, /* X Y Z [ \ ] ^ _ */ - 070, 071, 072, 073, -1, 075, 076, 077, + 070, 071, 072, 073, 024, 075, 076, 077, /* ` a b c d e f g */ -1, 041, 042, 043, 044, 045, 046, 047, /* 14 -1 177 */ /* h i j k l m n o */ @@ -155,7 +161,33 @@ sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) uint8 image[80]; int checksum; - if (match_ext(fnam, "txt")) { + if (match_ext(fnam, "card")) { + + addr = 0; + while (fgets(buffer, 100, fileref)) { + /* Convert bits into image */ + memset(image, 0, sizeof(image)); + for (j = 0; j < 80; j++) { + if (buffer[j] == '\n') + break; + image[j] = ascii_to_mem[buffer[j]]; + if (image[j] < 0) { + fprintf(stderr, "Char %c: %s", buffer[j], buffer); + return SCPE_FMT; + } + } + for (j = 0; j < 64; ) { + data = 0; + for (k = 0; k < 4; k++) + data = (data << 6) | image[j++]; + fprintf(stderr, "Addr: %06o %08o\n\r", addr, data); + if (addr < 8) + XR[addr++] = data; +else + M[addr++] = data; + } + } + } else if (match_ext(fnam, "txt")) { while (fgets(buffer, 100, fileref)) { /* Convert bits into image */ @@ -201,9 +233,31 @@ sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) break; case 2: case 3: + checksum = 0; + for (j = 0; j < 4; j++) + checksum = (checksum << 6) | image[j]; + addr = 0; + for (; j < 8; j++) + addr = (addr << 6) | image[j]; + checksum += addr; + RC = addr; + data = 0; + for (i = 3; i < image[1]; i++) { + data = 0; + for (k = 0; k < 4; k++) + data = (data << 6) | image[j++]; + checksum += data; + } + for (k = 0; k < 4; k++) + data = (data << 6) | image[j++]; + data = FMASK & (checksum + data); + if (data != 0) + fprintf(stderr, "Check %08o %08o: %s", addr, data, buffer); + break; case 4: case 5: case 6: + fprintf(stderr, "%o %c%c%c%c\n", image[3],buffer[4], buffer[5], buffer[6], buffer[7]); break; default: fprintf(stderr, "B? :%s", buffer); diff --git a/ICL1900/icl1900_tp.c b/ICL1900/icl1900_tp.c new file mode 100644 index 0000000..87fe2bf --- /dev/null +++ b/ICL1900/icl1900_tp.c @@ -0,0 +1,459 @@ +/* icl1900_tp.c: ICL1900 Paper Tape Punch simulator + + Copyright (c) 2018, Richard Cornwell + + 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 + RICHARD CORNWELL 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 Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell + +*/ + +#include "icl1900_defs.h" + +#ifndef NUM_DEVS_PTP +#define NUM_DEVS_PTP 0 +#endif + +#define PP_V_MODE (UNIT_V_UF + 0) +#define PP_M_MODE (1 << PP_V_MODE) +#define UNIT_V_TYPE (UNIT_V_UF + 1) +#define UNIT_TYPE (0xf << UNIT_V_TYPE) +#define GET_TYPE(x) ((UNIT_TYPE & (x)) >> UNIT_V_TYPE) +#define SET_TYPE(x) (UNIT_TYPE & ((x) << UNIT_V_TYPE)) + +#define NSI_TYPE(x) ((GET_TYPE(x) & 1) == 0) +#define SI_TYPE(x) ((GET_TYPE(x) & 1) != 0) +#define PP_MODE_7B 0 +#define PP_MODE_7P 1 + +#define CMD u3 +#define STATUS u4 +#define HOLD u5 + +#define ALPHA_MODE 0001 +#define BETA_MODE 0000 +#define BIN_MODE 0002 +#define PUN_BLNK 0004 +#define DISC 0010 +#define BUSY 0020 +#define DELTA_MODE 0040 + +#define TERMINATE 001 +#define OPAT 002 +#define ERROR 004 +#define ACCEPT 020 + +#define ALPHA_SHIFT 074 +#define BETA_SHIFT 075 +#define DELTA_SHIFT 076 + +#if (NUM_DEVS_PTP > 0) + +#define T1925_1 0 +#define T1925_2 1 +#define T1926_1 2 +#define T1926_2 3 + +#define UNIT_PTP(x) UNIT_ADDR(x)|SET_TYPE(T1925_2)|UNIT_ATTABLE|\ + UNIT_DISABLE|UNIT_TEXT|TT_MODE_7B + +/* + * Character translation. + * + * Alpha shift 074 + * Beta shift 075 + * Delta shift 076 + * + * p000xxxx Delta + 01xxxx + * p001xxxx Delta + 00xxxx + * p10111xx Delta + 1101xx + * p11111xx Delta + 1110xx + * p010xxxx 01xxxx + * p011xxxx 00xxxx + * p100xxxx Alpha + 10xxxx + * p101xxxx Alpha + 11xxxx xxxx < 4 + * p110xxxx Beta + 10xxxx + * p111xxxx Beta + 11xxxx xxxx < 4 + * + * Two modes Alpha and Beta. Delta is always output. + * + * Graphics mode translation. + * + * p010xxxx 01xxxx + * p011xxxx 00xxxx + * p100xxxx 10xxxx + * p101xxxx 11xxxx + * p110xxxx 10xxxx + * p111xxxx 11xxxx + * + */ + +void ptp_cmd (int dev, uint32 cmd, uint32 *resp); +void ptp_nsi_cmd (int dev, uint32 cmd); +void ptp_nsi_status (int dev, uint32 *resp); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +CONST char *ptp_description (DEVICE *dptr); + +DIB ptp_dib = { CHAR_DEV, &ptp_cmd, &ptp_nsi_cmd, &ptp_nsi_status }; + +UNIT ptp_unit[] = { + { UDATA (&ptp_svc, UNIT_PTP(6), 0), 10000 }, + { UDATA (&ptp_svc, UNIT_PTP(7), 0), 10000 }, + }; + + +MTAB ptp_mod[] = { + { PP_M_MODE, PP_MODE_7B, "7b", "7B", NULL }, + { PP_M_MODE, PP_MODE_7P, "7p", "7P", NULL }, + { UNIT_TYPE, SET_TYPE(T1925_1), "1925/1", "1925/1", NULL, NULL, "ICL 1925/1 NSI 300CPM punch."}, + { UNIT_TYPE, SET_TYPE(T1925_2), "1925/2", "1925/2", NULL, NULL, "ICL 1922/2 SI 300CPM punch."}, + { UNIT_TYPE, SET_TYPE(T1926_1), "1926/1", "1926/1", NULL, NULL, "ICL 1926/1 NSI 1000CPM punch."}, + { UNIT_TYPE, SET_TYPE(T1926_2), "1926/2", "1926/2", NULL, NULL, "ICL 1926/2 SI 1000CPM punch."}, + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "DEV", "DEV", &set_chan, &get_chan, NULL, "Device Number"}, + { 0 } + }; + +DEVICE ptp_dev = { + "TP", ptp_unit, NULL, ptp_mod, + NUM_DEVS_PTP, 8, 22, 1, 8, 22, + NULL, NULL, &ptp_reset, NULL, &attach_unit, &detach_unit, + &ptp_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &ptp_help, NULL, NULL, &ptp_description + }; + +/* + * Command codes + * + * 001xxx Write + * 001xx0 Start in current shift + * 001xx1 Start in alpha shift + * 001x1x Graphics + * 001x0x BCD. + * 0011xx Punch Blank characters. + * 0010xx Punch characters. + * 010000 Send Q. + * 010100 Send P. + * 011110 Disconnect. + */ + + +void ptp_cmd(int dev, uint32 cmd, uint32 *resp) { + int i; + UNIT *uptr = NULL; + + *resp = 0; + /* Find the unit from dev */ + for (i = 0; i < NUM_DEVS_PTP; i++) { + if (GET_UADDR(ptp_unit[i].flags) == dev) { + uptr = &ptp_unit[i]; + break; + } + } + + /* Should not happen, but just in case */ + if (uptr == NULL) + return; + + /* Ignore this command if not a SI device */ + if (NSI_TYPE(uptr->flags)) + return; + + if ((uptr->flags & UNIT_ATT) == 0) + return; + + cmd &= 077; + switch(cmd & 070) { + case 010: /* Command */ + if ((uptr->flags & UNIT_ATT) == 0) + break; + if (uptr->CMD & BUSY) { + *resp = 3; + break; + } + if (cmd & 1) + uptr->CMD = 0; + uptr->CMD &= DELTA_MODE|1; + uptr->CMD |= BUSY | (cmd & 07); + uptr->STATUS = 0; + sim_activate(uptr, uptr->wait); + chan_clr_done(GET_UADDR(uptr->flags)); + *resp = 5; + break; + + case 020: if (cmd == 020) { /* Send Q */ + if ((uptr->flags & UNIT_ATT) == 0) + *resp = 040; + if (uptr->STATUS & 06) + *resp = 040; + *resp |= uptr->STATUS & TERMINATE; + } else if (cmd == 024) { /* Send P */ + if ((uptr->flags & UNIT_ATT) != 0) + *resp = (uptr->STATUS & ERROR) | 1; + uptr->STATUS = 0; + chan_clr_done(GET_UADDR(uptr->flags)); + } + break; + + case 030: if (cmd == 036) { /* Disconnect */ + uptr->CMD |= DISC; + *resp = 5; + } + break; + + default: + break; + } +} + +/* + * Command codes + * + * xxxx01 Start punch. + * xxxx10 Stop punch. + * xx1xxx Start in Previous shift, else alpha. + * x1xxxx Graphics mode. + * 1xxxxx Punch blanks. + */ +void ptp_nsi_cmd(int dev, uint32 cmd) { + int i; + UNIT *uptr = NULL; + + /* Find the unit from dev */ + for (i = 0; i < NUM_DEVS_PTP; i++) { + if (GET_UADDR(ptp_unit[i].flags) == dev) { + uptr = &ptp_unit[i]; + break; + } + } + + /* Should not happen, but just in case */ + if (uptr == NULL) + return; + + /* Ignore this command if not a SI device */ + if (SI_TYPE(uptr->flags)) + return; + + + if (cmd & 02) { + if (uptr->CMD & BUSY) + uptr->CMD |= DISC; + return; + } + + if (cmd & 01) { + if (uptr->CMD & BUSY || (uptr->flags & UNIT_ATT) == 0) { + uptr->STATUS |= OPAT; + chan_set_done(GET_UADDR(uptr->flags)); + return; + } + if (cmd & 010) + uptr->CMD &= ALPHA_MODE; + else + uptr->CMD = ALPHA_MODE; + if (cmd & 020) + uptr->CMD |= BIN_MODE; + if ((cmd & 040) == 0) + uptr->CMD |= PUN_BLNK; + uptr->CMD |= BUSY; + uptr->STATUS = 0; + sim_activate(uptr, uptr->wait); + chan_clr_done(GET_UADDR(uptr->flags)); + } +} + +/* + * NSI Status bits. + * + * 001 End. + * 002 Opat. + * 004 ERROR + * 020 ACCEPT + * 040 BUSY + */ +void ptp_nsi_status(int dev, uint32 *resp) { + int i; + UNIT *uptr = NULL; + + *resp = 0; + /* Find the unit from dev */ + for (i = 0; i < NUM_DEVS_PTP; i++) { + if (GET_UADDR(ptp_unit[i].flags) == dev) { + uptr = &ptp_unit[i]; + break; + } + } + + /* Should not happen, but just in case */ + if (uptr == NULL) + return; + + /* Ignore this command if not a SI device */ + if (SI_TYPE(uptr->flags)) + return; + + *resp = uptr->STATUS; + if (uptr->CMD & BUSY) + *resp |= 040; + uptr->STATUS = 0; + chan_clr_done(GET_UADDR(uptr->flags)); +} + + + +t_stat ptp_svc (UNIT *uptr) +{ + t_stat r; + uint8 ch; + int data; + int eor; + + /* Handle a disconnect request */ + if (uptr->CMD & DISC) { + uptr->CMD &= 1; + chan_set_done(GET_UADDR(uptr->flags)); + return SCPE_OK; + } + /* If not busy, false schedule, just exit */ + if ((uptr->CMD & BUSY) == 0) + return SCPE_OK; + + /* Check if attached */ + if ((uptr->flags & UNIT_ATT) == 0) { + uptr->CMD &= 1; + uptr->STATUS = TERMINATE; + chan_set_done(GET_UADDR(uptr->flags)); + return SCPE_OK; + } + + eor = chan_output_char(GET_UADDR(uptr->flags), &ch, 0); + if ((uptr->CMD & PUN_BLNK) != 0) { + data = 0400; + } else if (uptr->CMD & BIN_MODE) { + data = ch & 017; + switch (ch & 060) { + case 0000: data |= 060; break; + case 0020: data |= 040; break; + case 0040: data |= 0100; break; + case 0060: data |= 0120; break; + } + } else { + data = 0; + if (ch == ALPHA_SHIFT) { + uptr->CMD &= BUSY|DISC|BIN_MODE|PUN_BLNK; + uptr->CMD |= ALPHA_MODE; + } else if (ch == BETA_SHIFT) { + uptr->CMD &= BUSY|DISC|BIN_MODE|PUN_BLNK; + uptr->CMD |= BETA_MODE; + } else if (ch == DELTA_SHIFT) { + uptr->CMD &= BUSY|DISC|BIN_MODE|PUN_BLNK|ALPHA_SHIFT; + uptr->CMD |= DELTA_MODE; + } else if (ch == 077) { + uptr->CMD &= BUSY|DISC|BIN_MODE|PUN_BLNK|ALPHA_SHIFT; + } else { + if (uptr->CMD & DELTA_MODE) { + uptr->CMD &= ~DELTA_MODE; + data = ch; + if (ch & 040) + data |= 0174 ^ ((ch & 020) << 1); + } else if (uptr->CMD & ALPHA_MODE) { + data = ch & 017; + switch (ch & 060) { + case 0000: data |= 060; break; + case 0020: data |= 040; break; + case 0040: data |= 0100; break; + case 0060: data |= 0120; break; + } + } else { + data = ch & 017; + switch (ch & 060) { + case 0000: data |= 060; break; + case 0020: data |= 040; break; + case 0040: data |= 0140; break; + case 0060: data |= 0160; break; + } + } + } + } + if (data != 0) { + /* Check parity is even */ + if ((uptr->flags & PP_M_MODE) == PP_MODE_7P) { + data &= 0177; + ch = data ^ (data << 4); + ch = ch ^ (ch << 2); + ch = ch ^ (ch << 1); + data |= ch; + } + fputc(data, uptr->fileref); + uptr->pos = ftell(uptr->fileref); + if (ferror (uptr->fileref)) { + uptr->STATUS |= TERMINATE|ERROR; + uptr->CMD &= DELTA_MODE|1; + chan_set_done(GET_UADDR(uptr->flags)); + return SCPE_OK; + } + } + /* Check if Done */ + if (eor) { + uptr->STATUS |= TERMINATE; + uptr->CMD &= DELTA_MODE|1; + chan_set_done(GET_UADDR(uptr->flags)); + return SCPE_OK; + } + sim_activate (uptr, uptr->wait); /* try again */ + return SCPE_OK; +} + + +/* Reset */ + +t_stat ptp_reset (DEVICE *dptr) +{ + UNIT *uptr = dptr->units; + int unit; + + for (unit = 0; unit < NUM_DEVS_PTP; unit++, uptr++) { + uptr->CMD = ALPHA_MODE; + uptr->STATUS = 0; + uptr->HOLD = 0; + chan_clr_done(GET_UADDR(uptr->flags)); + } + return SCPE_OK; +} + + +t_stat ptp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "The Paper Tape Punch can be set to one of two modes: 7P, or 7B.\n\n"); +fprintf (st, " mode \n"); +fprintf (st, " 7P Process even parity input tapes. \n"); +fprintf (st, " 7B Ignore parity of input data.\n"); +fprintf (st, "The default mode is 7B.\n"); +return SCPE_OK; +} + +CONST char *ptp_description (DEVICE *dptr) +{ + return "PTP"; + +} +#endif diff --git a/ICL1900/icl1900_tr.c b/ICL1900/icl1900_tr.c new file mode 100644 index 0000000..85aa35e --- /dev/null +++ b/ICL1900/icl1900_tr.c @@ -0,0 +1,524 @@ +/* icl1900_tr.c: ICL1900 Paper Tape Reader simulator + + Copyright (c) 2018, Richard Cornwell + + 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 + RICHARD CORNWELL 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 Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell + +*/ + +#include "icl1900_defs.h" + +#ifndef NUM_DEVS_PTR +#define NUM_DEVS_PTR 0 +#endif + +#define PP_V_MODE (UNIT_V_UF + 0) +#define PP_M_MODE (1 << PP_V_MODE) +#define UNIT_V_TYPE (UNIT_V_UF + 1) +#define UNIT_TYPE (0xf << UNIT_V_TYPE) +#define GET_TYPE(x) ((UNIT_TYPE & (x)) >> UNIT_V_TYPE) +#define SET_TYPE(x) (UNIT_TYPE & ((x) << UNIT_V_TYPE)) + +#define NSI_TYPE(x) ((GET_TYPE(x) & 1) == 0) +#define SI_TYPE(x) ((GET_TYPE(x) & 1) != 0) +#define PP_MODE_7B 0 +#define PP_MODE_7P 1 + + +#define CMD u3 +#define STATUS u4 +#define HOLD u5 + +#define ALPHA_MODE 0001 +#define BETA_MODE 0000 +#define BIN_MODE 0002 +#define IGN_BLNK 0004 +#define DISC 0010 +#define BUSY 0020 +#define DELTA_MODE 0040 +#define STOP_CHAR 0100 + +#define TERMINATE 001 +#define OPAT 002 +#define ERROR 004 +#define ACCEPT 020 + +#define ALPHA_SHIFT 074 +#define BETA_SHIFT 075 +#define DELTA_SHIFT 076 + +#if (NUM_DEVS_PTR > 0) + +#define T1915_1 0 +#define T1915_2 1 +#define T1916_1 2 +#define T1916_2 3 + +#define UNIT_PTR(x) UNIT_ADDR(x)|SET_TYPE(T1915_2)|UNIT_ATTABLE|\ + UNIT_DISABLE|UNIT_TEXT|UNIT_RO|TT_MODE_7B + +/* + * Character translation. + * + * Alpha shift 074 + * Beta shift 075 + * Delta shift 076 + * + * p000xxxx Delta + 01xxxx + * p001xxxx Delta + 00xxxx + * p10111xx Delta + 1101xx + * p11111xx Delta + 1110xx + * p010xxxx 01xxxx + * p011xxxx 00xxxx + * p100xxxx Alpha + 10xxxx + * p101xxxx Alpha + 11xxxx xxxx < 4 + * p110xxxx Beta + 10xxxx + * p111xxxx Beta + 11xxxx xxxx < 4 + * + * Two modes Alpha and Beta. Delta is always output. + * + * Graphics mode translation. + * + * p010xxxx 01xxxx + * p011xxxx 00xxxx + * p100xxxx 10xxxx + * p101xxxx 11xxxx + * p110xxxx 10xxxx + * p111xxxx 11xxxx + * + */ + +void ptr_cmd (int dev, uint32 cmd, uint32 *resp); +void ptr_nsi_cmd (int dev, uint32 cmd); +void ptr_nsi_status (int dev, uint32 *resp); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unit_num, DEVICE * dptr); +t_stat ptr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +CONST char *ptr_description (DEVICE *dptr); + +DIB ptr_dib = { CHAR_DEV, &ptr_cmd, &ptr_nsi_cmd, &ptr_nsi_status }; + +UNIT ptr_unit[] = { + { UDATA (&ptr_svc, UNIT_PTR(4), 0), 10000 }, + { UDATA (&ptr_svc, UNIT_PTR(5), 0), 10000 }, + }; + + +MTAB ptr_mod[] = { + { PP_M_MODE, PP_MODE_7B, "7b", "7B", NULL }, + { PP_M_MODE, PP_MODE_7P, "7p", "7P", NULL }, + { UNIT_TYPE, SET_TYPE(T1915_1), "1915/1", "1915/1", NULL, NULL, "ICL 1915/1 NSI 300CPM reader."}, + { UNIT_TYPE, SET_TYPE(T1915_2), "1915/2", "1915/2", NULL, NULL, "ICL 1912/2 SI 300CPM reader."}, + { UNIT_TYPE, SET_TYPE(T1916_1), "1916/1", "1916/1", NULL, NULL, "ICL 1916/1 NSI 1000CPM reader."}, + { UNIT_TYPE, SET_TYPE(T1916_2), "1916/2", "1916/2", NULL, NULL, "ICL 1916/2 SI 1000CPM reader."}, + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "DEV", "DEV", &set_chan, &get_chan, NULL, "Device Number"}, + { 0 } + }; + +DEVICE ptr_dev = { + "TR", ptr_unit, NULL, ptr_mod, + NUM_DEVS_PTR, 8, 22, 1, 8, 22, + NULL, NULL, &ptr_reset, &ptr_boot, &attach_unit, &detach_unit, + &ptr_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &ptr_help, NULL, NULL, &ptr_description + }; + +/* + * Command codes + * + * 001xxx Read + * 001xx0 Start in current shift + * 001xx1 Start in alpha shift + * 001x1x Graphics + * 001x0x BCD. + * 0011xx Ignore blank tape and erase + * 0010xx Read Blank and erase + * 010000 Send Q. + * 010100 Send P. + * 011110 Disconnect. + */ + + +void ptr_cmd(int dev, uint32 cmd, uint32 *resp) { + int i; + UNIT *uptr = NULL; + + *resp = 0; + /* Find the unit from dev */ + for (i = 0; i < NUM_DEVS_PTR; i++) { + if (GET_UADDR(ptr_unit[i].flags) == dev) { + uptr = &ptr_unit[i]; + break; + } + } + + /* Should not happen, but just in case */ + if (uptr == NULL) + return; + + /* Ignore this command if not a SI device */ + if (NSI_TYPE(uptr->flags)) + return; + + if ((uptr->flags & UNIT_ATT) == 0) + return; + cmd &= 077; + switch(cmd & 070) { + case 010: /* Command */ + if ((uptr->flags & UNIT_ATT) == 0) + break; + if (uptr->CMD & BUSY) { + *resp = 3; + break; + } + sim_debug(DEBUG_CMD, &ptr_dev, "CMD: %03o %03o %03o\n", cmd, uptr->CMD, uptr->STATUS); + uptr->CMD &= 1; + uptr->CMD |= BUSY| (cmd & 07); + uptr->STATUS = 0; + sim_activate(uptr, uptr->wait); + chan_clr_done(GET_UADDR(uptr->flags)); + *resp = 5; + break; + + case 020: if (cmd == 020) { /* Send Q */ + if ((uptr->flags & UNIT_ATT) != 0) + *resp = 040; + if (uptr->STATUS & 06) + *resp = 040; + *resp |= uptr->STATUS & TERMINATE; + } else if (cmd == 024) { /* Send P */ + if ((uptr->flags & UNIT_ATT) != 0) + *resp = (uptr->STATUS & ERROR) | 1; + uptr->STATUS = 0; + chan_clr_done(GET_UADDR(uptr->flags)); + } + break; + + case 030: if (cmd == 036) { /* Disconnect */ + uptr->CMD |= DISC; + *resp = 5; + } + break; + + default: + break; + } +} + +/* + * Command codes + * + * xxxx01 Start reader. + * xxxx10 Stop reader. + * xxx1xx Stop on return, else only stop on count. + * xx1xxx Start in Previous shift, else alpha. + * x1xxxx Graphics mode. + * 1xxxxx All characters. + */ +void ptr_nsi_cmd(int dev, uint32 cmd) { + int i; + UNIT *uptr = NULL; + + /* Find the unit from dev */ + for (i = 0; i < NUM_DEVS_PTR; i++) { + if (GET_UADDR(ptr_unit[i].flags) == dev) { + uptr = &ptr_unit[i]; + break; + } + } + + /* Should not happen, but just in case */ + if (uptr == NULL) + return; + + /* Ignore this command if not a SI device */ + if (SI_TYPE(uptr->flags)) + return; + + + if (cmd & 02) { + if (uptr->CMD & BUSY) + uptr->CMD |= DISC; + return; + } + + if (cmd & 01) { + if (uptr->CMD & BUSY || (uptr->flags & UNIT_ATT) == 0) { + uptr->STATUS |= OPAT; + chan_set_done(GET_UADDR(uptr->flags)); + return; + } + if (cmd & 010) + uptr->CMD &= ALPHA_MODE; + else + uptr->CMD = ALPHA_MODE; + if (cmd & 004) + uptr->CMD |= STOP_CHAR; + if (cmd & 020) + uptr->CMD |= BIN_MODE; + if ((cmd & 040) == 0) + uptr->CMD |= IGN_BLNK; + uptr->CMD |= BUSY; + uptr->STATUS = 0; + sim_activate(uptr, uptr->wait); + chan_clr_done(GET_UADDR(uptr->flags)); + } +} + +/* + * NSI Status bits. + * + * 001 End. + * 002 Opat. + * 004 ERROR + * 020 ACCEPT + * 040 BUSY + */ +void ptr_nsi_status(int dev, uint32 *resp) { + int i; + UNIT *uptr = NULL; + + *resp = 0; + /* Find the unit from dev */ + for (i = 0; i < NUM_DEVS_PTR; i++) { + if (GET_UADDR(ptr_unit[i].flags) == dev) { + uptr = &ptr_unit[i]; + break; + } + } + + /* Should not happen, but just in case */ + if (uptr == NULL) + return; + + /* Ignore this command if not a SI device */ + if (SI_TYPE(uptr->flags)) + return; + + *resp = uptr->STATUS; + if (uptr->CMD & BUSY) + *resp |= 040; + uptr->STATUS = 0; + chan_clr_done(GET_UADDR(uptr->flags)); +} + +t_stat ptr_svc (UNIT *uptr) +{ + t_stat r; + uint8 ch; + uint8 shift = 0; + int data; + int eor; + + /* Handle a disconnect request */ + if (uptr->CMD & DISC) { + uptr->CMD &= 1; + chan_set_done(GET_UADDR(uptr->flags)); + return SCPE_OK; + } + /* If not busy, false schedule, just exit */ + if ((uptr->CMD & BUSY) == 0) + return SCPE_OK; + if (uptr->HOLD != 0) { + ch = uptr->HOLD & 077; + uptr->HOLD = 0; + eor = chan_input_char(GET_UADDR(uptr->flags), &ch, 0); + if (eor) { + uptr->CMD &= 1; + chan_set_done(GET_UADDR(uptr->flags)); + uptr->STATUS = TERMINATE; + return SCPE_OK; + } + if ((uptr->CMD & STOP_CHAR) != 0 && uptr->HOLD == 032) + uptr->STATUS |= TERMINATE; + } + + /* Read next charater */ + if ((uptr->flags & UNIT_ATT) == 0 || + feof(uptr->fileref) || + (data = getc (uptr->fileref)) == EOF) { + uptr->CMD &= 1; + chan_set_done(GET_UADDR(uptr->flags)); + uptr->STATUS = TERMINATE; + return SCPE_OK; + } + + sim_debug(DEBUG_DATA, &ptr_dev, "data: %03o\n", data); + /* Check parity is even */ + if ((uptr->flags & PP_M_MODE) == PP_MODE_7P) { + ch = data ^ (data >> 4); + ch = ch ^ (ch >> 2); + ch = ch ^ (ch >> 1); + if (ch != 0) + uptr->STATUS = TERMINATE | ERROR; + chan_set_done(GET_UADDR(uptr->flags)); + } + data &= 0177; + if ((data == 0 || data == 0177) && (uptr->CMD & IGN_BLNK) != 0) { + sim_activate (uptr, uptr->wait); /* try again */ + return SCPE_OK; + } + + if (uptr->CMD & BIN_MODE) { + switch (data & 0160) { + case 0000: + case 0020: /* Terminate */ + uptr->STATUS |= TERMINATE; + chan_set_done(GET_UADDR(uptr->flags)); + uptr->CMD &= 1; + return SCPE_OK; + case 0040: + ch = 020 | (data & 017); + break; + case 0060: + ch = 000 | (data & 017); + break; + case 0100: + case 0140: + ch = 040 | (data & 017); + break; + case 0120: + case 0160: + ch = 060 | (data & 017); + break; + } + } else { + switch (data & 0160) { + case 0000: + case 0020: + if ((uptr->CMD & STOP_CHAR) != 0 && data == 012) + uptr->STATUS |= TERMINATE; + shift = DELTA_SHIFT; + break; + case 0040: + ch = 020 | (data & 017); + break; + case 0060: + ch = 000 | (data & 017); + break; + case 0140: + if ((data & 017) > 013) { + shift = DELTA_SHIFT; + ch = 070 | (data & 03); + break; + } + case 0100: + if ((uptr->CMD & 1) == BETA_MODE) + shift = ALPHA_SHIFT; + uptr->CMD |= ALPHA_MODE; + ch = 040 | (data & 037); + break; + case 0160: + if ((data & 017) > 013) { + shift = DELTA_SHIFT; + ch = 064 | (data & 03); + break; + } + case 0120: + if ((uptr->CMD & 1) == ALPHA_MODE) + shift = BETA_SHIFT; + uptr->CMD &= ~ALPHA_MODE; + ch = 040 | (data & 037); + break; + } + } + /* Check if error */ + if (shift != 0) { + eor = chan_input_char(GET_UADDR(uptr->flags), &shift, 0); + if (eor) { + uptr->STATUS |= TERMINATE; + chan_set_done(GET_UADDR(uptr->flags)); + uptr->CMD &= 1; + uptr->HOLD = 0100 | ch; + return SCPE_OK; + } + } + eor = chan_input_char(GET_UADDR(uptr->flags), &ch, 0); + if (eor) { + uptr->STATUS |= TERMINATE; + chan_set_done(GET_UADDR(uptr->flags)); + uptr->CMD &= 1; + return SCPE_OK; + } + if (uptr->STATUS & TERMINATE) { + chan_set_done(GET_UADDR(uptr->flags)); + uptr->CMD &= 1; + return SCPE_OK; + } + sim_activate (uptr, uptr->wait); /* try again */ + return SCPE_OK; +} + + +/* Reset */ + +t_stat ptr_reset (DEVICE *dptr) +{ + UNIT *uptr = dptr->units; + int unit; + + for (unit = 0; unit < NUM_DEVS_PTR; unit++, uptr++) { + uptr->CMD = ALPHA_MODE; + uptr->STATUS = 0; + uptr->HOLD = 0; + chan_clr_done(GET_UADDR(uptr->flags)); + } + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +ptr_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + int chan = GET_UADDR(uptr->flags); + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + M[64 + chan] = 0; + M[256 + 4 * chan] = 0; + M[257 + 4 * chan] = 020; + loading = 1; + uptr->CMD = BUSY|ALPHA_MODE|BIN_MODE|IGN_BLNK; + sim_activate (uptr, uptr->wait); + return SCPE_OK; +} + + +t_stat ptr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "The Paper Tape Reader can be set to one of twp modes: 7P, or 7B.\n\n"); +fprintf (st, " mode \n"); +fprintf (st, " 7P Process even parity input tapes. \n"); +fprintf (st, " 7B Ignore parity of input data.\n"); +fprintf (st, "The default mode is 7B.\n"); +return SCPE_OK; +} + +CONST char *ptr_description (DEVICE *dptr) +{ + return "PTR"; + +} +#endif