/* pdp11_cr.c: CR/CM/CD-11 card reader simulator | |
Copyright (c) 2005, 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 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 | |
CDRSER.MAC - TOPS card reader driver source | |
http://pdp-10.trailing-edge.com/custsupcuspmar86_bb-x130b-sb/02/cdrser.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. No PDP-10 support; volunteers needed | |
7. 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. | |
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? | |
Don't have any information about Unix or Ultrix-11 yet. Same | |
for VAX Unices. | |
TOPS: it appears that both the CD11 and CR11 were supported. | |
I don't have any knowledge on how to make this work with the | |
PDP-10 simulation, though. | |
Revision History: | |
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 | |
*/ | |
#if defined (VM_PDP10) /* PDP10 version */ | |
#include "pdp10_defs.h" | |
extern int32 int_req; | |
#elif defined (VM_VAX) /* VAX version */ | |
#include "vax_defs.h" | |
extern int32 int_req[IPL_HLVL]; | |
#else /* PDP-11 version */ | |
#include "pdp11_defs.h" | |
extern int32 int_req[IPL_HLVL]; | |
#endif | |
extern FILE *sim_deb; /* sim_console.c */ | |
/* 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_CR11 (UNIT_V_UF + 0) | |
#define UNIT_CR11 (1u << UNIT_V_CR11) | |
#define UNIT_V_AUTOEOF (UNIT_V_UF + 1) | |
#define UNIT_AUTOEOF (1u << UNIT_V_AUTOEOF) | |
#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 */ | |
#define CDCSR_V_RDRCHK 14 /* reader check */ | |
#define CDCSR_V_EOF 13 /* CD11-E EOF button */ | |
#define CDCSR_V_OFFLINE 12 /* off line */ | |
#define CDCSR_V_DATAERR 11 /* data 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 */ | |
#define CDCSR_V_XBA17 5 | |
#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 */ | |
#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_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) | |
/* 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 char *cardFormat = "unknown"; | |
static t_bool (*readRtn)(FILE *, 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 int table = 3; /* character translation table */ | |
static const int *codeTbl = o29_code; /* punch translation table */ | |
static int32 blowerState = BLOW_OFF; /* reader vacuum/blower motor */ | |
static int32 spinUp = 3000; /* blower spin-up time: 3 seconds */ | |
static int32 spinDown = 2000; /* blower spin-down time: 2 seconds */ | |
static t_bool EOFcard = FALSE; /* played special card yet? */ | |
static int32 cpm = 285; /* reader rate: cards per minute */ | |
/* 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 = 0; /* 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 = 0; /* control/status */ | |
static int32 cdcc = 0; /* column count */ | |
static int32 cdba = 0; /* current address, low 16 bits */ | |
static int32 cddb = 0; /* data, 2nd status */ | |
/* forward references */ | |
DEVICE cr_dev; | |
static void setupCardFile (UNIT *, int32); | |
t_stat cr_rd (int32 *, int32, int32); | |
t_stat cr_wr (int32, int32, int32); | |
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_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_trans (UNIT *, int32, char*, void *); | |
t_stat cr_show_trans (FILE *, UNIT *, int32, void *); | |
/* 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 | |
*/ | |
static DIB cr_dib = { IOBA_CR, IOLN_CR, &cr_rd, &cr_wr, | |
1, IVCL (CR), VEC_CR, { NULL } }; | |
static UNIT cr_unit = { | |
UDATA (&cr_svc, | |
UNIT_ATTABLE+UNIT_SEQ+UNIT_ROABLE+UNIT_DISABLE+ | |
UNIT_CR11+UNIT_AUTOEOF, 0), | |
(60 * 1000) / 285 }; | |
static const REG cr_reg[] = { | |
{ GRDATA (BUF, cr_unit.buf, DEV_RDX, 8, 0) }, | |
{ GRDATA (CRS, crs, DEV_RDX, 16, 0) }, | |
{ GRDATA (CRB1, crb1, DEV_RDX, 16, 0) }, | |
{ GRDATA (CRB2, crb2, DEV_RDX, 16, 0) }, | |
{ GRDATA (CRM, crm, DEV_RDX, 16, 0) }, | |
{ GRDATA (CDST, cdst, DEV_RDX, 16, 0) }, | |
{ GRDATA (CDCC, cdcc, DEV_RDX, 16, 0) }, | |
{ GRDATA (CDBA, cdba, DEV_RDX, 16, 0) }, | |
{ GRDATA (CDDB, cddb, DEV_RDX, 16, 0) }, | |
{ GRDATA (BLOWER, blowerState, DEV_RDX, 2, 0) }, | |
{ FLDATA (INT, IREQ (CR), INT_V_CR) }, | |
{ FLDATA (ERR, crs, CSR_V_ERR) }, | |
{ FLDATA (IE, crs, CSR_V_IE) }, | |
{ DRDATA (POS, cr_unit.pos, T_ADDR_W), PV_LEFT }, | |
{ DRDATA (TIME, cr_unit.wait, 24), 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 const MTAB cr_mod[] = { | |
{ UNIT_CR11, UNIT_CR11, "CR11", "CR11", &cr_set_type }, | |
{ UNIT_CR11, 0, "CD11", "CD11", &cr_set_type }, | |
{ UNIT_AUTOEOF, UNIT_AUTOEOF, "auto EOF", "AUTOEOF", NULL }, | |
{ UNIT_AUTOEOF, 0, "no auto EOF", "NOAUTOEOF", NULL }, | |
/* card reader RESET switch */ | |
{ MTAB_XTD|MTAB_VDV, 0, NULL, "RESET", | |
&cr_set_reset, NULL, NULL }, | |
/* card reader STOP switch */ | |
{ MTAB_XTD|MTAB_VDV, 0, NULL, "STOP", | |
&cr_set_stop, NULL, NULL }, | |
{ MTAB_XTD|MTAB_VUN, 0, "FORMAT", NULL, | |
NULL, &cr_show_format, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 006, "ADDRESS", "ADDRESS", | |
&set_addr, &show_addr, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", | |
&set_vec, &show_vec, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={DEFAULT|200..1200}", | |
&cr_set_rate, &cr_show_rate, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 0, "TRANSLATION", | |
"TRANSLATION={DEFAULT|026|026FTN|029|EBCDIC}", | |
&cr_set_trans, &cr_show_trans, NULL }, | |
{ 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 | DEV_UBUS | DEV_DEBUG }; | |
/* 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. | |
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. | |
*/ | |
static t_bool readCardImage ( FILE *fp, | |
int16 *hcard, | |
char *ccard, | |
char *acard ) | |
{ | |
int c1, c2, c3, col; | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "readCardImage pos %d\n", ftell (fp)); | |
/* get card header bytes */ | |
c1 = fgetc (fp); | |
c2 = fgetc (fp); | |
c3 = fgetc (fp); | |
cr_unit.pos = ftell (fp); | |
/* check for EOF */ | |
if (c1 == EOF) { | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "hopper empty\n"); | |
if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { | |
EOFcard = TRUE; | |
for (col = 1; col <= 8; col++) { | |
hcard[col] = PUNCH_EOD; | |
ccard[col] = h2c_code[PUNCH_EOD]; | |
acard[col] = ' '; | |
} | |
while (col <= colEnd) { | |
hcard[col] = PUNCH_SPACE; | |
ccard[col] = PUNCH_SPACE; | |
acard[col] = ' '; | |
col++; | |
} | |
return (TRUE); | |
} | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; | |
crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); | |
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; | |
if (cr_unit.flags & UNIT_AUTOEOF) | |
cdst |= CDCSR_EOF; | |
blowerState = BLOW_STOP; | |
return (FALSE); | |
} | |
/* check for valid header */ | |
if ((c2 == EOF) || (c3 == EOF) || ((c1 & 0x80) == 0) || | |
((c2 & 0x80) == 0) || ((c3 & 0x80) == 0)) { | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "header error\n"); | |
/* unexpected EOF or format problems */ | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; | |
crs &= ~CRCSR_ONLINE; | |
cdst |= CSR_ERR | CDCSR_RDRCHK; | |
blowerState = BLOW_STOP; | |
return (FALSE); | |
} | |
assert (colStart < colEnd); | |
assert (colStart >= 0); | |
assert (colEnd <= 81); | |
for (col = colStart; col < colEnd; ) { | |
int16 i; | |
/* get 3 bytes */ | |
c1 = fgetc (fp); | |
c2 = fgetc (fp); | |
c3 = fgetc (fp); | |
cr_unit.pos = ftell (fp); | |
if (ferror (fp) || feof (fp)) { | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "file error\n"); | |
/* signal error; unexpected EOF, format problems, or file error(s) */ | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; | |
crs &= ~CRCSR_ONLINE; | |
cdst |= CSR_ERR | CDCSR_RDRCHK; | |
blowerState = BLOW_STOP; | |
return (FALSE); | |
} | |
/* convert to 2 columns */ | |
i = ((c1 << 4) | ( c2 >> 4)) & 0xFFF; | |
hcard[col] = i; | |
ccard[col] = h2c_code[i]; | |
acard[col] = ascii_code[i]; | |
col++; | |
i = (((c2 & 017) << 8) | c3) & 0xFFF; | |
hcard[col] = i; | |
ccard[col] = h2c_code[i]; | |
acard[col] = ascii_code[i]; | |
col++; | |
} | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "successfully loaded card\n"); | |
return (TRUE); | |
} | |
static t_bool readColumnBinary ( FILE *fp, | |
int16 *hcard, | |
char *ccard, | |
char *acard ) | |
{ | |
int col; | |
for (col = colStart; col <= colEnd; col++) { | |
int16 i; | |
i = fgetc (fp) & 077; | |
i |= ((fgetc (fp) & 077) << 6); | |
cr_unit.pos = ftell (fp); | |
if (feof (fp)) { | |
if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { | |
EOFcard = TRUE; | |
for (col = 1; col <= 8; col++) { | |
hcard[col] = PUNCH_EOD; | |
ccard[col] = h2c_code[PUNCH_EOD]; | |
acard[col] = ' '; | |
} | |
while (col <= colEnd) { | |
hcard[col] = PUNCH_SPACE; | |
ccard[col] = PUNCH_SPACE; | |
acard[col] = ' '; | |
col++; | |
} | |
return (TRUE); | |
} | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | | |
CRCSR_OFFLINE; | |
crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); | |
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; | |
if (cr_unit.flags & UNIT_AUTOEOF) | |
cdst |= CDCSR_EOF; | |
blowerState = BLOW_STOP; | |
return (FALSE); | |
} | |
if (ferror (fp)) { | |
/* signal error */ | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; | |
crs &= ~CRCSR_ONLINE; | |
cdst |= CSR_ERR | CDCSR_RDRCHK; | |
blowerState = BLOW_STOP; | |
return (FALSE); | |
} | |
hcard[col] = i; | |
ccard[col] = 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? | |
*/ | |
static t_bool readCardASCII ( FILE *fp, | |
int16 *hcard, | |
char *ccard, | |
char *acard ) | |
{ | |
int c, col; | |
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)) { | |
/* signal error */ | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; | |
crs &= ~CRCSR_ONLINE; | |
cdst |= CSR_ERR | CDCSR_RDRCHK; | |
blowerState = BLOW_STOP; | |
cr_unit.pos = ftell (fp); | |
return (FALSE); | |
} | |
if (col == colStart) { | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "hopper empty\n"); | |
if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { | |
EOFcard = TRUE; | |
for (col = 1; col <= 8; col++) { | |
hcard[col] = PUNCH_EOD; | |
ccard[col] = h2c_code[PUNCH_EOD]; | |
acard[col] = ' '; | |
} | |
c = '\n'; | |
goto fill_card; | |
} | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; | |
crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); | |
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; | |
if (cr_unit.flags & UNIT_AUTOEOF) | |
cdst |= CDCSR_EOF; | |
blowerState = BLOW_STOP; | |
cr_unit.pos = ftell (fp); | |
return (FALSE); | |
} | |
/* fall through */ | |
case '\r': | |
case '\n': | |
fill_card: | |
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] = 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\n", | |
col); | |
} | |
ccard[col] = h2c_code[hcard[col]]; | |
acard[col] = c; | |
col++; | |
break; | |
} | |
} | |
/* silently truncate/flush long lines, or flag over-length card? */ | |
if (c != '\n') { | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "truncating card\n"); | |
do c = fgetc (fp); | |
while ((c != EOF) && (c != '\n') && (c != '\r')); | |
} | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "successfully loaded card\n"); | |
cr_unit.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)); | |
switch (table) { | |
case 1: | |
codeTbl = o26_comm_code; | |
for (i = ' '; i < '`'; i++) | |
ascii_code[o26_comm_code[i]] = i; | |
break; | |
case 2: | |
codeTbl = o26_ftn_code; | |
for (i = ' '; i < '`'; i++) | |
ascii_code[o26_ftn_code[i]] = i; | |
break; | |
case 3: | |
codeTbl = o29_code; | |
for (i = ' '; i < '`'; i++) | |
ascii_code[o29_code[i]] = i; | |
break; | |
case 4: | |
codeTbl = EBCDIC_code; | |
for (i = 0; i < 0177; i++) | |
ascii_code[EBCDIC_code[i]] = i; | |
break; | |
default: | |
/* can't happen */ | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, | |
"bad CR translation initialization value\n"); | |
break; | |
} | |
} | |
/* | |
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 & (077000)) | |
cdst |= CSR_ERR; | |
else | |
cdst &= ~CSR_ERR; | |
*data = (cr_unit.flags & UNIT_CR11) ? | |
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: | |
*data = (cr_unit.flags & UNIT_CR11) ? crb1 : cdcc; | |
/* Does crb1 clear after read? Implied by VMS driver. */ | |
crb1 = 0; | |
crs &= ~CRCSR_COLRDY; | |
if (DEBUG_PRS (cr_dev)) { | |
if (cr_unit.flags & UNIT_CR11) | |
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); | |
} | |
break; | |
case 2: | |
*data = (cr_unit.flags & UNIT_CR11) ? crb2 : cdba; | |
crb2 = 0; /* see note for crb1 */ | |
crs &= ~CRCSR_COLRDY; | |
if (DEBUG_PRS (cr_dev)) { | |
if (cr_unit.flags & UNIT_CR11) | |
fprintf (sim_deb, "cr_rd crb2 %06o\n", crb2); | |
else | |
fprintf (sim_deb, "\r\ncr_rd cdba %06o\n", cdba); | |
} | |
break; | |
case 3: | |
default: | |
if (cr_unit.flags & UNIT_CR11) | |
*data = crm; | |
else | |
*data = 0100000 | (cdst & CDCSR_RDRCHK) | | |
(cdst & CDCSR_OFFLINE) ? | |
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 ) | |
{ | |
switch ((PA >> 1) & 03) { | |
case 0: | |
if (cr_unit.flags & UNIT_CR11) { | |
/* 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); | |
crs &= ~(CSR_ERR | CRCSR_CRDDONE | CRCSR_TIMERR); | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_wr data %06o crs %06o\n", | |
data, crs); | |
if (data & CSR_GO) { | |
if (blowerState != BLOW_ON) { | |
sim_activate (&cr_unit, spinUp); | |
blowerState = BLOW_START; | |
} else | |
sim_activate (&cr_unit, cr_unit.wait); | |
} | |
} else { | |
if (data & CDCSR_PWRCLR) { | |
CLR_INT (CR); | |
sim_cancel (&cr_unit); | |
cdst &= ~(CDCSR_RDRCHK |CDCSR_OFFLINE | | |
CDCSR_RDY | CDCSR_HOPPER); | |
cdst |= CDCSR_RDY; | |
cdcc = 0; | |
cdba = 0; | |
break; | |
} | |
if (!(data & CSR_IE)) | |
CLR_INT (CR); | |
cdst = (cdst & ~CDCSR_RW) | (data & CDCSR_RW); | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_wr data %06o cdst %06o\n", | |
data, cdst); | |
if (data & CSR_GO) { | |
if (blowerState != BLOW_ON) { | |
sim_activate (&cr_unit, spinUp); | |
blowerState = BLOW_START; | |
} else | |
sim_activate (&cr_unit, cr_unit.wait); | |
} | |
} | |
break; | |
case 1: | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_wr cdcc %06o\n", data); | |
if (cr_unit.flags & UNIT_CR11) | |
break; | |
cdcc = data & 0177777; | |
break; | |
case 2: | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_wr crba %06o\n", data); | |
if (cr_unit.flags & UNIT_CR11) | |
break; | |
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 (!(cr_unit.flags & UNIT_CR11)) | |
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); | |
} | |
/* | |
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; | |
if (blowerState == BLOW_STOP) { | |
blowerState = BLOW_OFF; | |
return (SCPE_OK); | |
} | |
if (blowerState == BLOW_START) | |
blowerState = BLOW_ON; | |
/* (almost) anything we do now will cause a CR interrupt */ | |
if (crs & CSR_IE) | |
SET_INT (CR); | |
if (!(uptr->flags & UNIT_ATT) || (crs & CSR_ERR) || (cdst & CSR_ERR)) | |
return (SCPE_OK); | |
if ((crs & CRCSR_BUSY) && (currCol > colEnd)) { | |
crs &= ~(CRCSR_BUSY | CSR_GO | CRCSR_COLRDY); | |
crs |= CRCSR_CRDDONE; | |
if (cdst & (CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM)) | |
cdst |= CSR_ERR; | |
if (cdst & CSR_IE) | |
SET_INT (CR); | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_svc card done\n"); | |
return (SCPE_OK); | |
} | |
if (!(crs & CRCSR_BUSY)) { | |
/* try to read a card */ | |
/* crs &= ~CRCSR_CRDDONE; */ | |
if (!readRtn (uptr->fileref, hcard, ccard, acard)) { | |
sim_activate (uptr, spinDown); | |
return (SCPE_OK); | |
} | |
currCol = colStart; | |
crs |= CRCSR_BUSY; /* indicate reader busy */ | |
} | |
/* check for overrun (timing error) */ | |
if ((uptr->flags & UNIT_CR11) && (crs & CRCSR_COLRDY)) | |
crs |= CSR_ERR | CRCSR_TIMERR; | |
crb1 = hcard[currCol] & 07777; | |
crb2 = ccard[currCol] & 0377; | |
uptr->buf = acard[currCol] & 0377; /* helpful for debugging */ | |
if (!(uptr->flags & UNIT_CR11)) { | |
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) { | |
c = cddb = ccard[currCol] & 0377; | |
if (Map_WriteB (pa, 1, &c)) | |
cdst |= CDCSR_NXM; | |
pa = (pa + 1) & 0777777; | |
} else { | |
w = cddb = hcard[currCol] & 07777; | |
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; | |
#if 0 | |
if (!(cdst & CSR_IE) && !(crs & CRCSR_CRDDONE)) | |
CLR_INT (CR); | |
#endif | |
} | |
currCol++; /* advance the column counter */ | |
if (!(crs & CRCSR_EJECT)) | |
crs |= CRCSR_COLRDY; | |
else | |
CLR_INT (CR); | |
sim_activate (uptr, uptr->wait); | |
return (SCPE_OK); | |
} | |
t_stat cr_reset ( DEVICE *dptr ) | |
{ | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_reset\n"); | |
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); | |
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; | |
cdcc = 0; | |
cdba = 0; | |
cddb = 0; | |
if ((cr_unit.flags & UNIT_ATT) && !feof (cr_unit.fileref)) { | |
crs |= CRCSR_ONLINE; /* non-standard */ | |
crs &= ~(CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE); | |
cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); | |
} else { | |
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; | |
crs = CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; | |
} | |
sim_cancel (&cr_unit); /* deactivate unit */ | |
if (blowerState != BLOW_OFF) { | |
blowerState = BLOW_STOP; | |
sim_activate (&cr_unit, spinDown); | |
} | |
EOFcard = FALSE; | |
CLR_INT (CR); | |
/* TBD: flush current card */ | |
/* init uptr->wait ? */ | |
return (SCPE_OK); | |
} | |
/* | |
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')) | |
t_stat cr_attach ( UNIT *uptr, | |
char *cptr ) | |
{ | |
t_stat reason; | |
extern int32 sim_switches; | |
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)) { | |
crs &= ~CRCSR_ONLINE; | |
crs |= CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY; | |
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; | |
} else { | |
setupCardFile (uptr, sim_switches); | |
crs |= CRCSR_ONLINE; | |
crs &= ~(CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY); | |
cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); | |
EOFcard = FALSE; | |
} | |
return (reason); | |
} | |
t_stat cr_detach ( UNIT *uptr ) | |
{ | |
crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; | |
/* interrupt? */ | |
crs &= ~CRCSR_ONLINE; | |
cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER | CDCSR_OFFLINE; | |
cardFormat = "unknown"; | |
if (blowerState != BLOW_OFF) { | |
blowerState = BLOW_STOP; | |
sim_activate (uptr, spinDown); | |
} | |
return (detach_unit (uptr)); | |
} | |
t_stat cr_set_type ( UNIT *uptr, | |
int32 val, | |
char *cptr, | |
void *desc ) | |
{ | |
/* disallow type change if currently attached */ | |
if (uptr->flags & UNIT_ATT) | |
return (SCPE_NOFNC); | |
cpm = (val & UNIT_CR11) ? 285 : 1000; | |
uptr->wait = (60 * 1000) / cpm; | |
return (SCPE_OK); | |
} | |
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 = (uptr->flags & UNIT_CR11) ? 285 : 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 * 1000) / cpm; | |
} | |
} | |
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 */ | |
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; | |
crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_SUPPLY|CRCSR_RDCHK|CRCSR_TIMERR| | |
CRCSR_BUSY|CRCSR_COLRDY|CRCSR_EJECT|CSR_GO); | |
cdst |= CDCSR_ONLINE; | |
cdst &= ~(CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK | CDCSR_HOPPER | | |
CDCSR_EOF); | |
if ((crs & CSR_IE) || (cdst & CSR_IE)) { | |
SET_INT (CR); | |
if (DEBUG_PRS (cr_dev)) | |
fprintf (sim_deb, "cr_set_reset setting interrupt\n"); | |
} | |
/* start up the blower if the hopper is not empty */ | |
if (blowerState != BLOW_ON) | |
blowerState = BLOW_START; | |
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 |= CDCSR_OFFLINE; | |
/* CD11 does not appear to interrupt on STOP. */ | |
if (crs & CSR_IE) | |
SET_INT (CR); | |
if (blowerState != BLOW_OFF) { | |
blowerState = BLOW_STOP; | |
/* set timer to turn it off completely */ | |
sim_activate (uptr, spinDown); | |
} | |
return (SCPE_OK); | |
} | |
static const char * const trans[] = { | |
"unknown", "026", "026FTN", "029", "EBCDIC" | |
}; | |
t_stat cr_set_trans ( UNIT *uptr, | |
int32 val, | |
char *cptr, | |
void *desc ) | |
{ | |
int i; | |
if (!cptr) | |
return (SCPE_MISVAL); | |
if (strcmp (cptr, "DEFAULT") == 0) | |
i = 3; | |
else { | |
for (i = 1; i < 5; i++) { | |
if (strcmp (cptr, trans[i]) == 0) | |
break; | |
} | |
} | |
if (i < 1 || i > 4) | |
return (SCPE_ARG); | |
table = i; | |
initTranslation (); /* reinitialize tables */ | |
return (SCPE_OK); | |
} | |
t_stat cr_show_trans ( FILE *st, | |
UNIT *uptr, | |
int32 val, | |
void *desc ) | |
{ | |
fprintf (st, "translation %s", trans[table]); | |
return (SCPE_OK); | |
} |