/* sds_cr.c: SDS-930 card reader simulator Copyright (c) 2020, Ken Rector 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 KEN RECTOR 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 Ken Rector shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Ken Rector. 03-Mar-20 kenr Initial Version 10-Mar-23 kenr Reset cr_eor on disconnect */ /* This card reader simulator uses sim_card.c to attach and read input records in CBN format. When BCD mode is specified by the buffer control EOM, input data is translated from the Hollerith encoded data in the card columns to the SDS Internal Code as defined by the SDS 930 Computer Reference Manual. The translation function was modified from the sim_card.c code to provide SDS Internal BCD codes. The card reader delays the disconnect after the last character until the trailing edge of the card is detected. In this simulator, this delay is accomplished by scheduling a final service request after the last characters have been delivered too the channel. The timing for this service has been adjusted to handle some example SDS programs. Too long a delay causes errors in some, too short a delay affects others. */ #include "sds_defs.h" #include "sim_card.h" #define FEEDING 00001000 /* feeding card to read station */ #define READING 00004000 /* Card at read station */ #define STATUS u3 /* status */ #define CARD_RDY(u) (sim_card_input_hopper_count(u) > 0 || \ sim_card_eof(u) == 1) extern uint32 xfr_req; extern int32 stop_invins, stop_invdev, stop_inviop; extern uint8 chan_cpw[NUM_CHAN]; /* char per word */ extern uint8 chan_cnt[NUM_CHAN]; /* char count */ int32 cr_bptr = 0; /* buf ptr */ int32 cr_blnt = 0; /* buf length */ int32 cr_chr = 0; /* char no.*/ int32 cr_inst = 0; /* saved instr */ int32 cr_eor = 0; /* end of record */ uint16 cr_buffer[80]; /* card record */ DSPT cr_tplt[] = {{1,0},{0,0}}; /* template */ t_stat cr_svc(UNIT *); t_stat cr_boot(int32, DEVICE *); t_stat cr_reset(DEVICE *); t_stat cr_attach(UNIT *, CONST char *); t_stat cr_detach(UNIT *); t_stat cr_devio(uint32 fnc, uint32 inst, uint32 *dat); t_stat cr_show_cap (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat cr_readrec (UNIT *uptr); void cr_set_err (UNIT *uptr); DIB cr_dib = { CHAN_W, DEV_CR, XFR_CR, cr_tplt, &cr_devio }; UNIT cr_unit = { UDATA(&cr_svc, UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | MODE_029 | MODE_CBN,0), 60 }; REG cr_reg[] = { { DRDATA (BPTR, cr_bptr, 18), PV_LEFT }, { DRDATA (BLNT, cr_blnt, 18), PV_LEFT }, { FLDATA (XFR, xfr_req, XFR_V_CR) }, { ORDATA (INST, cr_inst, 24) }, { DRDATA (POS, cr_unit.pos, T_ADDR_W), PV_LEFT }, { NULL } }; MTAB cr_mod[] = { {MTAB_XTD | MTAB_VDV, 0, "CHANNEL", "CHANNEL", &set_chan,&show_chan,NULL, "Device Channel"}, {MTAB_XTD | MTAB_VDV, 0, "FORMAT", "FORMAT", &sim_card_set_fmt, &sim_card_show_fmt, NULL,"Card Format"}, { MTAB_XTD|MTAB_VDV, 0, "CAPACITY", NULL, NULL, &cr_show_cap, NULL, "Card Input Status" }, {0} }; DEVICE cr_dev = { "CR", &cr_unit, cr_reg, cr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &cr_reset, &cr_boot, &cr_attach,NULL, &cr_dib, DEV_DISABLE | DEV_CARD, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* Returns the SDS Internal style BCD of the hollerith code or 0x7f if error */ uint8 hol_to_sdsbcd(uint16 hol) { uint8 bcd; /* Convert 10,11,12 rows */ switch (hol & 0xe00) { case 0x000: if ((hol & 0x1ff) == 0) return 060; bcd = 000; // digits 1-9 break; case 0x200: /* 0 */ if ((hol & 0x1ff) == 0) return 00; // digit 0 bcd = 060; // /,S-Z break; case 0x400: /* 11 */ bcd = 040; // -,J-R break; case 0x600: /* 11-10 Punch */ bcd = 052; break; case 0x800: /* 12 */ bcd = 020; // +,A-I break; case 0xA00: /* 12-10 Punch */ bcd = 032; break; default: /* Double punch in 10,11,12 rows */ return 0x7f; } hol &= 0x1ff; /* Mask rows 0-9 */ /* Check row 8 punched */ if (hol & 0x2) { bcd += 8; hol &= ~0x2; } /* Convert rows 0-9 */ while (hol != 0 && (hol & 0x200) == 0) { bcd++; hol <<= 1; } /* Any more columns punched? */ if ((hol & 0x1ff) != 0) return 0x7f; return bcd; } /* device i/o routine */ t_stat cr_devio (uint32 fnc, uint32 inst, uint32 *dat) { UNIT *uptr = &cr_unit; /* get unit ptr */ int32 new_ch; int32 t; t_stat r; unsigned char chr; switch (fnc) { /* case function */ case IO_CONN: /* bufer control EOM */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != cr_dib.chan) /* wrong chan? */ return SCPE_IERR; if (sim_is_active(uptr)) /* busy ?*/ CRETIOP; /* if not reading and no card in reader and hopper has cards */ if ((uptr->STATUS & (FEEDING|READING)) == 0 && (sim_card_input_hopper_count(uptr) > 0)) { uptr->STATUS = FEEDING; cr_inst = inst; cr_blnt = 0; cr_bptr = 0; xfr_req = xfr_req & ~XFR_CR; /* clr xfr flag */ sim_activate (uptr,2*uptr->wait); /* start timer */ } else { /* if feeding or reading in different mode */ if ((inst & 01000) != (cr_inst & 01000)) { if (cr_inst & 01000) { if (cr_chr & 1) { /* was binary - at 2nd 6 bits */ cr_bptr++; /* skip to next column*/ } } cr_chr = 0; } } cr_inst = inst; /* save EOM with mode */ break; case IO_EOM1: /* I/O Control EOM */ new_ch = I_GETEOCH (inst); /* get new chan */ if (new_ch != cr_dib.chan) /* wrong chan? err */ return SCPE_IERR; if ((inst & 07700) == 02000) { /* skip remainder of card*/ sim_cancel (uptr); /* stop timer */ chan_set_flag (cr_dib.chan, CHF_EOR); /* end record */ uptr->STATUS = 0; chan_disc (cr_dib.chan); xfr_req = xfr_req & ~XFR_CR; /* clr xfr flag */ } break; case IO_DISC: /* disconnect */ xfr_req = xfr_req & ~XFR_CR; /* clr xfr flag */ sim_cancel (uptr); /* deactivate unit */ cr_eor = 0; uptr->STATUS = 0; break; case IO_SKS: /* SKS */ new_ch = I_GETSKCH (inst); /* get chan # */ if (new_ch != cr_dib.chan) /* wrong chan? */ return SCPE_IERR; t = I_GETSKCND (inst); /* get skip cond */ switch (t) { /* case sks cond */ case 004: /* sks 1100n */ // CFT if ((uptr->STATUS & (FEEDING|READING)) == 0 && (sim_card_input_hopper_count(uptr) > 0)) *dat = 1; /* skip if not EOF */ break; case 010: /* sks 1200n */ // CRT // hopper not empty // no feed or read cycle is in progress if ((uptr->STATUS & (FEEDING|READING)) == 0 && (sim_card_input_hopper_count(uptr) > 0)) *dat = 1; /* skip if reader ready */ break; case 020: /* sks 1400n */ if ((uptr->STATUS & READING) && /* first column test */ (((cr_inst & 01000) && (cr_chr < 2)) || (cr_chr < 1))) *dat = 1; /* skip if first column*/ break; } break; case IO_READ: xfr_req = xfr_req & ~XFR_CR; if (cr_blnt == 0) { /* first read? */ r = cr_readrec (uptr); /* get data */ if ((r != SCPE_OK) || (cr_blnt == 0)) /* err, inv reclnt? */ return r; } if (cr_blnt) { if (cr_inst & 01000) { if (cr_chr & 1) /* binary */ chr =cr_buffer[cr_bptr++] & 077; else chr = (cr_buffer[cr_bptr] >> 6) & 077; cr_chr++; *dat = chr & 077; } else { chr = hol_to_sdsbcd(cr_buffer[cr_bptr++]); /* bcd */ *dat = chr & 077; } } if (cr_bptr >= cr_blnt) { /* The card reader doesn't disconnect from the channel until the trailing edge of the card passes the read station so we need to schedule another service event here. But if it disconnects too soon some programs (Fortran and 850647 (unencode) don't work right and if it takes too long Symbol will try to connect to the LP before it's disconnected. */ cr_eor = 1; sim_cancel(uptr); sim_activate (uptr, 50); } break; case IO_WREOR: case IO_WRITE: CRETINS; } return SCPE_OK; } /* Service routine */ t_stat cr_svc(UNIT * uptr) { xfr_req = xfr_req & ~XFR_CR; if (cr_eor) { cr_eor = 0; sim_cancel (uptr); chan_set_flag (cr_dib.chan, CHF_EOR); uptr->STATUS = 0; return SCPE_OK; } xfr_req = xfr_req | XFR_CR; sim_activate (uptr, 50); return SCPE_OK; } /* Read start - get next record */ t_stat cr_readrec (UNIT *uptr) { int r; switch(r = sim_read_card(uptr, cr_buffer)) { case CDSE_EOF: /* parser found tape mark attach */ case CDSE_EMPTY: /* not attached or hopper empty */ case CDSE_ERROR: /* parser found error during attach */ default: uptr->STATUS = 0; /* read failed, no card in reader */ cr_set_err(uptr); return r; case CDSE_OK: uptr->STATUS = READING; cr_bptr = 0; cr_blnt = 80; cr_chr = 0; break; } return SCPE_OK; } /* Fatal error */ void cr_set_err (UNIT *uptr) { chan_set_flag (cr_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */ chan_disc (cr_dib.chan); /* disconnect */ xfr_req = xfr_req & ~XFR_CR; /* clear xfr */ sim_cancel (uptr); /* stop */ cr_bptr = 0; /* buf empty */ return; } t_stat cr_reset (DEVICE *dptr) { chan_disc (cr_dib.chan); /* disconnect */ cr_bptr = cr_blnt = 0; xfr_req = xfr_req & ~XFR_CR; /* clr xfr flag */ sim_cancel (&cr_unit); /* deactivate unit */ return SCPE_OK; } t_stat cr_attach (UNIT *uptr, CONST char *cptr) { return sim_card_attach(uptr, cptr); } /* Boot routine - simulate FILL console command */ t_stat cr_boot (int32 unitno, DEVICE *dptr) { extern uint32 P, M[]; cr_reset(dptr); M[0] = 077777771; /* -7B */ M[1] = 007100000; /* LDX 0 */ M[2] = 000203606; /* EOM 3606 read card binary */ M[3] = 003200002; /* WIM 2 */ M[4] = 000100002; /* BRU 2 */ P = 1; /* start at 1 */ return SCPE_OK; } t_stat cr_show_cap (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int n; if ((n = sim_card_input_hopper_count(uptr)) == 0) fprintf(st,"hopper empty"); else { if (n == 1) fprintf(st,"1 card"); else fprintf(st,"%d cards",n); fprintf(st," in hopper"); } return SCPE_OK; }