First Commit of my working state
[simh.git] / I1620 / i1620_cd.c
1 /* i1620_cd.c: IBM 1622 card reader/punch
2
3 Copyright (c) 2002-2007, Robert M. Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 cdr 1622 card reader
27 cdp 1622 card punch
28
29 19-Jan-07 RMS Set UNIT_TEXT flag
30 13-Jul-06 RMS Fixed card reader fgets call (from Tom McBride)
31 Fixed card reader boot sequence (from Tom McBride)
32 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility
33 25-Apr-03 RMS Revised for extended file support
34
35 Cards are represented as ASCII text streams terminated by newlines.
36 This allows cards to be created and edited as normal files.
37 */
38
39 #include "i1620_defs.h"
40
41 #define CD_LEN 80
42
43 extern uint8 M[MAXMEMSIZE];
44 extern uint8 ind[NUM_IND];
45 extern UNIT cpu_unit;
46 extern int32 io_stop;
47
48 char cdr_buf[CD_LEN + 2];
49 char cdp_buf[CD_LEN + 2];
50
51 t_stat cdr_reset (DEVICE *dptr);
52 t_stat cdr_attach (UNIT *uptr, char *cptr);
53 t_stat cdr_boot (int32 unitno, DEVICE *dptr);
54 t_stat cdr_read (void);
55 t_stat cdp_reset (DEVICE *dptr);
56 t_stat cdp_write (uint32 len);
57 t_stat cdp_num (uint32 pa, uint32 ndig, t_bool dump);
58
59 /* Card reader data structures
60
61 cdr_dev CDR descriptor
62 cdr_unit CDR unit descriptor
63 cdr_reg CDR register list
64 */
65
66 UNIT cdr_unit = {
67 UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0)
68 };
69
70 REG cdr_reg[] = {
71 { FLDATA (LAST, ind[IN_LAST], 0) },
72 { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT },
73 { NULL }
74 };
75
76 DEVICE cdr_dev = {
77 "CDR", &cdr_unit, cdr_reg, NULL,
78 1, 10, 31, 1, 8, 7,
79 NULL, NULL, &cdr_reset,
80 &cdr_boot, &cdr_attach, NULL
81 };
82
83 /* CDP data structures
84
85 cdp_dev CDP device descriptor
86 cdp_unit CDP unit descriptor
87 cdp_reg CDP register list
88 */
89
90 UNIT cdp_unit = {
91 UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0)
92 };
93
94 REG cdp_reg[] = {
95 { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT },
96 { NULL }
97 };
98
99 DEVICE cdp_dev = {
100 "CDP", &cdp_unit, cdp_reg, NULL,
101 1, 10, 31, 1, 8, 7,
102 NULL, NULL, &cdp_reset,
103 NULL, NULL, NULL
104 };
105
106 /* Data tables. The card reader presents unusual problems.
107 - Unique codes needed for 11-2-8 (uses !) and 12-7-8 (uses ") .
108 - Can punch both 11 (-) and 11-0 (uses ]).
109 On input, the nul and nl generated by C are converted to
110 spaces; tabs and line feeds are also converted to spaces.
111
112 /* Card reader (ASCII) to numeric (one digit) */
113
114 const char cdr_to_num[128] = {
115 0x00, -1, -1, -1, -1, -1, -1, -1, /* 00 */
116 -1, 0x00, 0x00, -1, -1, 0x00, -1, -1,
117 -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
118 -1, -1, -1, -1, -1, -1, -1, -1,
119 0x00, 0x1A, 0x0F, 0x0B, 0x1B, 0x0C, 0x00, 0x0C, /* !"#$%&' */
120 0x0C, 0x0C, 0x1C, 0x00, 0x0B, 0x10, 0x1B, 0x01, /* ()*+,-./ */
121 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */
122 0x08, 0x09, 0x00, 0x1E, 0x1E, 0x0B, 0x0E, 0x1A, /* 89:;<=>? */
123 0x0C, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* @ABCDEFG */
124 0x08, 0x09, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* HIJKLMNO */
125 0x17, 0x18, 0x19, 0x02, 0x03, 0x04, 0x05, 0x06, /* PQRSTUVW */
126 0x07, 0x08, 0x09, 0x00, 0x0E, 0x10, 0x0A, 0x1F, /* XYZ[\]^_ */
127 -1, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* `abcdefg */
128 0x08, 0x09, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* hijklmno */
129 0x17, 0x18, 0x19, 0x02, 0x03, 0x04, 0x05, 0x06, /* pqrstuvw */
130 0x07, 0x08, 0x09, 0x0F, 0x0A, 0x1F, 0x00, -1 /* xyz{|}~ */
131 };
132
133 /* Numeric (flag + digit) to card punch (ASCII) */
134
135 const char num_to_cdp[32] = {
136 '0', '1', '2', '3', '4', '5', '6', '7', /* 0 */
137 '8', '9', '|', ',', ' ', '"', ' ', '"',
138 ']', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* F + 0 */
139 'Q', 'R', '!', '$', -1, -1, -1, '"'
140 };
141
142 /* Card reader (ASCII) to alphameric (two digits)
143
144 11-2-8 (!) reads as 5A
145 11-7-8 (_) reads as 5F
146 12-2-8 (?) reads inconsistently (here 02)
147 12-6-8 (<) reads inconsistently (here 5E)
148 12-7-8 (}) reads as 5F
149 */
150
151 const char cdr_to_alp[128] = {
152 0x00, -1, -1, -1, -1, -1, -1, -1, /* 00 */
153 -1, 0x00, 0x00, -1, -1, 0x00, -1, -1,
154 -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
155 -1, -1, -1, -1, -1, -1, -1, -1,
156 0x00, 0x5A, 0x0F, 0x33, 0x13, 0x24, 0x10, 0x34, /* !"#$%&' */
157 0x24, 0x04, 0x14, 0x10, 0x23, 0x20, 0x03, 0x21, /* ()*+,-./ */
158 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 01234567 */
159 0x78, 0x79, 0x70, 0x5E, 0x5E, 0x33, 0x0E, 0x02, /* 89:;<=>? */
160 0x34, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */
161 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* HIJKLMNO */
162 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* PQRSTUVW */
163 0x67, 0x68, 0x69, 0x40, 0x0E, 0x50, 0x0A, 0x5F, /* XYZ[\]^_ */
164 0x50, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */
165 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* hijklmno */
166 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* pqrstuvw */
167 0x67, 0x68, 0x69, 0x0F, 0x0A, 0x5F, 0x60, -1 /* xyz{|}~ */
168 };
169
170 /* Alphameric (two digits) to card punch (ASCII). Oddities:
171
172 02 -> 12-2-8 (?), symmetric
173 07 -> 12-7-8 (}), reads as 5F
174 12 -> 11-2-8 (!), reads as 5A
175 15 -> 11,0 (`), reads as 50
176 22 -> 0-2-8 (|), reads as 0A
177 32 -> 2-8 (^), reads as 0A
178 5B -> 11-3-8 (=), reads as 13
179 6A -> 0-2-8 (|), reads as 0A
180 6B -> 0-3-8 (,), reads as 23
181 AA -> 0-2-8 (|), reads as 0A
182
183 There is no way to punch 0-5-8 (~), 0-6-8 (\),
184 11-5-8 (]), 11-6-8 (;), 11-7-8 (_),
185 12-5-8 ([), or 12-6-8 (<)
186 */
187
188 const char alp_to_cdp[256] = {
189 ' ', -1, '?', '.', ')', -1, -1, '}', /* 00 */
190 -1, -1, '\'', -1, -1, -1, -1, '"',
191 '+', -1, '!', '$', '*', ']', -1, -1, /* 10 */
192 -1, -1, -1, -1, -1, -1, -1, -1,
193 '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */
194 -1, -1, -1, -1, -1, -1, -1, -1,
195 -1, -1, '^', '=', '@', ':', ' ', -1, /* 30 */
196 -1, -1, '|', -1, -1, -1, -1, '"',
197 -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */
198 'H', 'I', -1, -1, -1, -1, -1, -1,
199 '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */
200 'Q', 'R', '?', '=', -1, -1, -1, '}',
201 -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */
202 'Y', 'Z', '|', ',', -1, -1, -1, -1,
203 '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */
204 '8', '9', -1, -1, -1, -1, -1, -1,
205 -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */
206 -1, -1, -1, -1, -1, -1, -1, -1,
207 -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */
208 -1, -1, -1, -1, -1, -1, -1, -1,
209 -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */
210 -1, -1, '|', -1, -1, -1, -1, -1,
211 -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */
212 -1, -1, -1, -1, -1, -1, -1, -1,
213 -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */
214 -1, -1, -1, -1, -1, -1, -1, -1,
215 -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */
216 -1, -1, -1, -1, -1, -1, -1, -1,
217 -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */
218 -1, -1, -1, -1, -1, -1, -1, -1,
219 -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */
220 -1, -1, -1, -1, -1, -1, -1, -1
221 };
222
223 /* Card reader IO routine
224
225 - Hard errors stop the operation and halt the system.
226 - Invalid characters place a blank in memory and set RDCHK.
227 If IO stop is set, the system halts at the end of the operation.
228 */
229
230 t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1)
231 {
232 int32 i;
233 int8 cdc;
234 t_stat r, sta;
235
236 sta = SCPE_OK; /* assume ok */
237 switch (op) { /* case on op */
238
239 case OP_RN: /* read numeric */
240 r = cdr_read (); /* fill reader buf */
241 if (r != SCPE_OK) return r; /* error? */
242 for (i = 0; i < CD_LEN; i++) { /* transfer to mem */
243 cdc = cdr_to_num[cdr_buf[i]]; /* translate */
244 if (cdc < 0) { /* invalid? */
245 ind[IN_RDCHK] = 1; /* set read check */
246 if (io_stop) sta = STOP_INVCHR; /* set return status */
247 cdc = 0;
248 }
249 M[pa] = cdc; /* store digit */
250 PP (pa); /* incr mem addr */
251 }
252 break;
253
254 case OP_RA: /* read alphameric */
255 r = cdr_read (); /* fill reader buf */
256 if (r != SCPE_OK) return r; /* error? */
257 for (i = 0; i < CD_LEN; i++) { /* transfer to mem */
258 cdc = cdr_to_alp[cdr_buf[i]]; /* translate */
259 if (cdc < 0) { /* invalid? */
260 ind[IN_RDCHK] = 1; /* set read check */
261 if (io_stop) sta = STOP_INVCHR; /* set return status */
262 cdc = 0;
263 };
264 M[pa] = (M[pa] & FLAG) | (cdc & DIGIT); /* store 2 digits */
265 M[pa - 1] = (M[pa - 1] & FLAG) | ((cdc >> 4) & DIGIT);
266 pa = ADDR_A (pa, 2); /* incr mem addr */
267 }
268 break;
269
270 default: /* invalid function */
271 return STOP_INVFNC;
272 }
273
274 return sta;
275 }
276
277 /* Fill card reader buffer - all errors are hard errors */
278
279 t_stat cdr_read (void)
280 {
281 int32 i;
282
283 ind[IN_LAST] = 0; /* clear last card */
284 if ((cdr_unit.flags & UNIT_ATT) == 0) { /* attached? */
285 ind[IN_RDCHK] = 1; /* no, error */
286 return SCPE_UNATT;
287 }
288
289 for (i = 0; i < CD_LEN + 2; i++) cdr_buf[i] = ' '; /* clear buffer */
290 fgets (cdr_buf, CD_LEN + 2, cdr_unit.fileref); /* read card */
291 if (feof (cdr_unit.fileref)) return STOP_NOCD; /* eof? */
292 if (ferror (cdr_unit.fileref)) { /* error? */
293 ind[IN_RDCHK] = 1; /* set read check */
294 perror ("CDR I/O error");
295 clearerr (cdr_unit.fileref);
296 return SCPE_IOERR;
297 }
298 cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */
299 getc (cdr_unit.fileref); /* see if more */
300 if (feof (cdr_unit.fileref)) ind[IN_LAST] = 1; /* eof? set last */
301 fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); /* "backspace" */
302 return SCPE_OK;
303 }
304
305 /* Card reader attach */
306
307 t_stat cdr_attach (UNIT *uptr, char *cptr)
308 {
309 ind[IN_LAST] = 0; /* clear last card */
310 return attach_unit (uptr, cptr);
311 }
312
313 /* Card reader reset */
314
315 t_stat cdr_reset (DEVICE *dptr)
316 {
317 ind[IN_LAST] = 0; /* clear last card */
318 return SCPE_OK;
319 }
320
321 /* Bootstrap routine */
322
323 #define BOOT_START 0
324
325 t_stat cdr_boot (int32 unitno, DEVICE *dptr)
326 {
327 t_stat r;
328 int32 old_io_stop;
329 extern int32 saved_PC;
330
331 old_io_stop = io_stop;
332 io_stop = 1;
333 r = cdr (OP_RN, 0, 0, 0); /* read card @ 0 */
334 io_stop = old_io_stop;
335 if (r != SCPE_OK) return r; /* error? */
336 saved_PC = BOOT_START;
337 return SCPE_OK;
338 }
339
340 /* Card punch IO routine
341
342 - Hard errors stop the operation and halt the system.
343 - Invalid characters stop the operation and set WRCHK.
344 If IO stop is set, the system halts.
345 */
346
347 t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1)
348 {
349 int32 i;
350 int8 cdc;
351 uint8 z, d;
352
353 switch (op) { /* decode op */
354
355 case OP_DN:
356 return cdp_num (pa, 20000 - (pa % 20000), TRUE); /* dump numeric */
357
358 case OP_WN:
359 return cdp_num (pa, CD_LEN, FALSE); /* write numeric */
360
361 case OP_WA:
362 for (i = 0; i < CD_LEN; i++) { /* one card */
363 d = M[pa] & DIGIT; /* get digit pair */
364 z = M[pa - 1] & DIGIT;
365 cdc = alp_to_cdp[(z << 4) | d]; /* translate */
366 if (cdc < 0) { /* bad char? */
367 ind[IN_WRCHK] = 1; /* set write check */
368 CRETIOE (io_stop, STOP_INVCHR);
369 }
370 cdp_buf[i] = cdc; /* store in buf */
371 pa = ADDR_A (pa, 2); /* incr mem addr */
372 }
373 return cdp_write (CD_LEN); /* punch buffer */
374
375 default: /* invalid function */
376 break;
377 }
378
379 return STOP_INVFNC;
380 }
381
382 /* Punch card numeric */
383
384 t_stat cdp_num (uint32 pa, uint32 ndig, t_bool dump)
385 {
386 int32 i, ncd, len;
387 uint8 d;
388 int8 cdc;
389 t_stat r;
390
391 ncd = ndig / CD_LEN; /* number of cards */
392 while (ncd-- >= 0) { /* until done */
393 len = (ncd >= 0)? CD_LEN: (ndig % CD_LEN); /* card length */
394 if (len == 0) break;
395 for (i = 0; i < len; i++) { /* one card */
396 d = M[pa] & (FLAG | DIGIT); /* get char */
397 if (dump && (d == FLAG)) cdc = '-'; /* dump? F+0 is diff */
398 else cdc = num_to_cdp[d]; /* translate */
399 if (cdc < 0) { /* bad char? */
400 ind[IN_WRCHK] = 1; /* set write check */
401 CRETIOE (io_stop, STOP_INVCHR); /* stop */
402 }
403 cdp_buf[i] = cdc; /* store in buf */
404 PP (pa); /* incr mem addr */
405 }
406 r = cdp_write (len); /* punch card */
407 if (r != SCPE_OK) return r; /* error? */
408 }
409 return SCPE_OK;
410 }
411
412 /* Write punch card buffer - all errors are hard errors */
413
414 t_stat cdp_write (uint32 len)
415 {
416 if ((cdp_unit.flags & UNIT_ATT) == 0) { /* attached? */
417 ind[IN_WRCHK] = 1; /* no, error */
418 return SCPE_UNATT;
419 }
420
421 while ((len > 0) && (cdp_buf[len - 1] == ' ')) --len; /* trim spaces */
422 cdp_buf[len] = '\n'; /* newline, null */
423 cdp_buf[len + 1] = 0;
424
425 fputs (cdp_buf, cdp_unit.fileref); /* write card */
426 cdp_unit.pos = ftell (cdp_unit.fileref); /* count char */
427 if (ferror (cdp_unit.fileref)) { /* error? */
428 ind[IN_WRCHK] = 1;
429 perror ("CDP I/O error");
430 clearerr (cdp_unit.fileref);
431 return SCPE_IOERR;
432 }
433 return SCPE_OK;
434 }
435
436 /* Reset card punch */
437
438 t_stat cdp_reset (DEVICE *dptr)
439 {
440 return SCPE_OK;
441 }