1 /* i7094_cd.c: IBM 711/721 card reader/punch
3 Copyright (c) 2003-2007, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
29 19-Jan-07 RMS Added UNIT_TEXT
30 13-Jul-06 RMS Fixed problem with 80 column full cards
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:
36 column binary each character represents 6b of a 12b column
37 text each character represents all 12b of a column
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
45 #include "i7094_defs.h"
47 #define CD_BINLNT 24 /* bin buf length */
48 #define CD_CHRLNT 80 /* char buf length */
50 #define CDS_INIT 0 /* card in motion */
51 #define CDS_DATA 1 /* data transfer */
52 #define CDS_END 2 /* card complete */
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)
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 */
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 */
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
);
90 extern uint32 sim_switches
;
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];
100 /* Card reader data structures
102 cdr_dev CDR descriptor
103 cdr_unit CDR unit descriptor
104 cdr_reg CDR register list
107 DIB cdr_dib
= { &cdr_chsel
, NULL
};
110 UDATA (&cdr_svc
, UNIT_SEQ
+UNIT_ATTABLE
+UNIT_ROABLE
+UNIT_TEXT
, 0)
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
},
125 { UNIT_CBN
, UNIT_CBN
, "column binary", "BINARY", &cd_set_mode
},
126 { UNIT_CBN
, UNIT_CBN
, "text", "TEXT", &cd_set_mode
},
131 "CDR", &cdr_unit
, cdr_reg
, cdr_mod
,
133 NULL
, NULL
, &cdr_reset
,
134 &cdr_boot
, &cd_attach
, NULL
,
135 &cdr_dib
, DEV_DISABLE
138 /* CDP data structures
140 cdp_dev CDP device descriptor
141 cdp_unit CDP unit descriptor
142 cdp_reg CDP register list
145 DIB cdp_dib
= { &cdp_chsel
, &cdp_chwr
};
148 UDATA (&cdp_svc
, UNIT_SEQ
+UNIT_ATTABLE
+UNIT_TEXT
, 0)
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
},
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
},
174 "CDP", &cdp_unit
, cdp_reg
, cdp_mod
,
176 NULL
, NULL
, &cdp_reset
,
177 NULL
, &cd_attach
, NULL
,
178 &cdp_dib
, DEV_DISABLE
181 /* Card reader select */
183 t_stat
cdr_chsel (uint32 ch
, uint32 sel
, uint32 unit
)
185 if (sel
& CHSL_NDS
) return ch6_end_nds (ch
); /* nds? nop */
187 switch (sel
) { /* case on data sel */
189 case CHSL_RDS
: /* read */
190 if ((cdr_unit
.flags
& UNIT_ATT
) == 0) /* not attached? */
192 if (sim_is_active (&cdr_unit
)) /* busy? */
194 cdr_sta
= CDS_INIT
; /* initial state */
195 sim_activate (&cdr_unit
, cdp_tstart
); /* start reader */
199 return STOP_ILLIOP
; /* not allowed */
207 t_stat
cdr_svc (UNIT
*uptr
)
209 uint32 i
, col
, row
, bufw
, colbin
;
210 char cdr_cbuf
[(2 * CD_CHRLNT
) + 2];
213 if ((uptr
->flags
& UNIT_ATT
) == 0) return SCPE_UNATT
; /* not attached? */
214 switch (cdr_sta
) { /* case on state */
216 case CDS_INIT
: /* initial state */
217 for (i
= 0; i
< CD_BINLNT
; i
++) /* clear bin buf */
219 for (i
= 0; i
< ((2 * CD_CHRLNT
) + 2); i
++) /* clear char buf */
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 */
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
;
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
);
256 ch6_req_rd (CH_A
, U_CDR
, dat
, 0); /* req chan, dat */
257 sim_activate (uptr
, (cdr_bptr
& 1)? cdr_tleft
: cdr_tright
);
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 */
272 /* Card reader reset */
274 t_stat
cdr_reset (DEVICE
*dptr
)
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 */
285 /* Card reader bootstrap */
287 #define BOOT_START 01000
288 #define BOOT_SIZE (sizeof (boot_rom) / sizeof (t_uint64))
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 */
298 t_stat
cdr_boot (int32 unitno
, DEVICE
*dptr
)
303 for (i
= 0; i
< BOOT_SIZE
; i
++)
304 WriteP (BOOT_START
+ i
, boot_rom
[i
]);
309 /* Reader/punch attach */
311 t_stat
cd_attach (UNIT
*uptr
, char *cptr
)
313 t_stat r
= attach_unit (uptr
, cptr
);
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
;
327 /* Reader/punch set mode - valid only if not attached */
329 t_stat
cd_set_mode (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
331 return (uptr
->flags
& UNIT_ATT
)? SCPE_NOFNC
: SCPE_OK
;
334 /* Card punch select */
336 t_stat
cdp_chsel (uint32 ch
, uint32 sel
, uint32 unit
)
338 if (sel
& CHSL_NDS
) return ch6_end_nds (ch
); /* nds? nop */
340 switch (sel
) { /* case on cmd */
342 case CHSL_WRS
: /* write */
343 if ((cdp_unit
.flags
& UNIT_ATT
) == 0) /* not attached? */
345 if (sim_is_active (&cdp_unit
)) /* busy? */
347 cdp_sta
= CDS_INIT
; /* initial state */
348 sim_activate (&cdp_unit
, cdp_tstart
); /* start punch */
352 return STOP_ILLIOP
; /* not allowed */
357 /* Channel write routine - write word to buffer, write card when full */
359 t_stat
cdp_chwr (uint32 ch
, t_uint64 val
, uint32 eorfl
)
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 */
376 t_stat
cdp_svc (UNIT
*uptr
)
380 switch (cdp_sta
) { /* case on state */
382 case CDS_INIT
: /* initial state */
383 for (i
= 0; i
< CD_BINLNT
; i
++) /* clear bin buffer */
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 */
390 sim_activate (uptr
, cdp_tleft
); /* go again */
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
);
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 */
413 /* Card end - write card image to file, transition to end state */
415 t_stat
cdp_card_end (UNIT
*uptr
)
417 uint32 i
, col
, row
, bufw
, colbin
;
418 char *pch
, bcd
, cdp_cbuf
[(2 * CD_CHRLNT
) + 2];
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 */
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
];
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];
438 bcd
= colbin_to_bcd (colbin
); /* column bin -> BCD */
439 cdp_cbuf
[col
] = pch
[bcd
]; /* -> ASCII */
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
);
453 cdp_sta
= CDS_END
; /* end state */
454 sim_cancel (uptr
); /* cancel current */
455 sim_activate (uptr
, cdp_tstop
); /* long timer */
459 /* Card punch reset */
461 t_stat
cdp_reset (DEVICE
*dptr
)
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 */
470 sim_cancel (&cdp_unit
); /* stop punch */
474 /* Column binary to BCD
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.
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. */
485 static const char row_val
[12] = {
486 011, 010, 007, 006, 005, 004,
487 003, 002, 001, 020, 040, 060
490 char colbin_to_bcd (uint32 cb
)
495 for (i
= 0, bcd
= 0; i
< 12; i
++) { /* 'sum' rows */
496 if (cb
& (1 << i
)) bcd
|= row_val
[i
];