blob: fd3aa07bf471a6550c42d8cc99a91635b6912bdb [file] [log] [blame] [raw]
/* pdp11_cr.c: CR/CM/CD-11/CD20 card reader simulator
Copyright (c) 2005-2010, John A. Dundas III
Portions derived from work by Douglas W. Jones, jones@cs.uiowa.edu
Portions derived from work by 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
THE AUTHOR 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 the Author shall
not be used in advertising or otherwise to promote the sale, use
or other dealings in this Software without prior written
authorization from the Author.
------------------------------------------------------------------------------
cr CR11/CD11/CD20 punched and mark sense card reader for SIMH
The CR11 controller is also compatible with the CM11-F, CME11, and CMS11.
Information necessary to create this simulation was gathered from
a number of sources including:
CR11 Card Reader System Manual, DEC-11-HCRB-D
http://www.bitsavers.org/pdf/dec/unibus/DEC-11-HCRB-D_CR11_Mar72.pdf
Various editions of the Peripherals Handbook
OpenVMS VAX Card Reader, Line Printer, and LPA11-K I/O User's
Reference Manual, AA-PVXGA-TE
http://h71000.www7.hp.com/DOC/73final/documentation/pdf/OVMS_VAX_CARD_LP_REF.pdf
OpenVMS System Manager's Manual, Volume 1: Essentials
http://h71000.www7.hp.com/DOC/732FINAL/aa-pv5mh-tk/aa-pv5mh-tk.PDF
CRDRIVER.LIS - CR11 Card Reader Driver, X-9, graciously made available
by HP
Various RSTS manuals
RT-11 Software Support Manual
RT-11 System Reference Manual, DEC-11-ORUGA-C-D
Professor Douglas W. Jones's web site:
http://www.cs.uiowa.edu/~jones/cards/
Paul Mattes' x026 keypunch simulator
http://x3270.bgp.nu/x026.html
CD2SER.MAC - TOPS-10 card reader driver source
http://pdp-10.trailing-edge.com/custsupcuspmar86_bb-x130b-sb/02/cd2ser.mac
CDRIVE.MAC - TOPS GALAXY card reader spooler
http://pdp-10.trailing-edge.com/BB-BT99U-BB_1990/03/10,7/galaxy/cdrive/cdrive.mac
SPRINT.MAC - TOPS GALAXY control card interpreter
http://pdp-10.trailing-edge.com/BB-H138C-BM/01/galaxy-sources/sprint.mac
CDKSDV.MAC - TOPS-20 card reader driver source
http://pdp-10.trailing-edge.com/BB-Y393K-SM/01/monitor-sources/cdksdv.mac
PROKS.MAC - TOPS-20 bit definitions
http://pdp-10.trailing-edge.com/BB-Y393K-SM/01/monitor-sources/proks.mac
The Card Image format code and documentation is adapted from Prof.
Jones's site, with his permission. Please see his site for additional
documentation as well as the card image utilities referenced in
his documentation (cardmake, cardlist, etc.).
http://www.cs.uiowa.edu/~jones/cards/format.html
Known limitations:
1. Need a copy of the CR bootstrap (and some way to test it)
2. Need a copy of the XXDP+ test deck
3. No testing under RSX; volunteers needed
4. No testing under Ultrix or Unix for PDP-11; volunteers needed
5. No testing under Ultrix or Unix for VAX; volunteers needed
6. The simulator implements a single controller/reader combination
Operating System Notes
RT-11 (and CTS-300) support one CR11 or CM11, but no CD11.
VMS supports multiple CR11 controllers, but no CD11.
RSTS/E supports either the CR11/CM11 or CD11 but not both in
the same SIL. It appears to support only one unit.
For RSX there exists a CR/CM task handler. Is there a CD
handler?
To-do (RSX): The CR11 unit works as a regular device (ie,
you can PIP from it) but it does not work well as a job
input device (it works just once, somwhow the CRP processor
gets stuck).
Don't have any information about Unix or Ultrix-11 yet. Same
for VAX Unices.
TOPS: only the CD20 variant of the CD11 is supported. CD20 implies
ECOs (at least) for Data Buffer status and augmented image mode.
Revision History:
23-Feb-13 JGP Added DEC version of the 026 codepage
Fixed the handling of the CR11 error bits after
a control register write.
Added logic reset after RESET button press
Commented and reestructured code (to supress
dangling elses)
03-Jan-10 JAD Eliminate gcc warnings
01-Feb-07 RMS Added PDP-10 support
12-May-06 JAD Modify the DEBUG code to use the SIMH DEBUG_x
macros. Modify the UNIT structure to include
the DEBUG bit.
Mark the trans[] array contents constant.
Make device data structures static and constant
as appropriate.
18-Mar-05 JAD Slight optimization for blank punches recognizing
that blank is 0 in all character encodings.
17-Mar-05 JAD Completely initialize ascii_code correctly.
Define the end of deck punch code separately from
the cardcode.i file.
Make initTranslation() set a pointer to the correct
punch code table to use. Modify card read functions
to use this table pointer.
16-Mar-05 JAD Make certain switches passed to the ATTACH command
are valid; return error on any others.
Make default unit wait time compatible with default
device specification.
Implement SET TRANSLATION=value. Still need to
modify the H2ASCII table used for text files;
currently hard-coded to 029.
24-Feb-05 JAD Allow the maintenance bits in CRM to clear as
well as set status bits. Not sure this is the
correct behavior, though, without more documentation.
Catch three more places to spin down the blower
correctly.
Zero the CDDB and CRM at INIT.
17-Feb-05 JAD When the hopper empties, a pick check should
be generated 300ms later. They are simultaneous
for now.
Make sure readColumnBinary() generates a complete
EOF card.
08-Feb-05 JAD Replace blowerWait with different times for blower
spin up and down.
06-Feb-05 JAD After DETACH: mark CD offline, set appropriate
blower state.
Make sure unit wait time is recalculated every
time cpm is set.
04-Feb-05 JAD Better tracking of blower state throughout driver.
Make sure IE gets cleared for CR at INIT.
Normalize error response in read routines.
Finish condition handling for column binary.
02-Feb-05 JAD Remove Qbus support; Unibus only.
Support ATTACH switches:
A - ASCII, B - column binary, I - Card Image
If none given, check for .TXT or .CBN; if none,
examine file for magic header.
Finer granularity to blower state. Expose this
variable to examine/deposit from SIMH.
Preliminary implementation of support for
column binary format.
24-Jan-05 JAD Make AUTOEOF work as a surrogate for the EOF
button of a CD11 reader. May need to separate
this later, though.
Partial implementation of DATAERR for CD11.
Implement the Rev. J mods (as best I understand
them) to the CD11 affecting the CDDB used as a
second status register.
23-Jan-05 JAD Preliminary clean-up of CD state transitions.
Tested with RSTS/E (V9.1-05).
22-Jan-05 JAD Finish CR state transitions; should be close now.
Tested with RSTS/E (V9.1-05), RT-11 (V5.3), and
VAX/VMS (V7.2).
19-Jan-05 JAD Add bounds to the RATE command; also default and
help a la the XQ driver.
Improved handling of empty files.
17-Jan-05 JAD Add the CR maintenance register.
16-Jan-05 JAD Add preliminary CD11 support.
Simulate the STOP and RESET switches.
14-Jan-05 JAD Add the ability to automatically generate an 'EOF'
card recognized by DEC operating systems when
reading ASCII files.
08-Jan-05 JAD Original creation and testing
*/
/* Configuration notes:
* Keep VM_arch symbols here and use them only to select features.
* CR attributes use generic symbols so device support is easy to change,
* e.g. if software is discovered that uses a previously unsupported option.
* Conventions:
* *_ONLY (AND *_req) means feature * is unconditionally present/required.
* *_OK means feature * is selectable at runtime.
* neither means feature is not present.
* To support only one controller model, define <model>_ONLY.
* To support more than one, define them all as <model>_OK.
* Don't mix "_ONLY" and "_OK" for the same feature. You won't like it.
*
* The CD/CR will work on any UNIBUS, and the CR will also work on a QBUS.
* The configuration options used here are more restrictive to reflect
* known software support, as this reduces user configuration errors/confusion.
*/
#if defined (VM_PDP10) /* PDP10 version */
#include "pdp10_defs.h"
extern int32 int_req;
#define DFLT_DIS (DEV_DIS)
#define DFLT_TYPE (UNIT_CD20) /* CD20 (CD11) only */
#define CD20_ONLY (1)
#define DFLT_CPM 1200
#define AIECO_REQ (1) /* Requires Augmented Image ECO */
#elif defined (VM_VAX) /* VAX version */
#include "vax_defs.h"
extern int32 int_req[IPL_HLVL];
#define DFLT_DIS (DEV_QBUS) /* CR11 is programmed I/O only, Qbus OK */
#define DFLT_TYPE (UNIT_CR11) /* CR11 only */
#define CR11_ONLY (1)
#define DFLT_CPM 285
#else /* PDP-11 version */
#include "pdp11_defs.h"
extern int32 int_req[IPL_HLVL];
#define DFLT_DIS (DEV_QBUS) /* CR11 is programmed I/O only, Qbus OK */
#define DFLT_TYPE (UNIT_CR11) /* Default, but changable */
#define DFLT_CPM 285
#define CD20_OK (1)
#define AIECO_OK (1) /* Augmented Image ECO optional */
#define CR11_OK (1)
#define CD11_OK (1)
#endif
/* **** No VM_xxx macros should be referenced after this line **** */
/* create a int32 constant from four characters */
#define I4C(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
#define I4C_CBN I4C ('C','B','N',' ')
#define I4C_H80 I4C ('H','8','0',' ')
#define I4C_H82 I4C ('H','8','2',' ')
#define I4C_H40 I4C ('H','4','0',' ')
#define UNIT_V_TYPE (UNIT_V_UF + 0) /* Bit-encoded 2-bit field */
#define UNIT_TYPE (3u << UNIT_V_TYPE)
#define UNIT_CR11 (1u << UNIT_V_TYPE)
#define UNIT_CD20 (2u << UNIT_V_TYPE)
#define UNIT_V_AUTOEOF (UNIT_V_UF + 2)
#define UNIT_AUTOEOF (1u << UNIT_V_AUTOEOF)
#define UNIT_V_RDCHECK (UNIT_V_UF + 3)
#define UNIT_RDCHECK (1u << UNIT_V_RDCHECK)
#define UNIT_V_AIECO (UNIT_V_UF + 4)
#define UNIT_AIECO (1u << UNIT_V_AIECO)
/* Tests for which device is being emulated.
* Note that CD20 is a CD11 + mandatory ECOs. CD11_CTL will be true for both.
*/
#if defined (CD11_ONLY) || defined (CD20_ONLY)
#define CR11_CTL(up) (0)
#define CD11_CTL(up) (1)
#elif defined (CR11_ONLY)
#define CR11_CTL(up) (1)
#define CD11_CTL(up) (0)
#else
#define CR11_CTL(up) ((up)->flags & UNIT_CR11)
#define CD11_CTL(up) (!CR11_CTL(up))
#endif
#if defined (CD20_ONLY)
#define CD20_CTL(up) (1)
#elif defined (CD20_OK)
#define CD20_CTL(up) ((up)->flags & UNIT_CD20)
#else
#define CD20_CTL(up) (0)
#endif
/* Configuration */
#if defined (AIECO_REQ)
#define DFLT_AIECO (UNIT_AIECO)
#else
#define DFLT_AIECO (0)
#endif
#include <assert.h>
#define ERROR (00404)
#include "pdp11_cr_dat.h"
#define PUNCH_EOD (07417)
#define PUNCH_SPACE (0) /* same for all encodings */
/* CR */
/* also use CSR_ERR, CSR_IE, and CSR_GO */
#define CRCSR_V_CRDDONE 14 /* card done */
#define CRCSR_V_SUPPLY 13 /* supply error */
#define CRCSR_V_RDCHK 12 /* reader check */
#define CRCSR_V_TIMERR 11 /* timing error */
#define CRCSR_V_ONLINE 10 /* on line */
#define CRCSR_V_BUSY 9 /* busy reading */
#define CRCSR_V_OFFLINE 8 /* off line AKA READY? */
#define CRCSR_V_COLRDY 7 /* column ready */
#define CRCSR_V_EJECT 1 /* ignore card */
#define CRCSR_CRDDONE (1u << CRCSR_V_CRDDONE)
#define CRCSR_SUPPLY (1u << CRCSR_V_SUPPLY)
#define CRCSR_RDCHK (1u << CRCSR_V_RDCHK)
#define CRCSR_TIMERR (1u << CRCSR_V_TIMERR)
#define CRCSR_ONLINE (1u << CRCSR_V_ONLINE)
#define CRCSR_BUSY (1u << CRCSR_V_BUSY)
#define CRCSR_OFFLINE (1u << CRCSR_V_OFFLINE)
#define CRCSR_COLRDY (1u << CRCSR_V_COLRDY)
#define CRCSR_EJECT (1u << CRCSR_V_EJECT)
#define CRCSR_IMP (CSR_ERR | CRCSR_CRDDONE | CRCSR_SUPPLY | \
CRCSR_RDCHK | CRCSR_TIMERR | CRCSR_ONLINE | \
CRCSR_BUSY | CRCSR_OFFLINE | CRCSR_COLRDY | \
CSR_IE | CRCSR_EJECT)
#define CRCSR_RW (CSR_IE | CRCSR_EJECT | CSR_GO) /* read/write */
#define CRM_V_MAINT 15 /* enable maint funct */
#define CRM_V_BUSY 14
#define CRM_V_READY 13
#define CRM_V_HOPPER 12
#define CRM_MAINT (1u << CRM_V_MAINT)
#define CRM_BUSY (1u << CRM_V_BUSY)
#define CRM_READY (1u << CRM_V_READY)
#define CRM_HOPPER (1u << CRM_V_HOPPER)
/* CD */
/* also use CSR_ERR, CSR_IE, and CSR_GO */
/* ERR */
#define CDCSR_V_RDRCHK 14 /* reader check: HOPPER,STACK,PICK,READ */
#define CDCSR_V_EOF 13 /* CD11-E EOF button */
#define CDCSR_V_OFFLINE 12 /* off line */
#define CDCSR_V_DATAERR 11 /* data packing error */
#define CDCSR_V_LATE 10 /* data late */
#define CDCSR_V_NXM 9 /* non-existent memory */
#define CDCSR_V_PWRCLR 8 /* power clear */
#define CDCSR_V_RDY 7 /* ready */
/* IE */
#define CDCSR_V_XBA17 5 /* NPR bus address bits<17:16> */
#define CDCSR_V_XBA16 4
#define CDCSR_V_ONLINE 3 /* on line transition */
#define CDCSR_V_HOPPER 2 /* hopper check */
#define CDCSR_V_PACK 1 /* data packing */
/* GO */
#define CDCSR_RDRCHK (1u << CDCSR_V_RDRCHK)
#define CDCSR_EOF (1u << CDCSR_V_EOF)
#define CDCSR_OFFLINE (1u << CDCSR_V_OFFLINE)
#define CDCSR_DATAERR (1u << CDCSR_V_DATAERR)
#define CDCSR_LATE (1u << CDCSR_V_LATE)
#define CDCSR_NXM (1u << CDCSR_V_NXM)
#define CDCSR_PWRCLR (1u << CDCSR_V_PWRCLR)
#define CDCSR_RDY (1u << CDCSR_V_RDY)
#define CDCSR_XBA17 (1u << CDCSR_V_XBA17)
#define CDCSR_XBA16 (1u << CDCSR_V_XBA16)
#define CDCSR_ONLINE (1u << CDCSR_V_ONLINE)
#define CDCSR_HOPPER (1u << CDCSR_V_HOPPER)
#define CDCSR_PACK (1u << CDCSR_V_PACK)
#define CDCSR_ANYERR (CDCSR_RDRCHK | CDCSR_EOF | CDCSR_OFFLINE | CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM)
#define CDCSR_IMP (CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_OFFLINE | \
CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | \
CDCSR_PWRCLR | CDCSR_RDY | CSR_IE | \
CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | \
CDCSR_HOPPER | CDCSR_PACK | CSR_GO)
#define CDCSR_RW (CDCSR_PWRCLR | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | \
CDCSR_PACK | CSR_GO)
/* CD11 second status register bits. Valid only when not busy. All
* also set CDCSR_RDRCK (and CSR_ERR)
*/
#define CDDB_V_READ 14 /* Read check (extra punches, not readER check) */
#define CDDB_V_PICK 13 /* Pick check (card present, not grabbed) */
#define CDDB_V_STACK 12 /* Card did not arrive in stacker */
/* N.B. Per TOPS-20 driver, which references CD11 manual and printset:
* Stacker full is indicated by:
* CDCSR_RDRCHK && !(CDDB_READ|CDDB_PICK|CDDB_STACK)
*/
#define CDDB_READ (1U << CDDB_V_READ)
#define CDDB_PICK (1u << CDDB_V_PICK)
#define CDDB_STACK (1u << CDDB_V_STACK)
/* Blower state values */
#define BLOW_OFF (0) /* steady state off */
#define BLOW_START (1) /* starting up */
#define BLOW_ON (2) /* steady state on */
#define BLOW_STOP (3) /* shutting down */
/* Card Reader state */
static const char *cardFormat = "unknown";
static t_bool (*readRtn)(UNIT *, int16 *, char *, char *);
static char ascii_code[4096]; /* 2^12 possible values */
static int currCol; /* current column when reading */
static int colStart; /* starting column */
static int colEnd; /* ending column */
static const int *codeTbl = /* punch translation table */
#if defined(CD20_ONLY) || (defined(DFLT_TYPE) && (DFLT_TYPE == UNIT_CD20))
o29_decascii_code;
#else
o29_code;
#endif
static struct trans {
const char *const name;
const int *table;
} transcodes[] = {
{ "DEFAULT", o29_code, },
{ "026", o26_dec_code, },
{ "026FTN", o26_ftn_code, },
{ "026DECASCII", o26_decascii_code, },
{ "029", o29_code, },
{ "EBCDIC", EBCDIC_code, },
{ "026DEC", o26_dec_code, },
{ "029DECASCII", o29_decascii_code },
};
#define NTRANS (sizeof transcodes /sizeof transcodes[0])
static int32 blowerState = BLOW_OFF; /* reader vacuum/blower motor */
static int32 spinUp = 3000000; /* blower spin-up time: 3 seconds (usec) */
static int32 spinDown = 2000000; /* blower spin-down time: 2 seconds (usec) */
static int EOFcard = 0; /* played special card yet? */
static t_bool eofPending = FALSE; /* Manual EOF switch pressed */
static int32 cpm = DFLT_CPM; /* reader rate: cards per minute */
static int schedule_svc=0; /* Re-schedule service if true */
/* card image in various formats */
static int16 hcard[82]; /* Hollerith format */
static char ccard[82]; /* DEC compressed format */
static char acard[82]; /* ASCII format */
/* CR/CM registers */
static int32 crs = CSR_ERR | CRCSR_OFFLINE | CRCSR_SUPPLY; /* control/status */
static int32 crb1 = 0; /* 12-bit Hollerith characters */
static int32 crb2 = 0; /* 8-bit compressed characters */
static int32 crm = 0; /* CMS maintenance register */
/* CD registers */
static int32 cdst = CSR_ERR | CDCSR_OFFLINE | CDCSR_HOPPER; /* Control/status - off-line until attached */
static int32 cdcc = 0; /* column count */
static int32 cdba = 0; /* current address, low 16 bits */
static int32 cddb = 0; /* data, 2nd status */
static int32 cddbs = 0; /* second status bits (or with cddb) */
/* forward references */
static void setupCardFile (UNIT *, int32);
t_stat cr_rd (int32 *, int32, int32);
t_stat cr_wr (int32, int32, int32);
int32 cr_intac(void);
t_stat cr_svc (UNIT *);
t_stat cr_reset (DEVICE *);
t_stat cr_attach (UNIT *, char *);
t_stat cr_detach (UNIT *);
t_stat cr_set_type (UNIT *, int32, char *, void *);
t_stat cr_set_aieco (UNIT *, int32, char *, void *);
t_stat cr_show_format (FILE *, UNIT *, int32, void *);
t_stat cr_set_rate (UNIT *, int32, char *, void *);
t_stat cr_show_rate (FILE *, UNIT *, int32, void *);
t_stat cr_set_reset (UNIT *, int32, char *, void *);
t_stat cr_set_stop (UNIT *, int32, char *, void *);
t_stat cr_set_eof (UNIT *, int32, char *, void *);
t_stat cr_show_eof (FILE *, UNIT *, int32, void *);
t_stat cr_set_trans (UNIT *, int32, char*, void *);
t_stat cr_show_trans (FILE *, UNIT *, int32, void *);
static t_stat cr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *cr_description (DEVICE *dptr);
/* CR data structures
cr_dib CR device information block
cr_unit CR unit descriptor
cr_reg CR register list
cr_mod CR modifier table
cr_dev CR device descriptor
*/
#define IOLN_CR 010
static DIB cr_dib = { IOBA_AUTO, IOLN_CR, &cr_rd, &cr_wr,
1, IVCL (CR), VEC_AUTO, { cr_intac } };
static UNIT cr_unit = {
UDATA (&cr_svc,
UNIT_ATTABLE+UNIT_SEQ+UNIT_ROABLE+UNIT_DISABLE+
DFLT_TYPE+UNIT_AUTOEOF+UNIT_RDCHECK+DFLT_AIECO, 0),
(60 * 1000000) / (DFLT_CPM * 80) };
static const REG cr_reg[] = {
{ GRDATAD (BUF, cr_unit.buf, DEV_RDX, 8, 0, "ASCII value of last column processed") },
#if defined (CR11_OK) || defined (CR11_ONLY)
{ GRDATAD (CRS, crs, DEV_RDX, 16, 0, "CR11 status register") },
{ GRDATAD (CRB1, crb1, DEV_RDX, 16, 0, "CR11 12-bit Hollerith character") },
{ GRDATAD (CRB2, crb2, DEV_RDX, 16, 0, "CR11 8-bit compressed character") },
{ GRDATAD (CRM, crm, DEV_RDX, 16, 0, "CR11 maintenance register") },
#endif
#if defined (CD11_OK) || defined (CD11_ONLY) || defined (CD20_OK) || defined (CD20_ONLY)
{ GRDATAD (CDST, cdst, DEV_RDX, 16, 0, "CD11 control/status register") },
{ GRDATAD (CDCC, cdcc, DEV_RDX, 16, 0, "CD11 column count") },
{ GRDATAD (CDBA, cdba, DEV_RDX, 16, 0, "CD11 current bus address") },
{ GRDATAD (CDDB, cddb, DEV_RDX, 16, 0, "CD11 data buffer, 2nd status") },
#endif
{ GRDATAD (BLOWER, blowerState, DEV_RDX, 2, 0, "blower state value") },
{ FLDATAD (INT, IREQ (CR), INT_V_CR, "interrupt pending flag") },
{ FLDATAD (ERR, crs, CSR_V_ERR, "error flag (CRS<15>)") },
{ FLDATAD (IE, crs, CSR_V_IE, "interrupt enable flag (CRS<6>)") },
{ DRDATAD (POS, cr_unit.pos, T_ADDR_W, "file position - do not alter"), PV_LEFT },
{ DRDATAD (TIME, cr_unit.wait, 24, "delay time between columns"), PV_LEFT },
{ GRDATA (DEVADDR, cr_dib.ba, DEV_RDX, 32, 0), REG_HRO },
{ GRDATA (DEVVEC, cr_dib.vec, DEV_RDX, 16, 0), REG_HRO },
{ NULL } };
static char *translation_help = NULL;
static MTAB cr_mod[] = {
#if defined (CR11_OK)
{ UNIT_TYPE, UNIT_CR11, "CR11", "CR11",
&cr_set_type, NULL, NULL, "Set device type to CR11" },
#endif
#if defined (CD11_OK)
{ UNIT_TYPE, 0, "CD11", "CD11",
&cr_set_type, NULL, NULL, "Set device type to CD11" },
#endif
#if defined (CD20_OK)
{ UNIT_TYPE, UNIT_CD20, "CD20", "CD20",
&cr_set_type, NULL, NULL, "Set device type to CD20" },
#endif
#if defined (CR11_ONLY) || defined (CD11_ONLY) || defined (CD20_ONLY)
{ UNIT_TYPE, UNIT_CR11, "CR11", NULL, },
{ UNIT_TYPE, 0, "CD11", NULL, },
{ UNIT_TYPE, UNIT_CD20, "CD20", NULL, },
#endif
#if defined (AIECO_OK)
{ (UNIT_TYPE|UNIT_AIECO), (UNIT_CD20|UNIT_AIECO), "augmented image ECO", "AIECO",
&cr_set_aieco, NULL, NULL, "Enable CD20 augmented image ECO" },
{ (UNIT_TYPE|UNIT_AIECO), (UNIT_CD20|0), "standard", "NOAIECO",
&cr_set_aieco, NULL, NULL, "Disable CD20 augmented image ECO" },
#endif
{ UNIT_AUTOEOF, UNIT_AUTOEOF, "auto EOF", "AUTOEOF",
NULL, NULL, NULL, "Enable auto EOF mode" },
{ UNIT_AUTOEOF, 0, "no auto EOF", "NOAUTOEOF",
NULL, NULL, NULL, "Disable auto EOF mode" },
#if !defined (CR11_ONLY)
{ UNIT_RDCHECK, UNIT_RDCHECK, "read check", "RDCHECK",
NULL, NULL, NULL, "Enable read check errors" },
{ UNIT_RDCHECK, 0, "no read check", "NORDCHECK",
NULL, NULL, NULL, "Disable read check errors" },
#endif
/* card reader STOP switch */
{ MTAB_XTD|MTAB_VDV, 0, NULL, "STOP",
&cr_set_stop, NULL, NULL, "Pulse reader Stop button" },
/* card reader RESET switch */
{ MTAB_XTD|MTAB_VDV, 0, NULL, "RESET",
&cr_set_reset, NULL, NULL, "Pulse reader reset button" },
#if !defined (CR11_ONLY)
/* card reader EOF switch */
{ MTAB_XTD|MTAB_VDV, MTAB_XTD|MTAB_VDV, "EOF pending", "EOF",
&cr_set_eof, &cr_show_eof, NULL, "Pulse reader EOF button" },
#endif
{ MTAB_XTD|MTAB_VUN, 0, "FORMAT", NULL,
NULL, &cr_show_format, NULL, "Set reader input format" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 006, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "VECTOR", "VECTOR",
&set_vec, &show_vec, NULL, "Interrupt vector" },
{ MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={DEFAULT|200..1200}",
&cr_set_rate, &cr_show_rate, NULL, "Display input rate" },
{ MTAB_XTD|MTAB_VDV, 0, "TRANSLATION",
NULL,
&cr_set_trans, &cr_show_trans, NULL, "Display translation mode" },
{ 0 } };
DEVICE cr_dev = {
"CR", &cr_unit, (REG *) &cr_reg, (MTAB *) &cr_mod,
1, 10, 31, 1, DEV_RDX, 8,
NULL, NULL, &cr_reset,
NULL, &cr_attach, &cr_detach,
&cr_dib, DEV_DISABLE | DFLT_DIS | DEV_UBUS | DEV_DEBUG, 0,
NULL, NULL, NULL, &cr_help, NULL, NULL,
&cr_description
};
/* Utility routines */
/*
These functions read a "card" from a virtual deck file (passed in
fp) and fill in three arrays. The first array 'hcard' contains the
12-bit binary image of the punch in each column; the second array
'ccard' contains the 8-bit DEC encoded representation of the
corresponding column; the third array 'acard' contains the ASCII
representation (if possible) of the character. The routines return
TRUE if a card was read (possibly with errors) and FALSE if the
"hopper is empty" (EOF) or fatal file errors prevented any portion
of a card from being read.
Note that the hopper becomes empty when the last card moves to the
read station. Thus hopper empty without an error means that data
from that card is valid. Thus hopper empty is first signalled when
the NEXT card read would return EOF. Reads after that will return
some error bit.
Errors other than EOF are signaled out of band in the controller
state variables. Possible errors are data in columns 0 or 81
(signalled as read check; currently these columns are ignored), or
any file errors (signalled as motion check).
Might rethink this. Should probably treat file errors as "pick
check". Retry 3 times. After that, give up with error.
*/
/* Common handling for end of file and errors on input */
static t_bool fileEOF ( UNIT *uptr,
int16 *hcard,
char *ccard,
char *acard,
int32 cddbsBits )
{
int col;
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "hopper empty-eof\n");
if (!EOFcard && (uptr->flags & UNIT_AUTOEOF) && !ferror(uptr->fileref)) {
EOFcard = -1;
/* Generate EOD card, which empties the hopper */
for (col = 1; col <= 8; col++) {
hcard[col] = PUNCH_EOD;
ccard[col] = (char)h2c_code[PUNCH_EOD];
acard[col] = ' ';
}
while (col <= colEnd) {
hcard[col] = PUNCH_SPACE;
ccard[col] = PUNCH_SPACE;
acard[col] = ' ';
col++;
}
/* The CR11 doesn't set SUPPPLY at this time, but waits until the EOF card is done. */
cdst |= CDCSR_HOPPER;
return (TRUE);
}
/* Not auto EOF, or EOF already handled. This is an attempt to read
* with an empty hopper. Report a pick, read or stacker check as well
* as hopper empty to indicate that no data was transfered. One might
* think that cdcc unchanged would be sufficient, but that's not what
* the OSs check.
*/
crs |= CSR_ERR | CRCSR_SUPPLY | CRCSR_OFFLINE;
crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE);
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;
cddbs |= cddbsBits;
if (((uptr->flags & UNIT_AUTOEOF) || eofPending) && !ferror(uptr->fileref)) {
cdst |= CDCSR_EOF;
eofPending = FALSE;
}
return (FALSE);
}
static t_bool readCardImage ( UNIT *uptr,
int16 *hcard,
char *ccard,
char *acard )
{
int c1, c2, c3, col;
FILE *fp = uptr->fileref;
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "readCardImage pos %d\n", (int) ftell (fp));
do {
/* get card header bytes */
c1 = fgetc (fp);
c2 = fgetc (fp);
c3 = fgetc (fp);
uptr->pos = ftell (fp);
/* check for EOF */
if (c1 == EOF)
return fileEOF (uptr, hcard, ccard, acard, CDDB_PICK);
/* check for valid card header */
if ((c2 == EOF) || (c3 == EOF) || ((c1 & c2 & c3 & 0x80) == 0) ) {
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "header error\n");
/* unexpected EOF or format problems */
return fileEOF (uptr, hcard, ccard, acard, CDDB_READ);
}
/* Read card image into internal buffer */
assert (colStart < colEnd);
assert (colStart >= 0);
assert (colEnd <= 81);
for (col = colStart; col < colEnd; ) {
int16 i;
int c1, c2, c3;
/* get 3 bytes */
c1 = fgetc (fp);
c2 = fgetc (fp);
c3 = fgetc (fp);
uptr->pos = ftell (fp);
if (ferror (fp) || (c1 == EOF) || (c2 == EOF) || (c3 == EOF)) {
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "file error\n");
/* signal error; unexpected EOF, format problems, or file error(s) */
return fileEOF (uptr, hcard, ccard, acard, ferror(fp)? CDDB_READ: CDDB_PICK);
}
/* convert to 2 columns */
i = ((c1 << 4) | ( c2 >> 4)) & 0xFFF;
hcard[col] = i;
ccard[col] = (char)h2c_code[i];
acard[col] = ascii_code[i];
col++;
i = (((c2 & 017) << 8) | c3) & 0xFFF;
hcard[col] = i;
ccard[col] = (char)h2c_code[i];
acard[col] = ascii_code[i];
col++;
}
} while ((c3 & 0x3f) == 0x3f); /* Skip metacards (Revised Jones spec) */
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "successfully loaded card\n");
return (TRUE);
}
static t_bool readColumnBinary ( UNIT *uptr,
int16 *hcard,
char *ccard,
char *acard )
{
int col;
FILE *fp = uptr->fileref;
for (col = colStart; col <= colEnd; col++) {
int c1, c2;
uint16 i;
c1 = fgetc (fp);
c2 = fgetc (fp);
uptr->pos = ftell (fp);
if (c1 == EOF)
return fileEOF (uptr, hcard, ccard, acard, CDDB_PICK);
if ((c2 == EOF) || ferror(fp))
return fileEOF (uptr, hcard, ccard, acard, CDDB_READ);
i = (c1 & 077) | ((c2 & 077) << 6);
hcard[col] = i;
ccard[col] = (char)h2c_code[i];
acard[col] = ascii_code[i];
}
return (TRUE);
}
/*
Should this routine perform special handling of non-printable,
(e.g., control) characters or characters that have no encoded
representation? (In DEC026/DEC029 they all do...)
*/
static t_bool readCardASCII ( UNIT *uptr,
int16 *hcard,
char *ccard,
char *acard )
{
int c = 0, col, peek;
FILE *fp = uptr->fileref;
assert (colStart < colEnd);
assert (colStart >= 1);
assert (colEnd <= 80);
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "readCardASCII\n");
for (col = colStart; col <= colEnd; ) {
switch (c = fgetc (fp)) {
case EOF:
if (ferror (fp)) {
uptr->pos = ftell (fp);
return fileEOF (uptr, hcard, ccard, acard, CDDB_READ);
}
if (col == colStart) {
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "hopper empty\n");
uptr->pos = ftell (fp);
return fileEOF (uptr, hcard, ccard, acard, CDDB_PICK);
}
/* fall through */
case '\r':
peek = fgetc (uptr->fileref);
if ((peek != EOF) && (peek != '\n'))
ungetc (peek, uptr->fileref);
goto fill;
case '\n':
peek = fgetc (uptr->fileref);
if ((peek != EOF) && (peek != '\r'))
ungetc (peek, uptr->fileref);
fill:
while (col <= colEnd) {
hcard[col] = PUNCH_SPACE;
ccard[col] = PUNCH_SPACE;
acard[col] = ' ';
col++;
}
break;
case '\t':
do {
hcard[col] = PUNCH_SPACE;
ccard[col] = PUNCH_SPACE;
acard[col] = ' ';
col++;
} while (((col & 07) != 1) && (col <= colEnd));
break;
default:
hcard[col] = (uint16)codeTbl[c & 0177];
/* check for unrepresentable ASCII characters */
if (hcard[col] == ERROR) {
cdst |= CDCSR_DATAERR;
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb,
"error character at column %d (%c)\n",
col, c & 0177);
}
ccard[col] = (char)h2c_code[hcard[col]];
acard[col] = (char)c;
col++;
break;
}
}
/* silently truncate/flush long lines, or flag over-length card? */
if (c != '\n' && c != '\r') {
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "truncating card\n");
c = fgetc (fp);
while (c != EOF) {
if ((c == '\n') || (c == '\r')) {
peek = fgetc (uptr->fileref);
if (peek == EOF)
break;
if (((c == '\n') && (peek != '\r')) || ((c == '\r') && (peek != '\n')))
ungetc (peek, uptr->fileref);
break;
}
c = fgetc (fp);
}
}
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "successfully loaded card\n");
uptr->pos = ftell (fp);
return (TRUE);
}
/*
Initialize the binary translation table. Generally called when a
new deck is attached but could be set manually as well.
*/
static void initTranslation (void)
{
int32 i;
memset (ascii_code, '~', sizeof (ascii_code));
for (i = 0; i < 0177; i++)
ascii_code[codeTbl[i]] = (char)i;
}
/*
Examine the command switches, file extension, and virtual card deck
file to determine the format. Set up the global variables
appropriately. Rewind ASCII files to the beginning
*/
static void setupCardFile ( UNIT *uptr,
int32 switches )
{
int32 i;
if (switches & SWMASK ('A'))
i = 0;
else if (switches & SWMASK ('B'))
i = I4C_CBN;
else if (switches & SWMASK ('I'))
goto read_header;
else if (match_ext (uptr->filename, "TXT"))
i = 0;
else if (match_ext (uptr->filename, "CBN"))
i = I4C_CBN;
else {
read_header:
/* look for card image magic file number */
i = fgetc (uptr->fileref);
i = (i << 8) | fgetc (uptr->fileref);
i = (i << 8) | fgetc (uptr->fileref);
i = (i << 8) | ' ';
}
switch (i) {
case I4C_H80:
colStart = 1;
colEnd = 80;
cardFormat = "card image";
readRtn = readCardImage;
break;
case I4C_H82:
colStart = 0;
colEnd = 81;
cardFormat = "card image";
readRtn = readCardImage;
break;
case I4C_H40:
colStart = 1;
colEnd = 40;
cardFormat = "card image";
readRtn = readCardImage;
break;
case I4C_CBN:
colStart = 1;
colEnd = 80;
cardFormat = "column binary";
readRtn = readColumnBinary;
break;
default:
colStart = 1;
colEnd = 80;
cardFormat = "ASCII";
readRtn = readCardASCII;
fseek (uptr->fileref, 0L, SEEK_SET);
break;
}
initTranslation ();
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "colStart = %d, colEnd = %d\n",
colStart, colEnd);
cr_unit.pos = ftell (uptr->fileref);
}
/* Card reader routines
cr_rd I/O page read
cr_wr I/O page write
cr_svc process event (reader ready)
cr_reset process reset
cr_attach process attach
cr_detach process detach
*/
t_stat cr_rd ( int32 *data,
int32 PA,
int32 access )
{
switch ((PA >> 1) & 03) {
case 0: /* CSR */
if (cdst & (CDCSR_ANYERR))
cdst |= CSR_ERR;
else
cdst &= ~CSR_ERR;
*data = (CR11_CTL(&cr_unit)) ?
crs & CRCSR_IMP : cdst & CDCSR_IMP;
/* CR: if error removed, clear 15, 14, 11, 10 */
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_rd crs %06o cdst %06o\n",
crs, cdst);
break;
case 1:
/* Get word of data from crb1 (Hollerith code) or CD11 CC */
*data = (CR11_CTL(&cr_unit)) ? crb1 : cdcc;
crs &= ~CRCSR_COLRDY;
if (DEBUG_PRS (cr_dev)) {
if (CR11_CTL(&cr_unit))
fprintf (sim_deb, "cr_rd crb1 %06o '%c' %d\n",
crb1, cr_unit.buf, cr_unit.buf);
else
fprintf (sim_deb, "cr_rd cdcc %06o\n", cdcc);
}
/* Does crb1 clear after read? Implied by VMS driver. */
crb1 = 0;
break;
case 2:
/* Get word of data from crb2 (DEC Compressed) or CD11 BA */
*data = (CR11_CTL(&cr_unit)) ? crb2 : cdba;
crs &= ~CRCSR_COLRDY;
if (DEBUG_PRS (cr_dev)) {
if (CR11_CTL(&cr_unit))
fprintf (sim_deb, "cr_rd crb2 %06o\n", crb2);
else
fprintf (sim_deb, "\r\ncr_rd cdba %06o\n", cdba);
}
crb2 = 0; /* see note for crb1 */
break;
case 3:
default:
if (CR11_CTL(&cr_unit)) /* CR11 maintenance */
*data = crm;
else /* CD11 data buffer/status. Note this implementation returns extended
* status even while busy (rather than the zone). Might be wrong.
*/
*data = 0100000 | (cddbs & (CDDB_READ|CDDB_PICK|CDDB_STACK)) |
((crs & CRCSR_BUSY) ?
cddb & 0777 : 0777);
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_rd crm %06o cddb %06o data %06o\n",
crm, cddb, *data);
break;
}
return (SCPE_OK);
}
t_stat cr_wr ( int32 data,
int32 PA,
int32 access )
{
int curr_crs = crs; /* Save current crs to recover status */
switch ((PA >> 1) & 03) {
case 0:
if (CR11_CTL(&cr_unit)) {
/* ignore high-byte writes */
if (PA & 1)
break;
/* fixup data for low byte write */
if (access == WRITEB)
data = (crs & ~0377) | (data & 0377);
if (!(data & CSR_IE))
CLR_INT (CR);
crs = (crs & ~CRCSR_RW) | (data & CRCSR_RW);
/* Clear status bits after CSR load */
crs &= ~(CSR_ERR | CRCSR_ONLINE | CRCSR_CRDDONE | CRCSR_TIMERR);
if (crs & CRCSR_OFFLINE)
crs |= CSR_ERR;
/*
* Read card requested:
* Check if there was any error which required an operator
* intervention, and if so, reassert the corresponding
* error bits and assert interrupt.
* (Expected by the VMS CRDRIVER)
*/
if (data & CSR_GO) {
if (curr_crs & (CRCSR_SUPPLY | CRCSR_RDCHK | CRCSR_OFFLINE)) {
crs |= CSR_ERR | (curr_crs & (CRCSR_SUPPLY | CRCSR_RDCHK |
CRCSR_OFFLINE));
if (crs & CSR_IE) SET_INT(CR);
}
if (blowerState != BLOW_ON) {
blowerState = BLOW_START;
sim_activate_after (&cr_unit, spinUp);
} else {
sim_activate_after (&cr_unit, cr_unit.wait);
}
}
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_wr data %06o crs %06o\n",
data, crs);
} else { /* CD11 */
if (access == WRITEB)
data = (PA & 1)? (((data & 0xff)<<8) | (cdst & 0x00ff)):
((data & 0x00ff) | (cdst & 0xFF00));
if (data & CDCSR_PWRCLR) {
CLR_INT (CR);
sim_cancel (&cr_unit);
cdcc = 0;
cdba = 0;
cddb = 0;
cddbs = 0;
if (!(cr_unit.flags & UNIT_ATT)) { /* Clear troublesome bits, but leave error/offline */
cdst &= ~(CSR_IE | CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | CDCSR_RDY |
CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | CDCSR_PACK);
cdst |= CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK;
cddbs |= CDDB_STACK;
break;
}
crs &= ~CRCSR_BUSY;
cdst &= (CDCSR_OFFLINE | CDCSR_RDY | CDCSR_HOPPER);
if( (cr_unit.flags & UNIT_ATT) && !feof(cr_unit.fileref) && !ferror(cr_unit.fileref) )
cdst &= ~(CDCSR_HOPPER);
if (cdst & (CDCSR_ANYERR))
cdst |= CSR_ERR;
cdst |= CDCSR_RDY;
break;
}
if (data & CSR_GO) {
/* To simplify the service code, don't start if CDCC == 0.
* In the hardware, it's not sensible...
*/
if ((crs & CRCSR_BUSY) || (cdcc == 0)) {
cdst |= (CDCSR_RDRCHK | CDCSR_HOPPER | CSR_ERR);
} else {
cdst &= ~(CDCSR_RDRCHK | CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | CDCSR_RDY | CDCSR_ONLINE);
cdst = (cdst & ~(CDCSR_EOF | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_PACK | CDCSR_HOPPER))
| (data & (CDCSR_EOF | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_PACK));
cddbs &= ~(CDDB_READ|CDDB_PICK|CDDB_STACK);
/* Always attempt to start. If not attached, errors will set after delay */
if (!(cdst & CDCSR_HOPPER) )
cdst &= ~(CSR_ERR);
if (blowerState != BLOW_ON) {
blowerState = BLOW_START;
sim_activate_after (&cr_unit, spinUp);
} else {
sim_activate_after (&cr_unit, cr_unit.wait);
}
}
} else {
cdst = (cdst & ~(CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_DATAERR | CDCSR_LATE |
CDCSR_NXM | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | CDCSR_PACK))
|(data & (CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_DATAERR | CDCSR_LATE |
CDCSR_NXM | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | CDCSR_PACK));
}
/* Apparently the hardware does not SET_INT if ready/online are already set. If it did, TOPS-10's driver wouldn't work */
if (!(cdst & CSR_IE))
CLR_INT (CR);
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_wr data %06o cdst %06o\n",
data, cdst);
}
break;
case 1:
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_wr cdcc %06o\n", data);
if (CD11_CTL(&cr_unit))
cdcc = data & 0177777;
break;
case 2:
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_wr crba %06o\n", data);
if (CD11_CTL(&cr_unit))
cdba = data & 0177777;
break;
case 3:
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_wr cddb/crm %06o\n", data);
/* ignore writes to cddb */
if (CD11_CTL(&cr_unit))
break;
/* fixup data for byte writes and read-modify-write */
if (access == WRITEB)
data = (PA & 1) ?
(crm & 0377) | (data << 8) :
(crm & ~0377) | (data & 0377);
crm = data & 0177777;
/* not 100% certain how these work */
if (!(crm & CRM_MAINT))
break;
crs = (crm & CRM_BUSY) ?
(crs | CRCSR_BUSY) : (crs & ~CRCSR_BUSY);
crs = (crm & CRM_READY) ?
(crs | CRCSR_OFFLINE) : (crs & ~CRCSR_OFFLINE);
crs = (crm & CRM_HOPPER) ?
(crs | CRCSR_SUPPLY | CRCSR_RDCHK) :
(crs & ~(CRCSR_SUPPLY | CRCSR_RDCHK));
crb1 = crm & 07777; /* load low 12 bits */
break;
default:
/* can't happen */
break;
}
return (SCPE_OK);
}
/*
* Interrupt acknowledge routine
* Reschedule service routine if needed (based on
* schedule_svc flag).
* Do the actual scheduling just for the CR11 (VAX/PDP11). The PDP10 does
* not seem to call this entry point.
*/
int32 cr_intac() {
if CR11_CTL(&cr_unit) {
if (schedule_svc) {
sim_activate_after (&cr_unit, cr_unit.wait);
schedule_svc = 0;
}
}
return cr_dib.vec; /* Constant interrupt vector */
}
/*
Enter the service routine once for each column read from the card.
CR state bits drive this primarily (see _BUSY and _CRDDONE). However,
when in CD mode, also execute one column of DMA input.
*/
t_stat cr_svc ( UNIT *uptr )
{
uint32 pa;
uint8 c;
uint16 w;
int n;
/* Blower stopping: set it to OFF and do nothing */
if (blowerState == BLOW_STOP) {
blowerState = BLOW_OFF;
return (SCPE_OK);
}
/* Blower starting: set it to ON and do regular service */
if (blowerState == BLOW_START)
blowerState = BLOW_ON;
/* (almost) anything we do now will cause a CR (But not a CD) interrupt */
if ((CR11_CTL(uptr)) && (crs & CSR_IE))
SET_INT (CR);
/* Unit not attached, or error status while idle */
if (!(uptr->flags & UNIT_ATT) || (!(crs & CRCSR_BUSY) && ((CR11_CTL(uptr)?crs : cdst) & CSR_ERR))) {
if (CD11_CTL(uptr)) {
if (!(uptr->flags & UNIT_ATT)){
cdst |= (CDCSR_HOPPER | CDCSR_RDRCHK | CDCSR_OFFLINE | CSR_ERR);
cddbs |= CDDB_STACK;
}
if (cdst & CSR_IE)
SET_INT (CR);
}
return (SCPE_OK);
}
/* End of card: unit busy and column past end column */
if ((crs & CRCSR_BUSY) && (currCol > colEnd)) {
/* clear busy state and set card done bit */
crs &= ~(CRCSR_BUSY | CRCSR_COLRDY);
crs |= CRCSR_CRDDONE;
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_svc card done\n");
/* Check CD11 error status that stops transfers */
if (CD11_CTL(uptr) && (cdst & (CDCSR_LATE | CDCSR_NXM))) {
cdst |= CSR_ERR | CDCSR_OFFLINE | CDCSR_RDY | CDCSR_RDRCHK;
SET_INT (CR);
return (SCPE_OK);
}
if (CR11_CTL(uptr))
return (SCPE_OK);
/* If a CD11 gets this far, an interrupt is required. If CDCC != 0,
* continue reading the next card.
*/
SET_INT (CR);
if (cdcc == 0)
return (SCPE_OK);
}
/* If unit is not busy: try to read a card */
if (!(crs & CRCSR_BUSY)) {
crs &= ~CRCSR_CRDDONE; /* This line WAS commented out - JGP 2013.02.05 */
/* Call the appropriate read card routine.
* If no card is read (FALSE return), we tried to read with an empty hopper.
* The card read routine set the appropriate error bits. Shutdown.
*/
if (!readRtn (uptr, hcard, ccard, acard)) {
blowerState = BLOW_STOP;
if (CD11_CTL(uptr)) {
readFault:
cdst |= CDCSR_RDY;
if (cdst & (CDCSR_RDRCHK | CDCSR_HOPPER))
cdst |= CSR_ERR | CDCSR_OFFLINE;
if (cdst & CSR_IE)
SET_INT (CR);
} else {
/*
* CR11 handling: assert SUPPLY and ERROR bits,
* put de device offline and DO NOT TRIGGER AN INTERRUPT
* (if the interrupt is asserted RSX and VMS will get 80
* bytes of garbage, and RSX could crash).
*/
if (crs & (CRCSR_RDCHK | CRCSR_SUPPLY)) {
crs |= CSR_ERR | CRCSR_OFFLINE;
crs &= ~(CRCSR_ONLINE | CRCSR_BUSY | CRCSR_CRDDONE);
CLR_INT(CR);
}
}
sim_activate_after (uptr, spinDown);
return (SCPE_OK);
}
/* Card read: reset column counter and assert BUSY */
currCol = colStart;
crs |= CRCSR_BUSY;
/* Update status if this read emptied hopper.
* The CR11 doesn't set SUPPLY until after the last card is read.
*/
/* I/O error status bits have been set during read.
* Look ahead to see if another card is in file.
*/
n = feof (uptr->fileref);
if (n)
n = EOF;
else {
n = fgetc (uptr->fileref);
if (n != EOF)
ungetc (n, uptr->fileref);
}
if ((n == EOF) && ((EOFcard > 0) || !(uptr->flags & UNIT_AUTOEOF))) {
/* EOF and generated EOFcard sent or not an autoEOF unit.
* Set status to reflect last card taken.
*/
cdst |= (CDCSR_RDRCHK | CSR_ERR | CDCSR_OFFLINE | CDCSR_HOPPER);
if (eofPending) {
cdst |= CDCSR_EOF;
eofPending = FALSE;
}
}
if (EOFcard)
EOFcard = 1;
if (CD11_CTL(uptr)) {
/* Handle read check: punches in col 0 or 81/last (DEC only did 80 cols, but...) */
if ((uptr->flags & UNIT_RDCHECK) &&
(((colStart == 0) && (hcard[0] != 0)) || ((colEnd & 1) && (hcard[colEnd] != 0)))) {
cdst |= (CDCSR_RDRCHK | CSR_ERR);
cddbs |= CDDB_READ;
if (1) /* 0 if read check should transfer card */
goto readFault;
}
/* CDDB_PICK, CDDB_STACK, <stacker full) */
}
}
/* check for overrun (timing error) */
if (CR11_CTL(uptr) && (crs & CRCSR_COLRDY))
crs |= CSR_ERR | CRCSR_TIMERR;
/* Update the "buffer" registers with current column */
crb1 = hcard[currCol] & 07777; /* Hollerith value */
crb2 = ccard[currCol] & 0377; /* DEC compressed hollerith value */
uptr->buf = acard[currCol] & 0377; /* Helpful for debug: ASCII value */
/* CD11 specific code follows */
if (CD11_CTL(uptr)) {
pa = cdba | ((cdst & 060) << 12);
/*
The implementation of _NXM here is not quite the same as I interpret
the (limited) documentaiton I have to indicate. However the effect
should be similar. Documentation indicates that once _NXM is set,
further NPR requests are inhibited though the card is allowed to
read until completion. This implies that CDBA and the XBA bits are
incremented accordingly, even though no data transfer occurs. This
code detects and flags the NXM condition but allows attempts at
subsequent memory writes, thus insuring the address registers are
incremented properly. If this causes problems, I'll fix it.
*/
if (cdst & CDCSR_PACK)
cddb = c = ccard[currCol] & 0377;
else
cddb = w = hcard[currCol] & 07777; /* Punched zones: <12><11><0><1><2><3><4><5><6><7><8><9> */
if (cdcc == 0) /* Transfer requires CC non-zero */
cdst |= CDCSR_LATE;
else {
if (cdst & CDCSR_PACK) {
if (Map_WriteB (pa, 1, &c))
cdst |= CDCSR_NXM;
pa = (pa + 1) & 0777777;
} else {
/* "Augmented Image" - provides full column binary and packed encoding in 15 bits.
* Bits <14:12> encode which zone, if any, of 1-7 is punched. 0 => none, otherwise zone #.
* Bit 15 set indicates that more than one punch occured in zones 1-7; in this case the packed
* encoding is not valid. (Card may be binary data.)
* This was probably an ECO to the CD11. TOPS-10/20 depend on it, so it's definitely in the CD20.
*/
if (uptr->flags & UNIT_AIECO) {
uint16 z;
w |= ((ccard[currCol] & 07) << 12); /* Encode zones 1..7 - same as 'packed' format */
z = w & 0774;
if ((z & -z) != z) /* More than one punch in 1..7 */
w |= 0100000; /* sets Hollerith (encoding) failure (not an error) */
}
if (Map_WriteW (pa, 2, &w))
cdst |= CDCSR_NXM;
pa = (pa + 2) & 0777777;
}
cdba = pa & 0177777;
cdst = (cdst & ~(CDCSR_XBA17|CDCSR_XBA16)) |
((pa & 0600000) >> 12);
cdcc = (cdcc + 1) & 0177777;
/* Interrupt at end of buffer; read continues to end of card.
* If this is the last column, defer interrupt so end doesn't interrupt again.
*/
if ((cdcc == 0) && (cdst & CSR_IE) && (currCol < colEnd))
SET_INT (CR);
}
} else { /* CR11 */
/* Handle EJECT bit: if set DO NOT assert COLRDY */
/* nor interrupt */
if ((crs & CRCSR_EJECT)) {
CLR_INT (CR);
} else {
crs |= CRCSR_COLRDY;
}
}
/* CD11 and CR11 */
currCol++; /* advance the column counter */
/* Schedule next service cycle */
/* CR11 (VAX/PDP11): just raise the schedule_svc flag; the intack
* routine will do the actual rescheduling.
* CD11/20 (PDP10): Do the rescheduling (the intack seems to do nothing)
*/
if (CD11_CTL(uptr)) {
sim_activate_after(uptr, uptr->wait);
} else {
schedule_svc = 1;
}
return (SCPE_OK);
}
t_stat cr_reset ( DEVICE *dptr )
{
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_reset\n");
if (!translation_help) {
size_t i;
const char trans_hlp[] = "TRANSLATION={";
size_t size = sizeof(trans_hlp) +1;
for ( i = 0; i < NTRANS; i++ )
size += strlen (transcodes[i].name)+1;
translation_help = (char *)malloc (size );
strcpy(translation_help, trans_hlp);
for (i = 0; i < NTRANS; i++) {
strcat(translation_help, transcodes[i].name);
strcat(translation_help,"|");
}
strcpy(translation_help+strlen(translation_help)-1, "}");
for (i = 0; i < (sizeof cr_mod / sizeof cr_mod[0]); i++ )
if (cr_mod[i].pstring && !strcmp(cr_mod[i].pstring, "TRANSLATION")) {
cr_mod[i].mstring = translation_help;
break;
}
}
cr_unit.buf = 0;
currCol = 1;
crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_TIMERR|CRCSR_ONLINE|CRCSR_BUSY|
CRCSR_COLRDY|CSR_IE|CRCSR_EJECT|CSR_GO);
if (crs & (CRCSR_OFFLINE))
crs |= CSR_ERR;
crb1 = 0;
crb2 = 0;
crm = 0;
cdst &= ~(CSR_ERR|CDCSR_RDRCHK|CDCSR_EOF|CDCSR_DATAERR|CDCSR_LATE|
CDCSR_NXM|CSR_IE|CDCSR_XBA17|CDCSR_XBA16|CDCSR_ONLINE|
CDCSR_PACK|CSR_GO);
cdst |= CDCSR_RDY;
if (cdst & CDCSR_ANYERR)
cdst |= CSR_ERR;
cdcc = 0;
cdba = 0;
cddb = 0;
/* ATTACHed doesn't mean ONLINE; set CR reset (pushing the reset switch)
* is what puts the reader on-line. Reset doesn't control fingers.
*/
if ((cr_unit.flags & UNIT_ATT) && !feof (cr_unit.fileref)) {
if (!(crs & CRCSR_OFFLINE))
crs |= CRCSR_ONLINE; /* non-standard */
crs &= ~(CRCSR_RDCHK | CRCSR_SUPPLY );
cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER);
cddbs = 0;
} else {
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;
cddbs |= CDDB_STACK;
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY;
}
sim_cancel (&cr_unit); /* deactivate unit */
if (blowerState != BLOW_OFF) {
blowerState = BLOW_STOP;
sim_activate_after (&cr_unit, spinDown);
}
EOFcard = 0;
CLR_INT (CR);
/* TBD: flush current card */
/* init uptr->wait ? */
return auto_config (dptr->name, 1);
}
/*
Handle the interface status and SIMH portion of the ATTACH. Another
routine is used to evaluate the file and initialize other state
globals correctly.
*/
#define MASK (SWMASK('A')|SWMASK('B')|SWMASK('I')|SWMASK('R'))
/* Attach unit */
/* This should simulate physically putting a stack of cards into the hopper */
/* No bits should change, nor an interrupt should be asserted */
/* This is a change of behaviour respect to the previous code */
t_stat cr_attach ( UNIT *uptr,
char *cptr )
{
t_stat reason;
if (sim_switches & ~MASK)
return (SCPE_INVSW);
/* file must previously exist; kludge */
sim_switches |= SWMASK ('R');
reason = attach_unit (uptr, cptr);
if(uptr->flags & UNIT_ATT) {
setupCardFile(uptr, sim_switches);
}
return (reason);
}
/* Detach unit: assert SUPPLY and OFFLINE bits (and ERR) */
t_stat cr_detach ( UNIT *uptr )
{
crs |= CSR_ERR | CRCSR_SUPPLY | CRCSR_OFFLINE;
/* interrupt? */
crs &= ~CRCSR_ONLINE;
cdst |= CSR_ERR | CDCSR_HOPPER | CDCSR_OFFLINE;
cardFormat = "unknown";
if (blowerState != BLOW_OFF) {
blowerState = BLOW_STOP;
sim_activate_after (uptr, spinDown);
}
return (detach_unit (uptr));
}
#if defined (CR11_OK) || defined (CD11_OK) || defined (CD20_OK)
t_stat cr_set_type ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
DEVICE *dptr = find_dev_from_unit (uptr);
/* disallow type change if currently attached */
if (uptr->flags & UNIT_ATT)
return (SCPE_NOFNC);
if (val == UNIT_CR11) {
dptr->flags |= DEV_QBUS; /* Can be a Qbus device - programmed I/O only */
} else { /* CD11/CD20 are 18bit DMA devices */
if (!UNIBUS)
return SCPE_NOFNC;
dptr->flags &= ~DEV_QBUS; /* Not on a Qbus (22bit) */
}
cpm = (val & UNIT_CR11) ? 285 : ((val & UNIT_CD20)? 1200 :1000);
uptr->wait = (60 * 1000000) / (cpm * 80); /* Time between columns in usec.
* Readers are rated in card/min for 80 column cards */
transcodes[0].table = (val & UNIT_CD20)? o29_decascii_code : o29_code;
return (SCPE_OK);
}
#endif
#if defined (AIECO_OK)
t_stat cr_set_aieco ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
/* disallow eco change if currently attached or not CD20 */
if (uptr->flags & UNIT_ATT || !CD20_CTL(uptr))
return (SCPE_NOFNC);
uptr->flags = (uptr->flags & ~UNIT_AIECO) | (val & UNIT_AIECO);
return (SCPE_OK);
}
#endif
t_stat cr_show_format ( FILE *st,
UNIT *uptr,
int32 val,
void *desc )
{
fprintf (st, "%s format", cardFormat);
return (SCPE_OK);
}
t_stat cr_set_rate ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
t_stat status = SCPE_OK;
int32 i;
if (!cptr)
return (SCPE_MISVAL);
if (strcmp (cptr, "DEFAULT") == 0)
i = CR11_CTL(uptr) ? 285 : (CD20_CTL(uptr)? 1200 :1000);
else
i = (int32) get_uint (cptr, 10, 0xFFFFFFFF, &status);
if (status == SCPE_OK) {
if (i < 200 || i > 1200)
status = SCPE_ARG;
else {
cpm = i;
uptr->wait = (60 * 1000000) / (cpm * 80); /* Time between columns in usec.
* Readers are rated in card/min for 80 column cards */
}
}
return (status);
}
t_stat cr_show_rate ( FILE *st,
UNIT *uptr,
int32 val,
void *desc )
{
fprintf (st, "%d cards per minute", cpm);
return (SCPE_OK);
}
/* simulate pressing the card reader RESET button */
/* Per CR11 docs, transition to ONLINE, reset card */
/* reader logic. */
/* RESET is somewhat of a misnomer; START is the function */
t_stat cr_set_reset ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_set_reset\n");
/*
Ignore the RESET switch while a read cycle is in progress or the
unit simply is not attached.
*/
if ((crs & CRCSR_BUSY) || !(uptr->flags & UNIT_ATT))
return (SCPE_OK);
/* if no errors, signal transition to on line */
crs |= CRCSR_ONLINE;
/* Clear error bits */
crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_SUPPLY|CRCSR_RDCHK|CRCSR_TIMERR|
CRCSR_OFFLINE|CRCSR_BUSY|CRCSR_COLRDY|CRCSR_EJECT|CSR_GO);
cdst |= CDCSR_ONLINE;
cdst &= ~(CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK | CDCSR_HOPPER |
CDCSR_EOF);
/* I don't think the hardware clears these errors, but TOPS-10 seems to expect it.
* Since we know the reader is idle, and this is OPR intervention, it seems safe.
*/
cdst &= ~(CDCSR_LATE | CDCSR_NXM);
/* Assert interrupt if interrupts enabled */
if ((CR11_CTL(uptr)?crs : cdst) & CSR_IE) {
SET_INT (CR);
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "cr_set_reset setting interrupt\n");
}
/* Reset controller status */
cr_unit.buf = 0;
currCol = 1;
crb1 = 0;
crb2 = 0;
cdcc = 0;
cdba = 0;
cddb = 0;
cddbs = 0;
EOFcard = 0;
/* start up the blower if the hopper is not empty
if (blowerState != BLOW_ON) {
blowerState = BLOW_START;
sim_activate_after(uptr, spinUp);
}
*/
return (SCPE_OK);
}
/* simulate pressing the card reader STOP button */
t_stat cr_set_stop ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "set_stop\n");
crs &= ~CRCSR_ONLINE;
crs |= CSR_ERR | CRCSR_OFFLINE;
cdst |= CSR_ERR | CDCSR_OFFLINE;
/* CD11 does not appear to interrupt on STOP. */
if (CR11_CTL(uptr) && (crs & CSR_IE))
SET_INT (CR);
if (blowerState != BLOW_OFF) {
blowerState = BLOW_STOP;
}
return (SCPE_OK);
}
/* simulate pressing the card reader EOF button */
t_stat cr_set_eof ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
if (DEBUG_PRS (cr_dev))
fprintf (sim_deb, "set_eof\n");
eofPending = 1;
return (SCPE_OK);
}
t_stat cr_show_eof ( FILE *st,
UNIT *uptr,
int32 val,
void *desc )
{
fprintf (st, (eofPending? "EOF pending": "no EOF pending"));
return (SCPE_OK);
}
t_stat cr_set_trans ( UNIT *uptr,
int32 val,
char *cptr,
void *desc )
{
size_t i;
if (!cptr)
return (SCPE_MISVAL);
for (i = 0; i < NTRANS; i++) {
if (strcmp (cptr, transcodes[i].name) == 0)
break;
}
if (i >= NTRANS)
return (SCPE_ARG);
codeTbl = transcodes[i].table;
initTranslation (); /* reinitialize tables */
return (SCPE_OK);
}
t_stat cr_show_trans ( FILE *st,
UNIT *uptr,
int32 val,
void *desc )
{
size_t i;
for (i = 1; i < NTRANS; i++ )
if (transcodes[i].table == codeTbl) {
fprintf (st, "translation=%s", transcodes[i].name);
return SCPE_OK;
}
fprintf (st, "translation=%s", transcodes[0].name);
return (SCPE_OK);
}
/* Only used from here to EOF, so not passing size of string.
* This ugliness is more maintainable than a preprocessor mess.
*/
static void cr_supported ( char *string, int32 *bits )
{
int32 crtypes = 0;
#define MAXDESCRIP sizeof ("CR11/CD11/CD20/") /* sizeof includes \0 */
char devtype[MAXDESCRIP] = "";
#if defined (CR11_ONLY) || defined (CR11_OK)
crtypes |= 1;
#endif
#if defined (CD11_ONLY) || defined (CD11_OK)
crtypes |= 2;
#endif
#if defined (CD20_ONLY) || defined (CD20_OK)
crtypes |= 4;
#endif
if (string) {
if (crtypes & 1)
strcat (devtype, "CR11/");
if (crtypes & 2)
strcat (devtype, "CD11/");
if (crtypes & 4)
strcat (devtype, "CD20/");
devtype[strlen(devtype)-1] = '\0';
strcpy (string, devtype);
}
if (bits)
*bits = crtypes;
return;
}
static t_stat cr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
char devtype[MAXDESCRIP];
int32 crtypes;
cr_supported (devtype, &crtypes);
fprintf (st, "%s Card Reader (CR)\n\n", devtype);
fprintf (st, "The card reader (CR) implements a single controller (the model(s) shown\n");
fprintf (st, "above) and a card reader (e.g., Documation M200, GDI Model 100) by reading a\n");
fprintf (st, "file and presenting lines or cards to the simulator. Card decks may be\n");
fprintf (st, "represented by plain text ASCII files, card image files, or column binary\n");
fprintf (st, "files.\n\n");
fprintf (st, "The controller is also compatible with the CM11-F, CME11, and CMS11.\n\n");
fprintf (st, "Card image files are a file format designed by Douglas W. Jones at the\n");
fprintf (st, "University of Iowa to support the interchange of card deck data. These files\n");
fprintf (st, "have a much richer information carrying capacity than plain ASCII files. Card\n");
fprintf (st, "Image files can contain such interchange information as card-stock color,\n");
fprintf (st, "corner cuts, special artwork, as well as the binary punch data representing all\n");
fprintf (st, "12 columns. Complete details on the format, as well as sample code, are\n");
fprintf (st, "available at Prof. Jones's site: http://www.cs.uiowa.edu/~jones/cards/.\n\n");
if ((crtypes & -crtypes) != crtypes) {
fprintf (st, "The card reader device an be configured to emulate the following\n");
fprintf (st, "controller models with these commands:\n\n");
if (crtypes & 1)
fprintf (st, " SET CR CR11 set controller type to CR11\n");
if (crtypes & 2)
fprintf (st, " SET CR CD11 set controller type to CD11\n");
if (crtypes & 4) {
fprintf (st, " SET CR CD20 set controller type to CD20\n");
#if defined (AIECO_OK)
fprintf (st, " SET CR AIECO emulate the CD20 \"augmented image\" ECO\n");
fprintf (st, " default is %semulated.\n", (DFLT_AIECO? "":"not "));
#endif
}
fprintf (st, "\nThe controller type must be set before attaching a virtual card deck to the\n");
fprintf (st, "device. You may NOT change controller type once a file is attached.\n\n");
fprintf (st, "The primary differences between the controllers are summarized in the\n");
fprintf (st, "table below. By default, %s simulation is selected.\n\n",
(DFLT_TYPE & UNIT_CD20)? "CD20": ((DFLT_TYPE & UNIT_CR11)? "CR11" : "CD11"));
fprintf (st, " CR11 CD11/CD20\n");
fprintf (st, " BR 6 4\n");
fprintf (st, " registers 4 3\n");
fprintf (st, " data transfer BR DMA\n");
fprintf (st, " card rate 200-600 1000-1200\n");
fprintf (st, " hopper cap. <= 1000 1000-2250\n");
fprintf (st, " cards Mark-sense & punched only\n");
fprintf (st, " punched\n\n");
fprintf (st, "The CD11 simulation includes the Rev. J modification to make the CDDB act as\n");
fprintf (st, "a second status register during non-data transfer periods.\n\n");
}
if (crtypes & 1) {
fprintf (st, "Examples of the CR11 include the M8290 and M8291 (CMS11). All card readers use\n");
fprintf (st, "a common vector at 0230 and CSR at 177160. Even though the CR11 is normally\n");
fprintf (st, "configured as a BR6 device, it is configured for BR4 in this simulation.\n\n");
}
fprintf (st, "The card reader supports ASCII, card image, and column binary format card\n");
fprintf (st, "\"decks.\" When reading plain ASCII files, lines longer than 80 characters are\n");
fprintf (st, "silently truncated. Card image support is included for 80 column Hollerith,\n");
fprintf (st, "82 column Hollerith, and 40 column Hollerith (mark-sense) cards. \n");
fprintf (st, "Column binary supports 80 column card images only.\n");
if (crtypes & 6) {
fprintf (st, "The CD11/CD20 optionally check columns 0/81/41 for punches, which produce\n");
fprintf( st, "read check errors. As verifiers may produce these, this can be controlled:\n");
fprintf( st, " SET CR RDCHECK - Enable read check errors (default)\n");
fprintf( st, " SET CR NORDCHECK - Disable read check errors\n\n");
}
fprintf (st, "All files are attached read-only (as if the -R switch were given).\n");
fprintf (st, " ATTACH -A CR <file> file is ASCII text\n");
fprintf (st, " ATTACH -B CR <file> file is column binary\n");
fprintf (st, " ATTACH -I CR <file> file is card image format\n\n");
fprintf (st, "If no flags are given, the file extension is evaluated. If the filename ends\n");
fprintf (st, "in .TXT, the file is treated as ASCII text. If the filename ends in .CBN, the\n");
fprintf (st, "file is treated as column binary. Otherwise, the CR driver looks for a card\n");
fprintf (st, "image header. If a correct header is found the file is treated as card image\n");
fprintf (st, "format, otherwise it is treated as ASCII text.\n\n");
fprintf (st, "The correct character translation MUST be set if a plain text file is to be\n");
fprintf (st, "used for card deck input. The correct translation SHOULD be set to allow\n");
fprintf (st, "correct ASCII debugging of a card image or column binary input deck. Depending\n");
fprintf (st, "upon the operating system in use, how it was generated, and how the card data\n");
fprintf (st, "will be read and used, the translation must be set correctly so that the proper\n");
fprintf (st, "character set is used by the driver. Use the following command to explicitly\n");
fprintf (st, "set the correct translation:\n\n");
fprintf (st, " SET TRANSLATION={DEFAULT|026|026FTN|026DEC|026DECASCII|029|029DECASCII|EBCDIC}\n\n");
fprintf (st, "This command should be given after a deck is attached to the simulator. The\n");
fprintf (st, "mappings above are completely described at\n");
fprintf (st, " http://www.cs.uiowa.edu/~jones/cards/codes.html.\n");
fprintf (st, "Note that early DEC software typically used 029 or 026FTN mappings.\n");
fprintf (st, "Later systems used the 026DECASCII and/or 029DECASCII mappings, which include all 7-bit ASCII characters\n");
fprintf (st, "DEC operating systems used a variety of methods to determine the end of a deck\n");
fprintf (st, "(recognizing that 'hopper empty' does not necessarily mean the end of a deck.\n");
fprintf (st, "Below is a summary of the various operating system conventions for signaling\n");
fprintf (st, "end of deck (or end of file with multi-file batch systems):\n\n");
fprintf (st, " RT-11: 12-11-0-1-6-7-8-9 punch in column 1\n");
fprintf (st, " RSTS/E: 12-11-0-1 or 12-11-0-1-6-7-8-9 punch in column 1\n");
fprintf (st, " RSX: 12-11-0-1-6-7-8-9 punch in first 8 columns\n");
fprintf (st, " VMS: 12-11-0-1-6-7-8-9 punch in first 8 columns\n");
fprintf (st, " TOPS: 12-11-0-1 or 12-11-0-1-6-7-8-9 punch in column 1\n\n");
fprintf (st, "Using the AUTOEOF setting, the card reader can be set to automatically generate\n");
fprintf (st, "an EOF card consisting of the 12-11-0-1-6-7-8-9 punch in columns 1-8. ");
if (crtypes & 6) {
fprintf (st, "When set,\nThe %s ", ((crtypes & 6) == 2)? "CD11": ((crtypes & 6) == 4)? "CD20": "CD11/CD20");
fprintf (st, "will automatically set the EOF bit in the\n");
fprintf (st, "controller after the EOF card has been processed. By default AUTOEOF is enabled.\n");
fprintf (st, "The controller also supports an EOF switch that will set the EOF bit when the\n");
fprintf (st, "hopper empties. The switch resets each time the hopper empties. The SET EOF command emulates this.\n");
if (crtypes &1)
fprintf (st, "The CR11 does not support the EOF switch/bit.\n");
else
fprintf (st, "\n");
}
fprintf (st, "The default card reader rate for the ");
if (crtypes & 4) {
fprintf (st, "CD20 is 1200");
if (crtypes != 4)
fprintf (st, " and for the ");
}
if (crtypes & 3)
fprintf (st, "CR/CD11 is 285");
fprintf (st, " cpm.\n");
fprintf (st, "The reader rate can be set to its default value or to anywhere in the range\n");
fprintf (st, "of 200 to 1200 cpm.This rate may be changed while the unit is attached.\n\n");
fprintf (st, "It is standard operating procedure for operators to load a card deck and press\n");
fprintf (st, "the momentary action RESET button to clear any error conditions and alert the\n");
fprintf (st, "processor that a deck is available to read. Use the SET CR RESET command to\n");
fprintf (st, "simulate pressing the card reader RESET button.\n\n");
fprintf (st, "Another common control of physical card readers is the STOP button. An\n");
fprintf (st, "operator could use this button to finish the read operation for the current\n");
fprintf (st, "card and terminate reading a deck early. Use the SET CR STOP command to\n");
fprintf (st, "simulate pressing the card reader STOP button.\n\n");
fprintf (st, "The simulator does not support the BOOT command. The simulator does not\n");
fprintf (st, "stop on file I/O errors. Instead the controller signals a reader check to\n");
fprintf (st, "the CPU.\n");
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *cr_description (DEVICE *dptr)
{
/* Not thread-safe, but malloc() would be leak. */
static char desc[MAXDESCRIP+sizeof(" card reader")-1] = "";
if (desc[0] == '\0') {
cr_supported (desc, NULL);
strcat (desc, " card reader");
}
return desc;
}