Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* pdp11_cr.c: CR/CM/CD-11 card reader simulator\r |
2 | \r | |
3 | Copyright (c) 2005-2007, John A. Dundas III\r | |
4 | Portions derived from work by Douglas W. Jones, jones@cs.uiowa.edu\r | |
5 | Portions derived from work by Robert M Supnik\r | |
6 | \r | |
7 | Permission is hereby granted, free of charge, to any person obtaining a\r | |
8 | copy of this software and associated documentation files (the "Software"),\r | |
9 | to deal in the Software without restriction, including without limitation\r | |
10 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r | |
11 | and/or sell copies of the Software, and to permit persons to whom the\r | |
12 | Software is furnished to do so, subject to the following conditions:\r | |
13 | \r | |
14 | The above copyright notice and this permission notice shall be included in\r | |
15 | all copies or substantial portions of the Software.\r | |
16 | \r | |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r | |
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r | |
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r | |
20 | THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r | |
21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r | |
22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r | |
23 | \r | |
24 | Except as contained in this notice, the name of the Author shall\r | |
25 | not be used in advertising or otherwise to promote the sale, use\r | |
26 | or other dealings in this Software without prior written\r | |
27 | authorization from the Author.\r | |
28 | \r | |
29 | ------------------------------------------------------------------------------\r | |
30 | \r | |
31 | cr CR11/CD11 punched and mark sense card reader for SIMH\r | |
32 | The CR11 controller is also compatible with the CM11-F, CME11, and CMS11.\r | |
33 | \r | |
34 | Information necessary to create this simulation was gathered from\r | |
35 | a number of sources including:\r | |
36 | \r | |
37 | CR11 Card Reader System Manual, DEC-11-HCRB-D\r | |
38 | http://www.bitsavers.org/pdf/dec/unibus/DEC-11-HCRB-D_CR11_Mar72.pdf\r | |
39 | Various editions of the Peripherals Handbook\r | |
40 | OpenVMS VAX Card Reader, Line Printer, and LPA11-K I/O User's\r | |
41 | Reference Manual, AA-PVXGA-TE\r | |
42 | http://h71000.www7.hp.com/DOC/73final/documentation/pdf/OVMS_VAX_CARD_LP_REF.pdf\r | |
43 | OpenVMS System Manager's Manual, Volume 1: Essentials\r | |
44 | http://h71000.www7.hp.com/DOC/732FINAL/aa-pv5mh-tk/aa-pv5mh-tk.PDF\r | |
45 | CRDRIVER.LIS - CR11 Card Reader Driver, X-9, graciously made available\r | |
46 | by HP\r | |
47 | Various RSTS manuals\r | |
48 | RT-11 Software Support Manual\r | |
49 | RT-11 System Reference Manual, DEC-11-ORUGA-C-D\r | |
50 | Professor Douglas W. Jones's web site:\r | |
51 | http://www.cs.uiowa.edu/~jones/cards/\r | |
52 | Paul Mattes' x026 keypunch simulator\r | |
53 | http://x3270.bgp.nu/x026.html\r | |
54 | CD2SER.MAC - TOPS card reader driver source\r | |
55 | http://pdp-10.trailing-edge.com/custsupcuspmar86_bb-x130b-sb/02/cd2ser.mac\r | |
56 | \r | |
57 | The Card Image format code and documentation is adapted from Prof.\r | |
58 | Jones's site, with his permission. Please see his site for additional\r | |
59 | documentation as well as the card image utilities referenced in\r | |
60 | his documentation (cardmake, cardlist, etc.).\r | |
61 | http://www.cs.uiowa.edu/~jones/cards/format.html\r | |
62 | \r | |
63 | Known limitations:\r | |
64 | 1. Need a copy of the CR bootstrap (and some way to test it)\r | |
65 | 2. Need a copy of the XXDP+ test deck\r | |
66 | 3. No testing under RSX; volunteers needed\r | |
67 | 4. No testing under Ultrix or Unix for PDP-11; volunteers needed\r | |
68 | 5. No testing under Ultrix or Unix for VAX; volunteers needed\r | |
69 | 6. The simulator implements a single controller/reader combination\r | |
70 | \r | |
71 | Operating System Notes\r | |
72 | \r | |
73 | RT-11 (and CTS-300) support one CR11 or CM11, but no CD11.\r | |
74 | \r | |
75 | VMS supports multiple CR11 controllers, but no CD11.\r | |
76 | \r | |
77 | RSTS/E supports either the CR11/CM11 or CD11 but not both in\r | |
78 | the same SIL. It appears to support only one unit.\r | |
79 | \r | |
80 | For RSX there exists a CR/CM task handler. Is there a CD\r | |
81 | handler?\r | |
82 | \r | |
83 | Don't have any information about Unix or Ultrix-11 yet. Same\r | |
84 | for VAX Unices.\r | |
85 | \r | |
86 | TOPS: only the CD11 is supported, under the name CD20.\r | |
87 | \r | |
88 | Revision History:\r | |
89 | \r | |
90 | 01-Feb-07 RMS Added PDP-10 support\r | |
91 | 12-May-06 JAD Modify the DEBUG code to use the SIMH DEBUG_x\r | |
92 | macros. Modify the UNIT structure to include\r | |
93 | the DEBUG bit.\r | |
94 | Mark the trans[] array contents constant.\r | |
95 | Make device data structures static and constant\r | |
96 | as appropriate.\r | |
97 | 18-Mar-05 JAD Slight optimization for blank punches recognizing\r | |
98 | that blank is 0 in all character encodings.\r | |
99 | 17-Mar-05 JAD Completely initialize ascii_code correctly.\r | |
100 | Define the end of deck punch code separately from\r | |
101 | the cardcode.i file.\r | |
102 | Make initTranslation() set a pointer to the correct\r | |
103 | punch code table to use. Modify card read functions\r | |
104 | to use this table pointer.\r | |
105 | 16-Mar-05 JAD Make certain switches passed to the ATTACH command\r | |
106 | are valid; return error on any others.\r | |
107 | Make default unit wait time compatible with default\r | |
108 | device specification.\r | |
109 | Implement SET TRANSLATION=value. Still need to\r | |
110 | modify the H2ASCII table used for text files;\r | |
111 | currently hard-coded to 029.\r | |
112 | 24-Feb-05 JAD Allow the maintenance bits in CRM to clear as\r | |
113 | well as set status bits. Not sure this is the\r | |
114 | correct behavior, though, without more documentation.\r | |
115 | Catch three more places to spin down the blower\r | |
116 | correctly.\r | |
117 | Zero the CDDB and CRM at INIT.\r | |
118 | 17-Feb-05 JAD When the hopper empties, a pick check should\r | |
119 | be generated 300ms later. They are simultaneous\r | |
120 | for now.\r | |
121 | Make sure readColumnBinary() generates a complete\r | |
122 | EOF card.\r | |
123 | 08-Feb-05 JAD Replace blowerWait with different times for blower\r | |
124 | spin up and down.\r | |
125 | 06-Feb-05 JAD After DETACH: mark CD offline, set appropriate\r | |
126 | blower state.\r | |
127 | Make sure unit wait time is recalculated every\r | |
128 | time cpm is set.\r | |
129 | 04-Feb-05 JAD Better tracking of blower state throughout driver.\r | |
130 | Make sure IE gets cleared for CR at INIT.\r | |
131 | Normalize error response in read routines.\r | |
132 | Finish condition handling for column binary.\r | |
133 | 02-Feb-05 JAD Remove Qbus support; Unibus only.\r | |
134 | Support ATTACH switches:\r | |
135 | A - ASCII, B - column binary, I - Card Image\r | |
136 | If none given, check for .TXT or .CBN; if none,\r | |
137 | examine file for magic header.\r | |
138 | Finer granularity to blower state. Expose this\r | |
139 | variable to examine/deposit from SIMH.\r | |
140 | Preliminary implementation of support for\r | |
141 | column binary format.\r | |
142 | 24-Jan-05 JAD Make AUTOEOF work as a surrogate for the EOF\r | |
143 | button of a CD11 reader. May need to separate\r | |
144 | this later, though.\r | |
145 | Partial implementation of DATAERR for CD11.\r | |
146 | Implement the Rev. J mods (as best I understand\r | |
147 | them) to the CD11 affecting the CDDB used as a\r | |
148 | second status register.\r | |
149 | 23-Jan-05 JAD Preliminary clean-up of CD state transitions.\r | |
150 | Tested with RSTS/E (V9.1-05).\r | |
151 | 22-Jan-05 JAD Finish CR state transitions; should be close now.\r | |
152 | Tested with RSTS/E (V9.1-05), RT-11 (V5.3), and\r | |
153 | VAX/VMS (V7.2).\r | |
154 | 19-Jan-05 JAD Add bounds to the RATE command; also default and\r | |
155 | help a la the XQ driver.\r | |
156 | Improved handling of empty files.\r | |
157 | 17-Jan-05 JAD Add the CR maintenance register.\r | |
158 | 16-Jan-05 JAD Add preliminary CD11 support.\r | |
159 | Simulate the STOP and RESET switches.\r | |
160 | 14-Jan-05 JAD Add the ability to automatically generate an 'EOF'\r | |
161 | card recognized by DEC operating systems when\r | |
162 | reading ASCII files.\r | |
163 | 08-Jan-05 JAD Original creation and testing\r | |
164 | */\r | |
165 | \r | |
166 | #if defined (VM_PDP10) /* PDP10 version */\r | |
167 | #include "pdp10_defs.h"\r | |
168 | extern int32 int_req;\r | |
169 | #define DFLT_DIS (DEV_DIS)\r | |
170 | #define DFLT_CR11 (0) /* CD11 only */\r | |
171 | #define DFLT_CPM 1000\r | |
172 | \r | |
173 | #elif defined (VM_VAX) /* VAX version */\r | |
174 | #include "vax_defs.h"\r | |
175 | extern int32 int_req[IPL_HLVL];\r | |
176 | #define DFLT_DIS (0)\r | |
177 | #define DFLT_CR11 (UNIT_CR11)\r | |
178 | #define DFLT_CPM 285\r | |
179 | \r | |
180 | #else /* PDP-11 version */\r | |
181 | #include "pdp11_defs.h"\r | |
182 | extern int32 int_req[IPL_HLVL];\r | |
183 | #define DFLT_DIS (0)\r | |
184 | #define DFLT_CR11 (UNIT_CR11)\r | |
185 | #define DFLT_CPM 285\r | |
186 | #endif\r | |
187 | \r | |
188 | extern FILE *sim_deb; /* sim_console.c */\r | |
189 | \r | |
190 | /* create a int32 constant from four characters */\r | |
191 | #define I4C(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))\r | |
192 | #define I4C_CBN I4C ('C','B','N',' ')\r | |
193 | #define I4C_H80 I4C ('H','8','0',' ')\r | |
194 | #define I4C_H82 I4C ('H','8','2',' ')\r | |
195 | #define I4C_H40 I4C ('H','4','0',' ')\r | |
196 | \r | |
197 | #define UNIT_V_CR11 (UNIT_V_UF + 0)\r | |
198 | #define UNIT_CR11 (1u << UNIT_V_CR11)\r | |
199 | #define UNIT_V_AUTOEOF (UNIT_V_UF + 1)\r | |
200 | #define UNIT_AUTOEOF (1u << UNIT_V_AUTOEOF)\r | |
201 | \r | |
202 | #include <assert.h>\r | |
203 | #define ERROR (00404)\r | |
204 | #include "pdp11_cr_dat.h"\r | |
205 | #define PUNCH_EOD (07417)\r | |
206 | #define PUNCH_SPACE (0) /* same for all encodings */\r | |
207 | \r | |
208 | /* CR */\r | |
209 | /* also use CSR_ERR, CSR_IE, and CSR_GO */\r | |
210 | #define CRCSR_V_CRDDONE 14 /* card done */\r | |
211 | #define CRCSR_V_SUPPLY 13 /* supply error */\r | |
212 | #define CRCSR_V_RDCHK 12 /* reader check */\r | |
213 | #define CRCSR_V_TIMERR 11 /* timing error */\r | |
214 | #define CRCSR_V_ONLINE 10 /* on line */\r | |
215 | #define CRCSR_V_BUSY 9 /* busy reading */\r | |
216 | #define CRCSR_V_OFFLINE 8 /* off line AKA READY? */\r | |
217 | #define CRCSR_V_COLRDY 7 /* column ready */\r | |
218 | #define CRCSR_V_EJECT 1 /* ignore card */\r | |
219 | \r | |
220 | #define CRCSR_CRDDONE (1u << CRCSR_V_CRDDONE)\r | |
221 | #define CRCSR_SUPPLY (1u << CRCSR_V_SUPPLY)\r | |
222 | #define CRCSR_RDCHK (1u << CRCSR_V_RDCHK)\r | |
223 | #define CRCSR_TIMERR (1u << CRCSR_V_TIMERR)\r | |
224 | #define CRCSR_ONLINE (1u << CRCSR_V_ONLINE)\r | |
225 | #define CRCSR_BUSY (1u << CRCSR_V_BUSY)\r | |
226 | #define CRCSR_OFFLINE (1u << CRCSR_V_OFFLINE)\r | |
227 | #define CRCSR_COLRDY (1u << CRCSR_V_COLRDY)\r | |
228 | #define CRCSR_EJECT (1u << CRCSR_V_EJECT)\r | |
229 | \r | |
230 | #define CRCSR_IMP (CSR_ERR | CRCSR_CRDDONE | CRCSR_SUPPLY | \\r | |
231 | CRCSR_RDCHK | CRCSR_TIMERR | CRCSR_ONLINE | \\r | |
232 | CRCSR_BUSY | CRCSR_OFFLINE | CRCSR_COLRDY | \\r | |
233 | CSR_IE | CRCSR_EJECT)\r | |
234 | #define CRCSR_RW (CSR_IE | CRCSR_EJECT | CSR_GO) /* read/write */\r | |
235 | \r | |
236 | #define CRM_V_MAINT 15 /* enable maint funct */\r | |
237 | #define CRM_V_BUSY 14\r | |
238 | #define CRM_V_READY 13\r | |
239 | #define CRM_V_HOPPER 12\r | |
240 | \r | |
241 | #define CRM_MAINT (1u << CRM_V_MAINT)\r | |
242 | #define CRM_BUSY (1u << CRM_V_BUSY)\r | |
243 | #define CRM_READY (1u << CRM_V_READY)\r | |
244 | #define CRM_HOPPER (1u << CRM_V_HOPPER)\r | |
245 | \r | |
246 | /* CD */\r | |
247 | /* also use CSR_ERR, CSR_IE, and CSR_GO */\r | |
248 | #define CDCSR_V_RDRCHK 14 /* reader check */\r | |
249 | #define CDCSR_V_EOF 13 /* CD11-E EOF button */\r | |
250 | #define CDCSR_V_OFFLINE 12 /* off line */\r | |
251 | #define CDCSR_V_DATAERR 11 /* data error */\r | |
252 | #define CDCSR_V_LATE 10 /* data late */\r | |
253 | #define CDCSR_V_NXM 9 /* non-existent memory */\r | |
254 | #define CDCSR_V_PWRCLR 8 /* power clear */\r | |
255 | #define CDCSR_V_RDY 7 /* ready */\r | |
256 | #define CDCSR_V_XBA17 5\r | |
257 | #define CDCSR_V_XBA16 4\r | |
258 | #define CDCSR_V_ONLINE 3 /* on line transition */\r | |
259 | #define CDCSR_V_HOPPER 2 /* hopper check */\r | |
260 | #define CDCSR_V_PACK 1 /* data packing */\r | |
261 | \r | |
262 | #define CDCSR_RDRCHK (1u << CDCSR_V_RDRCHK)\r | |
263 | #define CDCSR_EOF (1u << CDCSR_V_EOF)\r | |
264 | #define CDCSR_OFFLINE (1u << CDCSR_V_OFFLINE)\r | |
265 | #define CDCSR_DATAERR (1u << CDCSR_V_DATAERR)\r | |
266 | #define CDCSR_LATE (1u << CDCSR_V_LATE)\r | |
267 | #define CDCSR_NXM (1u << CDCSR_V_NXM)\r | |
268 | #define CDCSR_PWRCLR (1u << CDCSR_V_PWRCLR)\r | |
269 | #define CDCSR_RDY (1u << CDCSR_V_RDY)\r | |
270 | #define CDCSR_XBA17 (1u << CDCSR_V_XBA17)\r | |
271 | #define CDCSR_XBA16 (1u << CDCSR_V_XBA16)\r | |
272 | #define CDCSR_ONLINE (1u << CDCSR_V_ONLINE)\r | |
273 | #define CDCSR_HOPPER (1u << CDCSR_V_HOPPER)\r | |
274 | #define CDCSR_PACK (1u << CDCSR_V_PACK)\r | |
275 | \r | |
276 | #define CDCSR_IMP (CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_OFFLINE | \\r | |
277 | CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | \\r | |
278 | CDCSR_PWRCLR | CDCSR_RDY | CSR_IE | \\r | |
279 | CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | \\r | |
280 | CDCSR_HOPPER | CDCSR_PACK | CSR_GO)\r | |
281 | \r | |
282 | #define CDCSR_RW (CDCSR_PWRCLR | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | \\r | |
283 | CDCSR_PACK | CSR_GO)\r | |
284 | \r | |
285 | /* Blower state values */\r | |
286 | #define BLOW_OFF (0) /* steady state off */\r | |
287 | #define BLOW_START (1) /* starting up */\r | |
288 | #define BLOW_ON (2) /* steady state on */\r | |
289 | #define BLOW_STOP (3) /* shutting down */\r | |
290 | \r | |
291 | /* Card Reader state */\r | |
292 | static char *cardFormat = "unknown";\r | |
293 | static t_bool (*readRtn)(FILE *, int16 *, char *, char *);\r | |
294 | static char ascii_code[4096]; /* 2^12 possible values */\r | |
295 | static int currCol; /* current column when reading */\r | |
296 | static int colStart; /* starting column */\r | |
297 | static int colEnd; /* ending column */\r | |
298 | static int table = 3; /* character translation table */\r | |
299 | static const int *codeTbl = o29_code; /* punch translation table */\r | |
300 | static int32 blowerState = BLOW_OFF; /* reader vacuum/blower motor */\r | |
301 | static int32 spinUp = 3000; /* blower spin-up time: 3 seconds */\r | |
302 | static int32 spinDown = 2000; /* blower spin-down time: 2 seconds */\r | |
303 | static t_bool EOFcard = FALSE; /* played special card yet? */\r | |
304 | static int32 cpm = DFLT_CPM; /* reader rate: cards per minute */\r | |
305 | /* card image in various formats */\r | |
306 | static int16 hcard[82]; /* Hollerith format */\r | |
307 | static char ccard[82]; /* DEC compressed format */\r | |
308 | static char acard[82]; /* ASCII format */\r | |
309 | /* CR/CM registers */\r | |
310 | static int32 crs = 0; /* control/status */\r | |
311 | static int32 crb1 = 0; /* 12-bit Hollerith characters */\r | |
312 | static int32 crb2 = 0; /* 8-bit compressed characters */\r | |
313 | static int32 crm = 0; /* CMS maintenance register */\r | |
314 | /* CD registers */\r | |
315 | static int32 cdst = 0; /* control/status */\r | |
316 | static int32 cdcc = 0; /* column count */\r | |
317 | static int32 cdba = 0; /* current address, low 16 bits */\r | |
318 | static int32 cddb = 0; /* data, 2nd status */\r | |
319 | \r | |
320 | /* forward references */\r | |
321 | DEVICE cr_dev;\r | |
322 | static void setupCardFile (UNIT *, int32);\r | |
323 | t_stat cr_rd (int32 *, int32, int32);\r | |
324 | t_stat cr_wr (int32, int32, int32);\r | |
325 | t_stat cr_svc (UNIT *);\r | |
326 | t_stat cr_reset (DEVICE *);\r | |
327 | t_stat cr_attach (UNIT *, char *);\r | |
328 | t_stat cr_detach (UNIT *);\r | |
329 | t_stat cr_set_type (UNIT *, int32, char *, void *);\r | |
330 | t_stat cr_show_format (FILE *, UNIT *, int32, void *);\r | |
331 | t_stat cr_set_rate (UNIT *, int32, char *, void *);\r | |
332 | t_stat cr_show_rate (FILE *, UNIT *, int32, void *);\r | |
333 | t_stat cr_set_reset (UNIT *, int32, char *, void *);\r | |
334 | t_stat cr_set_stop (UNIT *, int32, char *, void *);\r | |
335 | t_stat cr_set_trans (UNIT *, int32, char*, void *);\r | |
336 | t_stat cr_show_trans (FILE *, UNIT *, int32, void *);\r | |
337 | \r | |
338 | /* CR data structures\r | |
339 | \r | |
340 | cr_dib CR device information block\r | |
341 | cr_unit CR unit descriptor\r | |
342 | cr_reg CR register list\r | |
343 | cr_mod CR modifier table\r | |
344 | cr_dev CR device descriptor\r | |
345 | */\r | |
346 | \r | |
347 | static DIB cr_dib = { IOBA_CR, IOLN_CR, &cr_rd, &cr_wr,\r | |
348 | 1, IVCL (CR), VEC_CR, { NULL } };\r | |
349 | \r | |
350 | static UNIT cr_unit = {\r | |
351 | UDATA (&cr_svc,\r | |
352 | UNIT_ATTABLE+UNIT_SEQ+UNIT_ROABLE+UNIT_DISABLE+\r | |
353 | DFLT_CR11+UNIT_AUTOEOF, 0),\r | |
354 | (60 * 1000) / DFLT_CPM };\r | |
355 | \r | |
356 | static const REG cr_reg[] = {\r | |
357 | { GRDATA (BUF, cr_unit.buf, DEV_RDX, 8, 0) },\r | |
358 | { GRDATA (CRS, crs, DEV_RDX, 16, 0) },\r | |
359 | { GRDATA (CRB1, crb1, DEV_RDX, 16, 0) },\r | |
360 | { GRDATA (CRB2, crb2, DEV_RDX, 16, 0) },\r | |
361 | { GRDATA (CRM, crm, DEV_RDX, 16, 0) },\r | |
362 | { GRDATA (CDST, cdst, DEV_RDX, 16, 0) },\r | |
363 | { GRDATA (CDCC, cdcc, DEV_RDX, 16, 0) },\r | |
364 | { GRDATA (CDBA, cdba, DEV_RDX, 16, 0) },\r | |
365 | { GRDATA (CDDB, cddb, DEV_RDX, 16, 0) },\r | |
366 | { GRDATA (BLOWER, blowerState, DEV_RDX, 2, 0) },\r | |
367 | { FLDATA (INT, IREQ (CR), INT_V_CR) },\r | |
368 | { FLDATA (ERR, crs, CSR_V_ERR) },\r | |
369 | { FLDATA (IE, crs, CSR_V_IE) },\r | |
370 | { DRDATA (POS, cr_unit.pos, T_ADDR_W), PV_LEFT },\r | |
371 | { DRDATA (TIME, cr_unit.wait, 24), PV_LEFT },\r | |
372 | { GRDATA (DEVADDR, cr_dib.ba, DEV_RDX, 32, 0), REG_HRO },\r | |
373 | { GRDATA (DEVVEC, cr_dib.vec, DEV_RDX, 16, 0), REG_HRO },\r | |
374 | { NULL } };\r | |
375 | \r | |
376 | static const MTAB cr_mod[] = {\r | |
377 | #if defined (VM_PDP11)\r | |
378 | { UNIT_CR11, UNIT_CR11, "CR11", "CR11", &cr_set_type },\r | |
379 | { UNIT_CR11, 0, "CD11", "CD11", &cr_set_type },\r | |
380 | #else\r | |
381 | { UNIT_CR11, UNIT_CR11, "CR11", NULL },\r | |
382 | { UNIT_CR11, 0, "CD11", NULL },\r | |
383 | #endif\r | |
384 | { UNIT_AUTOEOF, UNIT_AUTOEOF, "auto EOF", "AUTOEOF", NULL },\r | |
385 | { UNIT_AUTOEOF, 0, "no auto EOF", "NOAUTOEOF", NULL },\r | |
386 | /* card reader RESET switch */\r | |
387 | { MTAB_XTD|MTAB_VDV, 0, NULL, "RESET",\r | |
388 | &cr_set_reset, NULL, NULL },\r | |
389 | /* card reader STOP switch */\r | |
390 | { MTAB_XTD|MTAB_VDV, 0, NULL, "STOP",\r | |
391 | &cr_set_stop, NULL, NULL },\r | |
392 | { MTAB_XTD|MTAB_VUN, 0, "FORMAT", NULL,\r | |
393 | NULL, &cr_show_format, NULL },\r | |
394 | { MTAB_XTD|MTAB_VDV, 006, "ADDRESS", "ADDRESS",\r | |
395 | &set_addr, &show_addr, NULL },\r | |
396 | { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",\r | |
397 | &set_vec, &show_vec, NULL },\r | |
398 | { MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={DEFAULT|200..1200}",\r | |
399 | &cr_set_rate, &cr_show_rate, NULL },\r | |
400 | { MTAB_XTD|MTAB_VDV, 0, "TRANSLATION",\r | |
401 | "TRANSLATION={DEFAULT|026|026FTN|029|EBCDIC}",\r | |
402 | &cr_set_trans, &cr_show_trans, NULL },\r | |
403 | { 0 } };\r | |
404 | \r | |
405 | DEVICE cr_dev = {\r | |
406 | "CR", &cr_unit, (REG *) &cr_reg, (MTAB *) &cr_mod,\r | |
407 | 1, 10, 31, 1, DEV_RDX, 8,\r | |
408 | NULL, NULL, &cr_reset,\r | |
409 | NULL, &cr_attach, &cr_detach,\r | |
410 | &cr_dib, DEV_DISABLE | DFLT_DIS | DEV_UBUS | DEV_DEBUG };\r | |
411 | \r | |
412 | /* Utility routines */\r | |
413 | \r | |
414 | /*\r | |
415 | These functions read a "card" from a virtual deck file (passed in\r | |
416 | fp) and fill in three arrays. The first array 'hcard' contains the\r | |
417 | 12-bit binary image of the punch in each column; the second array\r | |
418 | 'ccard' contains the 8-bit DEC encoded representation of the\r | |
419 | corresponding column; the third array 'acard' contains the ASCII\r | |
420 | representation (if possible) of the character. The routines return\r | |
421 | TRUE if a card was read (possibly with errors) and FALSE if the\r | |
422 | "hopper is empty" (EOF) or fatal file errors prevented any portion\r | |
423 | of a card from being read.\r | |
424 | \r | |
425 | Errors other than EOF are signaled out of band in the controller\r | |
426 | state variables. Possible errors are data in columns 0 or 81\r | |
427 | (signalled as read check; currently these columns are ignored), or\r | |
428 | any file errors (signalled as motion check).\r | |
429 | \r | |
430 | Might rethink this. Should probably treat file errors as "pick\r | |
431 | check". Retry 3 times. After that, give up with error.\r | |
432 | \r | |
433 | */\r | |
434 | \r | |
435 | static t_bool readCardImage ( FILE *fp,\r | |
436 | int16 *hcard,\r | |
437 | char *ccard,\r | |
438 | char *acard )\r | |
439 | {\r | |
440 | int c1, c2, c3, col;\r | |
441 | \r | |
442 | if (DEBUG_PRS (cr_dev))\r | |
443 | fprintf (sim_deb, "readCardImage pos %d\n", ftell (fp));\r | |
444 | /* get card header bytes */\r | |
445 | c1 = fgetc (fp);\r | |
446 | c2 = fgetc (fp);\r | |
447 | c3 = fgetc (fp);\r | |
448 | cr_unit.pos = ftell (fp);\r | |
449 | /* check for EOF */\r | |
450 | if (c1 == EOF) {\r | |
451 | if (DEBUG_PRS (cr_dev))\r | |
452 | fprintf (sim_deb, "hopper empty\n");\r | |
453 | if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) {\r | |
454 | EOFcard = TRUE;\r | |
455 | for (col = 1; col <= 8; col++) {\r | |
456 | hcard[col] = PUNCH_EOD;\r | |
457 | ccard[col] = h2c_code[PUNCH_EOD];\r | |
458 | acard[col] = ' ';\r | |
459 | }\r | |
460 | while (col <= colEnd) {\r | |
461 | hcard[col] = PUNCH_SPACE;\r | |
462 | ccard[col] = PUNCH_SPACE;\r | |
463 | acard[col] = ' ';\r | |
464 | col++;\r | |
465 | }\r | |
466 | return (TRUE);\r | |
467 | }\r | |
468 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE;\r | |
469 | crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE);\r | |
470 | cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;\r | |
471 | if (cr_unit.flags & UNIT_AUTOEOF)\r | |
472 | cdst |= CDCSR_EOF;\r | |
473 | blowerState = BLOW_STOP;\r | |
474 | return (FALSE);\r | |
475 | }\r | |
476 | /* check for valid header */\r | |
477 | if ((c2 == EOF) || (c3 == EOF) || ((c1 & 0x80) == 0) ||\r | |
478 | ((c2 & 0x80) == 0) || ((c3 & 0x80) == 0)) {\r | |
479 | if (DEBUG_PRS (cr_dev))\r | |
480 | fprintf (sim_deb, "header error\n");\r | |
481 | /* unexpected EOF or format problems */\r | |
482 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE;\r | |
483 | crs &= ~CRCSR_ONLINE;\r | |
484 | cdst |= CSR_ERR | CDCSR_RDRCHK;\r | |
485 | blowerState = BLOW_STOP;\r | |
486 | return (FALSE);\r | |
487 | }\r | |
488 | assert (colStart < colEnd);\r | |
489 | assert (colStart >= 0);\r | |
490 | assert (colEnd <= 81);\r | |
491 | for (col = colStart; col < colEnd; ) {\r | |
492 | int16 i;\r | |
493 | /* get 3 bytes */\r | |
494 | c1 = fgetc (fp);\r | |
495 | c2 = fgetc (fp);\r | |
496 | c3 = fgetc (fp);\r | |
497 | cr_unit.pos = ftell (fp);\r | |
498 | if (ferror (fp) || feof (fp)) {\r | |
499 | if (DEBUG_PRS (cr_dev))\r | |
500 | fprintf (sim_deb, "file error\n");\r | |
501 | /* signal error; unexpected EOF, format problems, or file error(s) */\r | |
502 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE;\r | |
503 | crs &= ~CRCSR_ONLINE;\r | |
504 | cdst |= CSR_ERR | CDCSR_RDRCHK;\r | |
505 | blowerState = BLOW_STOP;\r | |
506 | return (FALSE);\r | |
507 | }\r | |
508 | /* convert to 2 columns */\r | |
509 | i = ((c1 << 4) | ( c2 >> 4)) & 0xFFF;\r | |
510 | hcard[col] = i;\r | |
511 | ccard[col] = h2c_code[i];\r | |
512 | acard[col] = ascii_code[i];\r | |
513 | col++;\r | |
514 | \r | |
515 | i = (((c2 & 017) << 8) | c3) & 0xFFF;\r | |
516 | hcard[col] = i;\r | |
517 | ccard[col] = h2c_code[i];\r | |
518 | acard[col] = ascii_code[i];\r | |
519 | col++;\r | |
520 | }\r | |
521 | if (DEBUG_PRS (cr_dev))\r | |
522 | fprintf (sim_deb, "successfully loaded card\n");\r | |
523 | return (TRUE);\r | |
524 | }\r | |
525 | \r | |
526 | static t_bool readColumnBinary ( FILE *fp,\r | |
527 | int16 *hcard,\r | |
528 | char *ccard,\r | |
529 | char *acard )\r | |
530 | {\r | |
531 | int col;\r | |
532 | \r | |
533 | for (col = colStart; col <= colEnd; col++) {\r | |
534 | int16 i;\r | |
535 | i = fgetc (fp) & 077;\r | |
536 | i |= ((fgetc (fp) & 077) << 6);\r | |
537 | cr_unit.pos = ftell (fp);\r | |
538 | if (feof (fp)) {\r | |
539 | if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) {\r | |
540 | EOFcard = TRUE;\r | |
541 | for (col = 1; col <= 8; col++) {\r | |
542 | hcard[col] = PUNCH_EOD;\r | |
543 | ccard[col] = h2c_code[PUNCH_EOD];\r | |
544 | acard[col] = ' ';\r | |
545 | }\r | |
546 | while (col <= colEnd) {\r | |
547 | hcard[col] = PUNCH_SPACE;\r | |
548 | ccard[col] = PUNCH_SPACE;\r | |
549 | acard[col] = ' ';\r | |
550 | col++;\r | |
551 | }\r | |
552 | return (TRUE);\r | |
553 | }\r | |
554 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY |\r | |
555 | CRCSR_OFFLINE;\r | |
556 | crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE);\r | |
557 | cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;\r | |
558 | if (cr_unit.flags & UNIT_AUTOEOF)\r | |
559 | cdst |= CDCSR_EOF;\r | |
560 | blowerState = BLOW_STOP;\r | |
561 | return (FALSE);\r | |
562 | }\r | |
563 | if (ferror (fp)) {\r | |
564 | /* signal error */\r | |
565 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE;\r | |
566 | crs &= ~CRCSR_ONLINE;\r | |
567 | cdst |= CSR_ERR | CDCSR_RDRCHK;\r | |
568 | blowerState = BLOW_STOP;\r | |
569 | return (FALSE);\r | |
570 | }\r | |
571 | hcard[col] = i;\r | |
572 | ccard[col] = h2c_code[i];\r | |
573 | acard[col] = ascii_code[i];\r | |
574 | }\r | |
575 | return (TRUE);\r | |
576 | }\r | |
577 | \r | |
578 | /*\r | |
579 | \r | |
580 | Should this routine perform special handling of non-printable,\r | |
581 | (e.g., control) characters or characters that have no encoded\r | |
582 | representation?\r | |
583 | \r | |
584 | */\r | |
585 | \r | |
586 | static t_bool readCardASCII ( FILE *fp,\r | |
587 | int16 *hcard,\r | |
588 | char *ccard,\r | |
589 | char *acard )\r | |
590 | {\r | |
591 | int c, col;\r | |
592 | \r | |
593 | assert (colStart < colEnd);\r | |
594 | assert (colStart >= 1);\r | |
595 | assert (colEnd <= 80);\r | |
596 | \r | |
597 | if (DEBUG_PRS (cr_dev))\r | |
598 | fprintf (sim_deb, "readCardASCII\n");\r | |
599 | for (col = colStart; col <= colEnd; ) {\r | |
600 | switch (c = fgetc (fp)) {\r | |
601 | case EOF:\r | |
602 | if (ferror (fp)) {\r | |
603 | /* signal error */\r | |
604 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE;\r | |
605 | crs &= ~CRCSR_ONLINE;\r | |
606 | cdst |= CSR_ERR | CDCSR_RDRCHK;\r | |
607 | blowerState = BLOW_STOP;\r | |
608 | cr_unit.pos = ftell (fp);\r | |
609 | return (FALSE);\r | |
610 | }\r | |
611 | if (col == colStart) {\r | |
612 | if (DEBUG_PRS (cr_dev))\r | |
613 | fprintf (sim_deb, "hopper empty\n");\r | |
614 | if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) {\r | |
615 | EOFcard = TRUE;\r | |
616 | for (col = 1; col <= 8; col++) {\r | |
617 | hcard[col] = PUNCH_EOD;\r | |
618 | ccard[col] = h2c_code[PUNCH_EOD];\r | |
619 | acard[col] = ' ';\r | |
620 | }\r | |
621 | c = '\n';\r | |
622 | goto fill_card;\r | |
623 | }\r | |
624 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE;\r | |
625 | crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE);\r | |
626 | cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;\r | |
627 | if (cr_unit.flags & UNIT_AUTOEOF)\r | |
628 | cdst |= CDCSR_EOF;\r | |
629 | blowerState = BLOW_STOP;\r | |
630 | cr_unit.pos = ftell (fp);\r | |
631 | return (FALSE);\r | |
632 | }\r | |
633 | /* fall through */\r | |
634 | case '\r':\r | |
635 | case '\n':\r | |
636 | fill_card:\r | |
637 | while (col <= colEnd) {\r | |
638 | hcard[col] = PUNCH_SPACE;\r | |
639 | ccard[col] = PUNCH_SPACE;\r | |
640 | acard[col] = ' ';\r | |
641 | col++;\r | |
642 | }\r | |
643 | break;\r | |
644 | case '\t':\r | |
645 | do {\r | |
646 | hcard[col] = PUNCH_SPACE;\r | |
647 | ccard[col] = PUNCH_SPACE;\r | |
648 | acard[col] = ' ';\r | |
649 | col++;\r | |
650 | } while (((col & 07) != 1) && (col <= colEnd));\r | |
651 | break;\r | |
652 | default:\r | |
653 | hcard[col] = codeTbl[c & 0177];\r | |
654 | /* check for unrepresentable ASCII characters */\r | |
655 | if (hcard[col] == ERROR) {\r | |
656 | cdst |= CDCSR_DATAERR;\r | |
657 | if (DEBUG_PRS (cr_dev))\r | |
658 | fprintf (sim_deb,\r | |
659 | "error character at column %d\n",\r | |
660 | col);\r | |
661 | }\r | |
662 | ccard[col] = h2c_code[hcard[col]];\r | |
663 | acard[col] = c;\r | |
664 | col++;\r | |
665 | break;\r | |
666 | }\r | |
667 | }\r | |
668 | /* silently truncate/flush long lines, or flag over-length card? */\r | |
669 | if (c != '\n') {\r | |
670 | if (DEBUG_PRS (cr_dev))\r | |
671 | fprintf (sim_deb, "truncating card\n");\r | |
672 | do c = fgetc (fp);\r | |
673 | while ((c != EOF) && (c != '\n') && (c != '\r'));\r | |
674 | }\r | |
675 | if (DEBUG_PRS (cr_dev))\r | |
676 | fprintf (sim_deb, "successfully loaded card\n");\r | |
677 | cr_unit.pos = ftell (fp);\r | |
678 | return (TRUE);\r | |
679 | }\r | |
680 | \r | |
681 | /*\r | |
682 | \r | |
683 | Initialize the binary translation table. Generally called when a\r | |
684 | new deck is attached but could be set manually as well.\r | |
685 | \r | |
686 | */\r | |
687 | \r | |
688 | static void initTranslation (void)\r | |
689 | {\r | |
690 | int32 i;\r | |
691 | \r | |
692 | memset (ascii_code, '~', sizeof (ascii_code));\r | |
693 | switch (table) {\r | |
694 | case 1:\r | |
695 | codeTbl = o26_comm_code;\r | |
696 | for (i = ' '; i < '`'; i++)\r | |
697 | ascii_code[o26_comm_code[i]] = i;\r | |
698 | break;\r | |
699 | case 2:\r | |
700 | codeTbl = o26_ftn_code;\r | |
701 | for (i = ' '; i < '`'; i++)\r | |
702 | ascii_code[o26_ftn_code[i]] = i;\r | |
703 | break;\r | |
704 | case 3:\r | |
705 | codeTbl = o29_code;\r | |
706 | for (i = ' '; i < '`'; i++)\r | |
707 | ascii_code[o29_code[i]] = i;\r | |
708 | break;\r | |
709 | case 4:\r | |
710 | codeTbl = EBCDIC_code;\r | |
711 | for (i = 0; i < 0177; i++)\r | |
712 | ascii_code[EBCDIC_code[i]] = i;\r | |
713 | break;\r | |
714 | default:\r | |
715 | /* can't happen */\r | |
716 | if (DEBUG_PRS (cr_dev))\r | |
717 | fprintf (sim_deb,\r | |
718 | "bad CR translation initialization value\n");\r | |
719 | break;\r | |
720 | }\r | |
721 | }\r | |
722 | \r | |
723 | /*\r | |
724 | \r | |
725 | Examine the command switches, file extension, and virtual card deck\r | |
726 | file to determine the format. Set up the global variables\r | |
727 | appropriately. Rewind ASCII files to the beginning\r | |
728 | \r | |
729 | */\r | |
730 | \r | |
731 | static void setupCardFile ( UNIT *uptr,\r | |
732 | int32 switches )\r | |
733 | {\r | |
734 | int32 i;\r | |
735 | \r | |
736 | if (switches & SWMASK ('A'))\r | |
737 | i = 0;\r | |
738 | else if (switches & SWMASK ('B'))\r | |
739 | i = I4C_CBN;\r | |
740 | else if (switches & SWMASK ('I'))\r | |
741 | goto read_header;\r | |
742 | else if (match_ext (uptr->filename, "TXT"))\r | |
743 | i = 0;\r | |
744 | else if (match_ext (uptr->filename, "CBN"))\r | |
745 | i = I4C_CBN;\r | |
746 | else {\r | |
747 | read_header:\r | |
748 | /* look for card image magic file number */\r | |
749 | i = fgetc (uptr->fileref);\r | |
750 | i = (i << 8) | fgetc (uptr->fileref);\r | |
751 | i = (i << 8) | fgetc (uptr->fileref);\r | |
752 | i = (i << 8) | ' ';\r | |
753 | }\r | |
754 | switch (i) {\r | |
755 | case I4C_H80:\r | |
756 | colStart = 1;\r | |
757 | colEnd = 80;\r | |
758 | cardFormat = "card image";\r | |
759 | readRtn = readCardImage;\r | |
760 | break;\r | |
761 | case I4C_H82:\r | |
762 | colStart = 0;\r | |
763 | colEnd = 81;\r | |
764 | cardFormat = "card image";\r | |
765 | readRtn = readCardImage;\r | |
766 | break;\r | |
767 | case I4C_H40:\r | |
768 | colStart = 1;\r | |
769 | colEnd = 40;\r | |
770 | cardFormat = "card image";\r | |
771 | readRtn = readCardImage;\r | |
772 | break;\r | |
773 | case I4C_CBN:\r | |
774 | colStart = 1;\r | |
775 | colEnd = 80;\r | |
776 | cardFormat = "column binary";\r | |
777 | readRtn = readColumnBinary;\r | |
778 | break;\r | |
779 | default:\r | |
780 | colStart = 1;\r | |
781 | colEnd = 80;\r | |
782 | cardFormat = "ASCII";\r | |
783 | readRtn = readCardASCII;\r | |
784 | fseek (uptr->fileref, 0L, SEEK_SET);\r | |
785 | break;\r | |
786 | }\r | |
787 | initTranslation ();\r | |
788 | if (DEBUG_PRS (cr_dev))\r | |
789 | fprintf (sim_deb, "colStart = %d, colEnd = %d\n",\r | |
790 | colStart, colEnd);\r | |
791 | cr_unit.pos = ftell (uptr->fileref);\r | |
792 | }\r | |
793 | \r | |
794 | /* Card reader routines\r | |
795 | \r | |
796 | cr_rd I/O page read\r | |
797 | cr_wr I/O page write\r | |
798 | cr_svc process event (reader ready)\r | |
799 | cr_reset process reset\r | |
800 | cr_attach process attach\r | |
801 | cr_detach process detach\r | |
802 | */\r | |
803 | \r | |
804 | t_stat cr_rd ( int32 *data,\r | |
805 | int32 PA,\r | |
806 | int32 access )\r | |
807 | {\r | |
808 | switch ((PA >> 1) & 03) {\r | |
809 | case 0: /* CSR */\r | |
810 | if (cdst & (077000))\r | |
811 | cdst |= CSR_ERR;\r | |
812 | else\r | |
813 | cdst &= ~CSR_ERR;\r | |
814 | *data = (cr_unit.flags & UNIT_CR11) ?\r | |
815 | crs & CRCSR_IMP : cdst & CDCSR_IMP;\r | |
816 | /* CR: if error removed, clear 15, 14, 11, 10 */\r | |
817 | if (DEBUG_PRS (cr_dev))\r | |
818 | fprintf (sim_deb, "cr_rd crs %06o cdst %06o\n",\r | |
819 | crs, cdst);\r | |
820 | break;\r | |
821 | case 1:\r | |
822 | *data = (cr_unit.flags & UNIT_CR11) ? crb1 : cdcc;\r | |
823 | /* Does crb1 clear after read? Implied by VMS driver. */\r | |
824 | crb1 = 0;\r | |
825 | crs &= ~CRCSR_COLRDY;\r | |
826 | if (DEBUG_PRS (cr_dev)) {\r | |
827 | if (cr_unit.flags & UNIT_CR11)\r | |
828 | fprintf (sim_deb, "cr_rd crb1 %06o '%c' %d\n",\r | |
829 | crb1, cr_unit.buf, cr_unit.buf);\r | |
830 | else\r | |
831 | fprintf (sim_deb, "cr_rd cdcc %06o\n", cdcc);\r | |
832 | }\r | |
833 | break;\r | |
834 | case 2:\r | |
835 | *data = (cr_unit.flags & UNIT_CR11) ? crb2 : cdba;\r | |
836 | crb2 = 0; /* see note for crb1 */\r | |
837 | crs &= ~CRCSR_COLRDY;\r | |
838 | if (DEBUG_PRS (cr_dev)) {\r | |
839 | if (cr_unit.flags & UNIT_CR11)\r | |
840 | fprintf (sim_deb, "cr_rd crb2 %06o\n", crb2);\r | |
841 | else\r | |
842 | fprintf (sim_deb, "\r\ncr_rd cdba %06o\n", cdba);\r | |
843 | }\r | |
844 | break;\r | |
845 | case 3:\r | |
846 | default:\r | |
847 | if (cr_unit.flags & UNIT_CR11)\r | |
848 | *data = crm;\r | |
849 | else\r | |
850 | *data = 0100000 | (cdst & CDCSR_RDRCHK) |\r | |
851 | (cdst & CDCSR_OFFLINE) ?\r | |
852 | cddb & 0777 : 0777;\r | |
853 | if (DEBUG_PRS (cr_dev))\r | |
854 | fprintf (sim_deb, "cr_rd crm %06o cddb %06o data %06o\n",\r | |
855 | crm, cddb, *data);\r | |
856 | break;\r | |
857 | }\r | |
858 | return (SCPE_OK);\r | |
859 | }\r | |
860 | \r | |
861 | t_stat cr_wr ( int32 data,\r | |
862 | int32 PA,\r | |
863 | int32 access )\r | |
864 | {\r | |
865 | switch ((PA >> 1) & 03) {\r | |
866 | case 0:\r | |
867 | if (cr_unit.flags & UNIT_CR11) {\r | |
868 | /* ignore high-byte writes */\r | |
869 | if (PA & 1)\r | |
870 | break;\r | |
871 | /* fixup data for low byte write */\r | |
872 | if (access == WRITEB)\r | |
873 | data = (crs & ~0377) | (data & 0377); \r | |
874 | if (!(data & CSR_IE))\r | |
875 | CLR_INT (CR);\r | |
876 | crs = (crs & ~CRCSR_RW) | (data & CRCSR_RW);\r | |
877 | crs &= ~(CSR_ERR | CRCSR_CRDDONE | CRCSR_TIMERR);\r | |
878 | if (DEBUG_PRS (cr_dev))\r | |
879 | fprintf (sim_deb, "cr_wr data %06o crs %06o\n",\r | |
880 | data, crs);\r | |
881 | if (data & CSR_GO) {\r | |
882 | if (blowerState != BLOW_ON) {\r | |
883 | sim_activate (&cr_unit, spinUp);\r | |
884 | blowerState = BLOW_START;\r | |
885 | } else\r | |
886 | sim_activate (&cr_unit, cr_unit.wait);\r | |
887 | }\r | |
888 | } else {\r | |
889 | if (data & CDCSR_PWRCLR) {\r | |
890 | CLR_INT (CR);\r | |
891 | sim_cancel (&cr_unit);\r | |
892 | cdst &= ~(CDCSR_RDRCHK |CDCSR_OFFLINE |\r | |
893 | CDCSR_RDY | CDCSR_HOPPER);\r | |
894 | cdst |= CDCSR_RDY;\r | |
895 | cdcc = 0;\r | |
896 | cdba = 0;\r | |
897 | break;\r | |
898 | }\r | |
899 | if (!(data & CSR_IE))\r | |
900 | CLR_INT (CR);\r | |
901 | cdst = (cdst & ~CDCSR_RW) | (data & CDCSR_RW);\r | |
902 | if (DEBUG_PRS (cr_dev))\r | |
903 | fprintf (sim_deb, "cr_wr data %06o cdst %06o\n",\r | |
904 | data, cdst);\r | |
905 | if (data & CSR_GO) {\r | |
906 | if (blowerState != BLOW_ON) {\r | |
907 | sim_activate (&cr_unit, spinUp);\r | |
908 | blowerState = BLOW_START;\r | |
909 | } else\r | |
910 | sim_activate (&cr_unit, cr_unit.wait);\r | |
911 | }\r | |
912 | }\r | |
913 | break;\r | |
914 | case 1:\r | |
915 | if (DEBUG_PRS (cr_dev))\r | |
916 | fprintf (sim_deb, "cr_wr cdcc %06o\n", data);\r | |
917 | if (cr_unit.flags & UNIT_CR11)\r | |
918 | break;\r | |
919 | cdcc = data & 0177777;\r | |
920 | break;\r | |
921 | case 2:\r | |
922 | if (DEBUG_PRS (cr_dev))\r | |
923 | fprintf (sim_deb, "cr_wr crba %06o\n", data);\r | |
924 | if (cr_unit.flags & UNIT_CR11)\r | |
925 | break;\r | |
926 | cdba = data & 0177777;\r | |
927 | break;\r | |
928 | case 3:\r | |
929 | if (DEBUG_PRS (cr_dev))\r | |
930 | fprintf (sim_deb, "cr_wr cddb/crm %06o\n", data);\r | |
931 | /* ignore writes to cddb */\r | |
932 | if (!(cr_unit.flags & UNIT_CR11))\r | |
933 | break;\r | |
934 | /* fixup data for byte writes and read-modify-write */\r | |
935 | if (access == WRITEB)\r | |
936 | data = (PA & 1) ?\r | |
937 | (crm & 0377) | (data << 8) :\r | |
938 | (crm & ~0377) | (data & 0377);\r | |
939 | crm = data & 0177777;\r | |
940 | /* not 100% certain how these work */\r | |
941 | if (!(crm & CRM_MAINT))\r | |
942 | break;\r | |
943 | crs = (crm & CRM_BUSY) ?\r | |
944 | (crs | CRCSR_BUSY) : (crs & ~CRCSR_BUSY);\r | |
945 | crs = (crm & CRM_READY) ?\r | |
946 | (crs | CRCSR_OFFLINE) : (crs & ~CRCSR_OFFLINE);\r | |
947 | crs = (crm & CRM_HOPPER) ?\r | |
948 | (crs | CRCSR_SUPPLY | CRCSR_RDCHK) :\r | |
949 | (crs & ~(CRCSR_SUPPLY | CRCSR_RDCHK));\r | |
950 | crb1 = crm & 07777; /* load low 12 bits */\r | |
951 | break;\r | |
952 | default:\r | |
953 | /* can't happen */\r | |
954 | break;\r | |
955 | }\r | |
956 | return (SCPE_OK);\r | |
957 | }\r | |
958 | \r | |
959 | /*\r | |
960 | Enter the service routine once for each column read from the card.\r | |
961 | CR state bits drive this primarily (see _BUSY and _CRDDONE). However,\r | |
962 | when in CD mode, also execute one column of DMA input.\r | |
963 | \r | |
964 | */\r | |
965 | \r | |
966 | t_stat cr_svc ( UNIT *uptr )\r | |
967 | {\r | |
968 | uint32 pa;\r | |
969 | uint8 c;\r | |
970 | uint16 w;\r | |
971 | \r | |
972 | if (blowerState == BLOW_STOP) {\r | |
973 | blowerState = BLOW_OFF;\r | |
974 | return (SCPE_OK);\r | |
975 | }\r | |
976 | if (blowerState == BLOW_START)\r | |
977 | blowerState = BLOW_ON;\r | |
978 | /* (almost) anything we do now will cause a CR interrupt */\r | |
979 | if (crs & CSR_IE)\r | |
980 | SET_INT (CR);\r | |
981 | if (!(uptr->flags & UNIT_ATT) || (crs & CSR_ERR) || (cdst & CSR_ERR))\r | |
982 | return (SCPE_OK);\r | |
983 | if ((crs & CRCSR_BUSY) && (currCol > colEnd)) {\r | |
984 | crs &= ~(CRCSR_BUSY | CSR_GO | CRCSR_COLRDY);\r | |
985 | crs |= CRCSR_CRDDONE;\r | |
986 | if (cdst & (CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM))\r | |
987 | cdst |= CSR_ERR;\r | |
988 | if (cdst & CSR_IE)\r | |
989 | SET_INT (CR);\r | |
990 | if (DEBUG_PRS (cr_dev))\r | |
991 | fprintf (sim_deb, "cr_svc card done\n");\r | |
992 | return (SCPE_OK);\r | |
993 | }\r | |
994 | if (!(crs & CRCSR_BUSY)) {\r | |
995 | /* try to read a card */\r | |
996 | /* crs &= ~CRCSR_CRDDONE; */\r | |
997 | if (!readRtn (uptr->fileref, hcard, ccard, acard)) {\r | |
998 | sim_activate (uptr, spinDown);\r | |
999 | return (SCPE_OK);\r | |
1000 | }\r | |
1001 | currCol = colStart;\r | |
1002 | crs |= CRCSR_BUSY; /* indicate reader busy */\r | |
1003 | }\r | |
1004 | /* check for overrun (timing error) */\r | |
1005 | if ((uptr->flags & UNIT_CR11) && (crs & CRCSR_COLRDY))\r | |
1006 | crs |= CSR_ERR | CRCSR_TIMERR;\r | |
1007 | crb1 = hcard[currCol] & 07777;\r | |
1008 | crb2 = ccard[currCol] & 0377;\r | |
1009 | uptr->buf = acard[currCol] & 0377; /* helpful for debugging */\r | |
1010 | if (!(uptr->flags & UNIT_CR11)) {\r | |
1011 | pa = cdba | ((cdst & 060) << 12);\r | |
1012 | /*\r | |
1013 | The implementation of _NXM here is not quite the same as I interpret\r | |
1014 | the (limited) documentaiton I have to indicate. However the effect\r | |
1015 | should be similar. Documentation indicates that once _NXM is set,\r | |
1016 | further NPR requests are inhibited though the card is allowed to\r | |
1017 | read until completion. This implies that CDBA and the XBA bits are\r | |
1018 | incremented accordingly, even though no data transfer occurs. This\r | |
1019 | code detects and flags the NXM condition but allows attempts at\r | |
1020 | subsequent memory writes, thus insuring the address registers are\r | |
1021 | incremented properly. If this causes problems, I'll fix it.\r | |
1022 | */\r | |
1023 | if (cdst & CDCSR_PACK) {\r | |
1024 | c = cddb = ccard[currCol] & 0377;\r | |
1025 | if (Map_WriteB (pa, 1, &c))\r | |
1026 | cdst |= CDCSR_NXM;\r | |
1027 | pa = (pa + 1) & 0777777;\r | |
1028 | } else {\r | |
1029 | w = cddb = hcard[currCol] & 07777;\r | |
1030 | if (Map_WriteW (pa, 2, &w))\r | |
1031 | cdst |= CDCSR_NXM;\r | |
1032 | pa = (pa + 2) & 0777777;\r | |
1033 | }\r | |
1034 | cdba = pa & 0177777;\r | |
1035 | cdst = (cdst & ~(CDCSR_XBA17|CDCSR_XBA16)) |\r | |
1036 | ((pa & 0600000) >> 12);\r | |
1037 | cdcc = (cdcc + 1) & 0177777;\r | |
1038 | #if 0\r | |
1039 | if (!(cdst & CSR_IE) && !(crs & CRCSR_CRDDONE))\r | |
1040 | CLR_INT (CR);\r | |
1041 | #endif\r | |
1042 | }\r | |
1043 | currCol++; /* advance the column counter */\r | |
1044 | if (!(crs & CRCSR_EJECT))\r | |
1045 | crs |= CRCSR_COLRDY;\r | |
1046 | else\r | |
1047 | CLR_INT (CR);\r | |
1048 | sim_activate (uptr, uptr->wait);\r | |
1049 | return (SCPE_OK);\r | |
1050 | }\r | |
1051 | \r | |
1052 | t_stat cr_reset ( DEVICE *dptr )\r | |
1053 | {\r | |
1054 | if (DEBUG_PRS (cr_dev))\r | |
1055 | fprintf (sim_deb, "cr_reset\n");\r | |
1056 | cr_unit.buf = 0;\r | |
1057 | currCol = 1;\r | |
1058 | crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_TIMERR|CRCSR_ONLINE|CRCSR_BUSY|\r | |
1059 | CRCSR_COLRDY|CSR_IE|CRCSR_EJECT|CSR_GO);\r | |
1060 | crb1 = 0;\r | |
1061 | crb2 = 0;\r | |
1062 | crm = 0;\r | |
1063 | cdst &= ~(CSR_ERR|CDCSR_RDRCHK|CDCSR_EOF|CDCSR_DATAERR|CDCSR_LATE|\r | |
1064 | CDCSR_NXM|CSR_IE|CDCSR_XBA17|CDCSR_XBA16|CDCSR_ONLINE|\r | |
1065 | CDCSR_PACK|CSR_GO);\r | |
1066 | cdst |= CDCSR_RDY;\r | |
1067 | cdcc = 0;\r | |
1068 | cdba = 0;\r | |
1069 | cddb = 0;\r | |
1070 | if ((cr_unit.flags & UNIT_ATT) && !feof (cr_unit.fileref)) {\r | |
1071 | crs |= CRCSR_ONLINE; /* non-standard */\r | |
1072 | crs &= ~(CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE);\r | |
1073 | cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER);\r | |
1074 | } else {\r | |
1075 | cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;\r | |
1076 | crs = CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE;\r | |
1077 | }\r | |
1078 | sim_cancel (&cr_unit); /* deactivate unit */\r | |
1079 | if (blowerState != BLOW_OFF) {\r | |
1080 | blowerState = BLOW_STOP;\r | |
1081 | sim_activate (&cr_unit, spinDown);\r | |
1082 | }\r | |
1083 | EOFcard = FALSE;\r | |
1084 | CLR_INT (CR);\r | |
1085 | /* TBD: flush current card */\r | |
1086 | /* init uptr->wait ? */\r | |
1087 | return (SCPE_OK);\r | |
1088 | }\r | |
1089 | \r | |
1090 | /*\r | |
1091 | Handle the interface status and SIMH portion of the ATTACH. Another\r | |
1092 | routine is used to evaluate the file and initialize other state\r | |
1093 | globals correctly.\r | |
1094 | */\r | |
1095 | \r | |
1096 | #define MASK (SWMASK('A')|SWMASK('B')|SWMASK('I')|SWMASK('R'))\r | |
1097 | \r | |
1098 | t_stat cr_attach ( UNIT *uptr,\r | |
1099 | char *cptr )\r | |
1100 | {\r | |
1101 | t_stat reason;\r | |
1102 | extern int32 sim_switches;\r | |
1103 | \r | |
1104 | if (sim_switches & ~MASK)\r | |
1105 | return (SCPE_INVSW);\r | |
1106 | /* file must previously exist; kludge */\r | |
1107 | sim_switches |= SWMASK ('R');\r | |
1108 | reason = attach_unit (uptr, cptr);\r | |
1109 | if (!(uptr->flags & UNIT_ATT)) {\r | |
1110 | crs &= ~CRCSR_ONLINE;\r | |
1111 | crs |= CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY;\r | |
1112 | cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER;\r | |
1113 | } else {\r | |
1114 | setupCardFile (uptr, sim_switches);\r | |
1115 | crs |= CRCSR_ONLINE;\r | |
1116 | crs &= ~(CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY);\r | |
1117 | cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER);\r | |
1118 | EOFcard = FALSE;\r | |
1119 | }\r | |
1120 | return (reason);\r | |
1121 | }\r | |
1122 | \r | |
1123 | t_stat cr_detach ( UNIT *uptr )\r | |
1124 | {\r | |
1125 | crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE;\r | |
1126 | /* interrupt? */\r | |
1127 | crs &= ~CRCSR_ONLINE;\r | |
1128 | cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER | CDCSR_OFFLINE;\r | |
1129 | cardFormat = "unknown";\r | |
1130 | if (blowerState != BLOW_OFF) {\r | |
1131 | blowerState = BLOW_STOP;\r | |
1132 | sim_activate (uptr, spinDown);\r | |
1133 | }\r | |
1134 | return (detach_unit (uptr));\r | |
1135 | }\r | |
1136 | \r | |
1137 | t_stat cr_set_type ( UNIT *uptr,\r | |
1138 | int32 val,\r | |
1139 | char *cptr,\r | |
1140 | void *desc )\r | |
1141 | {\r | |
1142 | /* disallow type change if currently attached */\r | |
1143 | if (uptr->flags & UNIT_ATT)\r | |
1144 | return (SCPE_NOFNC);\r | |
1145 | cpm = (val & UNIT_CR11) ? 285 : 1000;\r | |
1146 | uptr->wait = (60 * 1000) / cpm;\r | |
1147 | return (SCPE_OK);\r | |
1148 | }\r | |
1149 | \r | |
1150 | t_stat cr_show_format ( FILE *st,\r | |
1151 | UNIT *uptr,\r | |
1152 | int32 val,\r | |
1153 | void *desc )\r | |
1154 | {\r | |
1155 | fprintf (st, "%s format", cardFormat);\r | |
1156 | return (SCPE_OK);\r | |
1157 | }\r | |
1158 | \r | |
1159 | t_stat cr_set_rate ( UNIT *uptr,\r | |
1160 | int32 val,\r | |
1161 | char *cptr,\r | |
1162 | void *desc )\r | |
1163 | {\r | |
1164 | t_stat status = SCPE_OK;\r | |
1165 | int32 i;\r | |
1166 | \r | |
1167 | if (!cptr)\r | |
1168 | return (SCPE_MISVAL);\r | |
1169 | if (strcmp (cptr, "DEFAULT") == 0)\r | |
1170 | i = (uptr->flags & UNIT_CR11) ? 285 : 1000;\r | |
1171 | else\r | |
1172 | i = (int32) get_uint (cptr, 10, 0xFFFFFFFF, &status);\r | |
1173 | if (status == SCPE_OK) {\r | |
1174 | if (i < 200 || i > 1200)\r | |
1175 | status = SCPE_ARG;\r | |
1176 | else {\r | |
1177 | cpm = i;\r | |
1178 | uptr->wait = (60 * 1000) / cpm;\r | |
1179 | }\r | |
1180 | }\r | |
1181 | return (status);\r | |
1182 | }\r | |
1183 | \r | |
1184 | t_stat cr_show_rate ( FILE *st,\r | |
1185 | UNIT *uptr,\r | |
1186 | int32 val,\r | |
1187 | void *desc )\r | |
1188 | {\r | |
1189 | fprintf (st, "%d cards per minute", cpm);\r | |
1190 | return (SCPE_OK);\r | |
1191 | }\r | |
1192 | \r | |
1193 | /* simulate pressing the card reader RESET button */\r | |
1194 | \r | |
1195 | t_stat cr_set_reset ( UNIT *uptr,\r | |
1196 | int32 val,\r | |
1197 | char *cptr,\r | |
1198 | void *desc )\r | |
1199 | {\r | |
1200 | if (DEBUG_PRS (cr_dev))\r | |
1201 | fprintf (sim_deb, "cr_set_reset\n");\r | |
1202 | /*\r | |
1203 | Ignore the RESET switch while a read cycle is in progress or the\r | |
1204 | unit simply is not attached.\r | |
1205 | */\r | |
1206 | if ((crs & CRCSR_BUSY) || !(uptr->flags & UNIT_ATT))\r | |
1207 | return (SCPE_OK);\r | |
1208 | /* if no errors, signal transition to on line */\r | |
1209 | crs |= CRCSR_ONLINE;\r | |
1210 | crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_SUPPLY|CRCSR_RDCHK|CRCSR_TIMERR|\r | |
1211 | CRCSR_BUSY|CRCSR_COLRDY|CRCSR_EJECT|CSR_GO);\r | |
1212 | cdst |= CDCSR_ONLINE;\r | |
1213 | cdst &= ~(CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK | CDCSR_HOPPER |\r | |
1214 | CDCSR_EOF);\r | |
1215 | if ((crs & CSR_IE) || (cdst & CSR_IE)) {\r | |
1216 | SET_INT (CR);\r | |
1217 | if (DEBUG_PRS (cr_dev))\r | |
1218 | fprintf (sim_deb, "cr_set_reset setting interrupt\n");\r | |
1219 | }\r | |
1220 | /* start up the blower if the hopper is not empty */\r | |
1221 | if (blowerState != BLOW_ON)\r | |
1222 | blowerState = BLOW_START;\r | |
1223 | return (SCPE_OK);\r | |
1224 | }\r | |
1225 | \r | |
1226 | /* simulate pressing the card reader STOP button */\r | |
1227 | \r | |
1228 | t_stat cr_set_stop ( UNIT *uptr,\r | |
1229 | int32 val,\r | |
1230 | char *cptr,\r | |
1231 | void *desc )\r | |
1232 | {\r | |
1233 | if (DEBUG_PRS (cr_dev))\r | |
1234 | fprintf (sim_deb, "set_stop\n");\r | |
1235 | crs &= ~CRCSR_ONLINE;\r | |
1236 | crs |= CSR_ERR | CRCSR_OFFLINE;\r | |
1237 | cdst |= CDCSR_OFFLINE;\r | |
1238 | /* CD11 does not appear to interrupt on STOP. */\r | |
1239 | if (crs & CSR_IE)\r | |
1240 | SET_INT (CR);\r | |
1241 | if (blowerState != BLOW_OFF) {\r | |
1242 | blowerState = BLOW_STOP;\r | |
1243 | /* set timer to turn it off completely */\r | |
1244 | sim_activate (uptr, spinDown);\r | |
1245 | }\r | |
1246 | return (SCPE_OK);\r | |
1247 | }\r | |
1248 | \r | |
1249 | static const char * const trans[] = {\r | |
1250 | "unknown", "026", "026FTN", "029", "EBCDIC"\r | |
1251 | };\r | |
1252 | \r | |
1253 | t_stat cr_set_trans ( UNIT *uptr,\r | |
1254 | int32 val,\r | |
1255 | char *cptr,\r | |
1256 | void *desc )\r | |
1257 | {\r | |
1258 | int i;\r | |
1259 | \r | |
1260 | if (!cptr)\r | |
1261 | return (SCPE_MISVAL);\r | |
1262 | if (strcmp (cptr, "DEFAULT") == 0)\r | |
1263 | i = 3;\r | |
1264 | else {\r | |
1265 | for (i = 1; i < 5; i++) {\r | |
1266 | if (strcmp (cptr, trans[i]) == 0)\r | |
1267 | break;\r | |
1268 | }\r | |
1269 | }\r | |
1270 | if (i < 1 || i > 4)\r | |
1271 | return (SCPE_ARG);\r | |
1272 | table = i;\r | |
1273 | initTranslation (); /* reinitialize tables */\r | |
1274 | return (SCPE_OK);\r | |
1275 | }\r | |
1276 | \r | |
1277 | t_stat cr_show_trans ( FILE *st,\r | |
1278 | UNIT *uptr,\r | |
1279 | int32 val,\r | |
1280 | void *desc )\r | |
1281 | {\r | |
1282 | fprintf (st, "translation %s", trans[table]);\r | |
1283 | return (SCPE_OK);\r | |
1284 | }\r |