First Commit of my working state
[simh.git] / I7094 / i7094_cd.c
1 /* i7094_cd.c: IBM 711/721 card reader/punch
2
3 Copyright (c) 2003-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 711 card reader
27 cdp 721 card punch
28
29 19-Jan-07 RMS Added UNIT_TEXT
30 13-Jul-06 RMS Fixed problem with 80 column full cards
31
32 Cards are represented as ASCII text streams terminated by newlines.
33 This allows cards to be created and edited as normal files. Two
34 formats are supported:
35
36 column binary each character represents 6b of a 12b column
37 text each character represents all 12b of a column
38
39 Internally, the 7094 works only with column binary and is limited
40 to 72 columns of data. Each row of the card is represented by 72b
41 of data (two 36b words). A complete card image consists of 12 rows
42 (24 36b words).
43 */
44
45 #include "i7094_defs.h"
46
47 #define CD_BINLNT 24 /* bin buf length */
48 #define CD_CHRLNT 80 /* char buf length */
49
50 #define CDS_INIT 0 /* card in motion */
51 #define CDS_DATA 1 /* data transfer */
52 #define CDS_END 2 /* card complete */
53
54 #define UNIT_V_CBN (UNIT_V_UF + 0) /* column binary file */
55 #define UNIT_V_PCA (UNIT_V_UF + 1) /* A vs H punch flag */
56 #define UNIT_CBN (1 << UNIT_V_CBN)
57 #define UNIT_PCA (1 << UNIT_V_PCA)
58
59 uint32 cdr_sta = 0; /* state */
60 uint32 cdr_bptr = 0; /* buffer ptr */
61 uint32 cdr_tstart = 27500; /* timing */
62 uint32 cdr_tstop = 27500;
63 uint32 cdr_tleft = 150;
64 uint32 cdr_tright = 4000;
65 t_uint64 cdr_bbuf[CD_BINLNT]; /* col binary buf */
66
67 uint32 cdp_sta = 0; /* state */
68 uint32 cdp_bptr = 0; /* buffer ptr */
69 uint32 cdp_tstart = 35000; /* timing */
70 uint32 cdp_tstop = 35000;
71 uint32 cdp_tleft = 150;
72 uint32 cdp_tright = 15500;
73 t_uint64 cdp_chob = 0;
74 uint32 cdp_chob_v = 0;
75 t_uint64 cdp_bbuf[CD_BINLNT]; /* col binary buf */
76
77 t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit);
78 t_stat cdr_reset (DEVICE *dptr);
79 t_stat cdr_svc (UNIT *uptr);
80 t_stat cdr_boot (int32 unitno, DEVICE *dptr);
81 t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit);
82 t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 flags);
83 t_stat cdp_reset (DEVICE *dptr);
84 t_stat cdp_svc (UNIT *uptr);
85 t_stat cdp_card_end (UNIT *uptr);
86 t_stat cd_attach (UNIT *uptr, char *cptr);
87 t_stat cd_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc);
88 char colbin_to_bcd (uint32 cb);
89
90 extern uint32 sim_switches;
91 extern uint32 PC;
92 extern uint32 ind_ioc;
93 extern char bcd_to_ascii_a[64];
94 extern char bcd_to_ascii_h[64];
95 extern uint32 bcd_to_colbin[64];
96 extern char ascii_to_bcd[128];
97 extern t_uint64 bit_masks[36];
98 extern uint32 col_masks[12];
99
100 /* Card reader data structures
101
102 cdr_dev CDR descriptor
103 cdr_unit CDR unit descriptor
104 cdr_reg CDR register list
105 */
106
107 DIB cdr_dib = { &cdr_chsel, NULL };
108
109 UNIT cdr_unit = {
110 UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0)
111 };
112
113 REG cdr_reg[] = {
114 { ORDATA (STATE, cdr_sta, 2) },
115 { DRDATA (BPTR, cdr_bptr, 5), PV_LEFT },
116 { BRDATA (BUF, cdr_bbuf, 8, 36, CD_BINLNT) },
117 { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT },
118 { DRDATA (TSTART, cdr_tstart, 24), PV_LEFT + REG_NZ },
119 { DRDATA (TSTOP, cdr_tstop, 24), PV_LEFT + REG_NZ },
120 { DRDATA (TLEFT, cdr_tleft, 24), PV_LEFT + REG_NZ },
121 { DRDATA (TRIGHT, cdr_tright, 24), PV_LEFT + REG_NZ },
122 { NULL } };
123
124 MTAB cdr_mod[] = {
125 { UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode },
126 { UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode },
127 { 0 }
128 };
129
130 DEVICE cdr_dev = {
131 "CDR", &cdr_unit, cdr_reg, cdr_mod,
132 1, 10, 31, 1, 8, 7,
133 NULL, NULL, &cdr_reset,
134 &cdr_boot, &cd_attach, NULL,
135 &cdr_dib, DEV_DISABLE
136 };
137
138 /* CDP data structures
139
140 cdp_dev CDP device descriptor
141 cdp_unit CDP unit descriptor
142 cdp_reg CDP register list
143 */
144
145 DIB cdp_dib = { &cdp_chsel, &cdp_chwr };
146
147 UNIT cdp_unit = {
148 UDATA (&cdp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0)
149 };
150
151 REG cdp_reg[] = {
152 { ORDATA (STATE, cdp_sta, 2) },
153 { ORDATA (CHOB, cdp_chob, 36) },
154 { FLDATA (CHOBV, cdp_chob_v, 0) },
155 { DRDATA (BPTR, cdp_bptr, 5), PV_LEFT },
156 { BRDATA (BUF, cdp_bbuf, 8, 36, CD_BINLNT) },
157 { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT },
158 { DRDATA (TSTART, cdp_tstart, 24), PV_LEFT + REG_NZ },
159 { DRDATA (TSTOP, cdp_tstop, 24), PV_LEFT + REG_NZ },
160 { DRDATA (TLEFT, cdp_tleft, 24), PV_LEFT + REG_NZ },
161 { DRDATA (TRIGHT, cdp_tright, 24), PV_LEFT + REG_NZ },
162 { NULL }
163 };
164
165 MTAB cdp_mod[] = {
166 { UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode },
167 { UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode },
168 { UNIT_PCA, UNIT_PCA, "business set", "BUSINESS", NULL },
169 { UNIT_PCA, 0, "Fortran set", "FORTRAN", NULL },
170 { 0 }
171 };
172
173 DEVICE cdp_dev = {
174 "CDP", &cdp_unit, cdp_reg, cdp_mod,
175 1, 10, 31, 1, 8, 7,
176 NULL, NULL, &cdp_reset,
177 NULL, &cd_attach, NULL,
178 &cdp_dib, DEV_DISABLE
179 };
180
181 /* Card reader select */
182
183 t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit)
184 {
185 if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */
186
187 switch (sel) { /* case on data sel */
188
189 case CHSL_RDS: /* read */
190 if ((cdr_unit.flags & UNIT_ATT) == 0) /* not attached? */
191 return SCPE_UNATT;
192 if (sim_is_active (&cdr_unit)) /* busy? */
193 return ERR_STALL;
194 cdr_sta = CDS_INIT; /* initial state */
195 sim_activate (&cdr_unit, cdp_tstart); /* start reader */
196 break;
197
198 default: /* other */
199 return STOP_ILLIOP; /* not allowed */
200 }
201
202 return SCPE_OK;
203 }
204
205 /* Unit timeout */
206
207 t_stat cdr_svc (UNIT *uptr)
208 {
209 uint32 i, col, row, bufw, colbin;
210 char cdr_cbuf[(2 * CD_CHRLNT) + 2];
211 t_uint64 dat = 0;
212
213 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* not attached? */
214 switch (cdr_sta) { /* case on state */
215
216 case CDS_INIT: /* initial state */
217 for (i = 0; i < CD_BINLNT; i++) /* clear bin buf */
218 cdr_bbuf[i] = 0;
219 for (i = 0; i < ((2 * CD_CHRLNT) + 2); i++) /* clear char buf */
220 cdr_cbuf[i] = ' ';
221 cdr_sta = CDS_DATA; /* data state */
222 cdr_bptr = 0; /* init buf ptr */
223 fgets (cdr_cbuf, (uptr->flags & UNIT_CBN)? (2 * CD_CHRLNT) + 2: CD_CHRLNT + 2,
224 uptr->fileref); /* read card */
225 if (feof (uptr->fileref)) /* eof? */
226 return ch6_err_disc (CH_A, U_CDR, CHF_EOF); /* set EOF, disc */
227 if (ferror (uptr->fileref)) { /* error? */
228 perror ("CDR I/O error");
229 clearerr (uptr->fileref);
230 return SCPE_IOERR; /* stop */
231 }
232 uptr->pos = ftell (uptr->fileref); /* update position */
233 for (i = 0; i < (2 * CD_CHRLNT); i++) /* convert to BCD */
234 cdr_cbuf[i] = ascii_to_bcd[cdr_cbuf[i] & 0177] & 077;
235 for (col = 0; col < 72; col++) { /* process 72 columns */
236 if (uptr->flags & UNIT_CBN) /* column binary? */
237 colbin = (((uint32) cdr_cbuf[2 * col]) << 6) |
238 ((uint32) cdr_cbuf[(2 * col) + 1]); /* 2 chars -> col bin */
239 else colbin = bcd_to_colbin[cdr_cbuf[col]]; /* cvt to col binary */
240 dat = bit_masks[35 - (col % 36)]; /* mask for column */
241 for (row = 0; row < 12; row++) { /* rows 9..0, 11, 12 */
242 bufw = (row * 2) + (col / 36); /* index to buffer */
243 if (colbin & col_masks[row]) /* row bit set? */
244 cdr_bbuf[bufw] |= dat;
245 }
246 }
247
248 case CDS_DATA: /* data state */
249 dat = cdr_bbuf[cdr_bptr++]; /* get next word */
250 if (cdr_bptr >= CD_BINLNT) { /* last word? */
251 cdr_sta = CDS_END; /* end state */
252 ch6_req_rd (CH_A, U_CDR, dat, CH6DF_EOR); /* req chan, dat, EOR */
253 sim_activate (uptr, cdr_tstop);
254 }
255 else {
256 ch6_req_rd (CH_A, U_CDR, dat, 0); /* req chan, dat */
257 sim_activate (uptr, (cdr_bptr & 1)? cdr_tleft: cdr_tright);
258 }
259 break;
260
261 case CDS_END: /* end state */
262 if (ch6_qconn (CH_A, U_CDR)) { /* if cdr still conn */
263 cdr_sta = CDS_INIT; /* return to init */
264 sim_activate (uptr, 1); /* next card */
265 }
266 break;
267 }
268
269 return SCPE_OK;
270 }
271
272 /* Card reader reset */
273
274 t_stat cdr_reset (DEVICE *dptr)
275 {
276 uint32 i;
277
278 for (i = 0; i < CD_BINLNT; i++) cdr_bbuf[i] = 0; /* clear buffer */
279 cdr_sta = 0; /* clear state */
280 cdr_bptr = 0; /* clear buf ptr */
281 sim_cancel (&cdr_unit); /* stop reader */
282 return SCPE_OK;
283 }
284
285 /* Card reader bootstrap */
286
287 #define BOOT_START 01000
288 #define BOOT_SIZE (sizeof (boot_rom) / sizeof (t_uint64))
289
290 static const t_uint64 boot_rom[] = {
291 00762000001000 + U_CDR, /* RDSA CDR */
292 00544000000000 + BOOT_START + 4, /* LCHA *+3 */
293 00544000000000, /* LCHA 0 */
294 00021000000001, /* TTR 1 */
295 05000030000000, /* IOCT 3,,0 */
296 };
297
298 t_stat cdr_boot (int32 unitno, DEVICE *dptr)
299 {
300 uint32 i;
301 extern t_uint64 *M;
302
303 for (i = 0; i < BOOT_SIZE; i++)
304 WriteP (BOOT_START + i, boot_rom[i]);
305 PC = BOOT_START;
306 return SCPE_OK;
307 }
308
309 /* Reader/punch attach */
310
311 t_stat cd_attach (UNIT *uptr, char *cptr)
312 {
313 t_stat r = attach_unit (uptr, cptr);
314
315 if (r != SCPE_OK) return r; /* attach */
316 if (sim_switches & SWMASK ('T')) /* text? */
317 uptr->flags = uptr->flags & ~UNIT_CBN;
318 else if (sim_switches & SWMASK ('C')) /* column binary? */
319 uptr->flags = uptr->flags | UNIT_CBN;
320 else if (match_ext (cptr, "TXT")) /* .txt? */
321 uptr->flags = uptr->flags & ~UNIT_CBN;
322 else if (match_ext (cptr, "CBN")) /* .cbn? */
323 uptr->flags = uptr->flags | UNIT_CBN;
324 return SCPE_OK;
325 }
326
327 /* Reader/punch set mode - valid only if not attached */
328
329 t_stat cd_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc)
330 {
331 return (uptr->flags & UNIT_ATT)? SCPE_NOFNC: SCPE_OK;
332 }
333
334 /* Card punch select */
335
336 t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit)
337 {
338 if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */
339
340 switch (sel) { /* case on cmd */
341
342 case CHSL_WRS: /* write */
343 if ((cdp_unit.flags & UNIT_ATT) == 0) /* not attached? */
344 return SCPE_UNATT;
345 if (sim_is_active (&cdp_unit)) /* busy? */
346 return ERR_STALL;
347 cdp_sta = CDS_INIT; /* initial state */
348 sim_activate (&cdp_unit, cdp_tstart); /* start punch */
349 break;
350
351 default: /* other */
352 return STOP_ILLIOP; /* not allowed */
353 }
354 return SCPE_OK;
355 }
356
357 /* Channel write routine - write word to buffer, write card when full */
358
359 t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 eorfl)
360 {
361 cdp_chob = val & DMASK; /* store data */
362 cdp_chob_v = 1; /* buffer valid */
363 if (cdp_sta == CDS_DATA) {
364 cdp_bbuf[cdp_bptr++] = cdp_chob; /* store data */
365 if ((cdp_bptr >= CD_BINLNT) || eorfl) { /* end card or end rec? */
366 ch6_set_flags (CH_A, U_CDP, CHF_EOR); /* set eor */
367 return cdp_card_end (&cdp_unit); /* write card */
368 }
369 return SCPE_OK;
370 }
371 return SCPE_IERR;
372 }
373
374 /* Unit timeout */
375
376 t_stat cdp_svc (UNIT *uptr)
377 {
378 uint32 i;
379
380 switch (cdp_sta) { /* case on state */
381
382 case CDS_INIT: /* initial state */
383 for (i = 0; i < CD_BINLNT; i++) /* clear bin buffer */
384 cdp_bbuf[i] = 0;
385 cdp_sta = CDS_DATA; /* data state */
386 cdp_bptr = 0; /* init pointer */
387 ch6_req_wr (CH_A, U_CDP); /* request channel */
388 cdp_chob = 0; /* clr, inval buffer */
389 cdp_chob_v = 0;
390 sim_activate (uptr, cdp_tleft); /* go again */
391 break;
392
393 case CDS_DATA: /* data state */
394 if (!ch6_qconn (CH_A, U_CDP)) /* chan disconnect? */
395 return cdp_card_end (uptr); /* write card */
396 if (cdp_chob_v) cdp_chob_v = 0; /* valid? clear */
397 else ind_ioc = 1; /* no, io check */
398 ch6_req_wr (CH_A, U_CDP); /* req channel */
399 sim_activate (uptr, (cdp_bptr & 1)? cdp_tleft: cdp_tright);
400 break;
401
402 case CDS_END: /* end state */
403 if (ch6_qconn (CH_A, U_CDP)) { /* if cdp still conn */
404 cdp_sta = CDS_INIT; /* return to init */
405 sim_activate (uptr, 1); /* next card */
406 }
407 break;
408 }
409
410 return SCPE_OK;
411 }
412
413 /* Card end - write card image to file, transition to end state */
414
415 t_stat cdp_card_end (UNIT *uptr)
416 {
417 uint32 i, col, row, bufw, colbin;
418 char *pch, bcd, cdp_cbuf[(2 * CD_CHRLNT) + 2];
419 t_uint64 dat;
420
421 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* not attached? */
422 if (uptr->flags & UNIT_PCA) pch = bcd_to_ascii_a;
423 else pch = bcd_to_ascii_h;
424 for (col = 0; col < ((2 * CD_CHRLNT) + 1); col++)
425 cdp_cbuf[col] = ' '; /* clear char buf */
426 for (col = 0; col < 72; col++) { /* process 72 columns */
427 colbin = 0;
428 dat = bit_masks[35 - (col % 36)]; /* mask for column */
429 for (row = 0; row < 12; row++) { /* proc 12 rows */
430 bufw = (row * 2) + (col / 36); /* index to buffer */
431 if (cdp_bbuf[bufw] & dat) colbin |= col_masks[row];
432 }
433 if (cdp_unit.flags & UNIT_CBN) { /* column binary? */
434 cdp_cbuf[2 * col] = pch[(colbin >> 6) & 077];
435 cdp_cbuf[(2 * col) + 1] = pch[colbin & 077];
436 }
437 else { /* text */
438 bcd = colbin_to_bcd (colbin); /* column bin -> BCD */
439 cdp_cbuf[col] = pch[bcd]; /* -> ASCII */
440 }
441 }
442 for (i = ((2 * CD_CHRLNT) + 1); (i > 0) &&
443 (cdp_cbuf[i - 1] == ' '); --i) ; /* trim spaces */
444 cdp_cbuf[i++] = '\n'; /* append nl */
445 cdp_cbuf[i++] = 0; /* append nul */
446 fputs (cdp_cbuf, uptr->fileref); /* write card */
447 uptr->pos = ftell (uptr->fileref); /* update position */
448 if (ferror (uptr->fileref)) { /* error? */
449 perror ("CDP I/O error");
450 clearerr (uptr->fileref);
451 return SCPE_IOERR;
452 }
453 cdp_sta = CDS_END; /* end state */
454 sim_cancel (uptr); /* cancel current */
455 sim_activate (uptr, cdp_tstop); /* long timer */
456 return SCPE_OK;
457 }
458
459 /* Card punch reset */
460
461 t_stat cdp_reset (DEVICE *dptr)
462 {
463 uint32 i;
464
465 for (i = 0; i < 24; i++) cdp_bbuf[i] = 0; /* clear buffer */
466 cdp_sta = 0; /* clear state */
467 cdp_bptr = 0; /* clear buf ptr */
468 cdp_chob = 0;
469 cdp_chob_v = 0;
470 sim_cancel (&cdp_unit); /* stop punch */
471 return SCPE_OK;
472 }
473
474 /* Column binary to BCD
475
476 This is based on documentation in the IBM 1620 manual and may not be
477 accurate for the 7094. Each row (12,11,0,1..9) is interpreted as a bit
478 pattern, and the appropriate bits are set. (Double punches inclusive
479 OR, eg, 1,8,9 is 9.) On the 1620, double punch errors are detected;
480 since the 7094 only reads column binary, double punches are ignored.
481
482 Bit order, left to right, is 12, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
483 The for loop works right to left, so the table is reversed. */
484
485 static const char row_val[12] = {
486 011, 010, 007, 006, 005, 004,
487 003, 002, 001, 020, 040, 060
488 };
489
490 char colbin_to_bcd (uint32 cb)
491 {
492 uint32 i;
493 char bcd;
494
495 for (i = 0, bcd = 0; i < 12; i++) { /* 'sum' rows */
496 if (cb & (1 << i)) bcd |= row_val[i];
497 }
498 return bcd;
499 }