First Commit of my working state
[simh.git] / I7094 / i7094_lp.c
1 /* i7094_lp.c: IBM 716 line printer simulator
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 lpt 716 line printer
27
28 19-Jan-07 RMS Added UNIT_TEXT flag
29
30 Internally, the 7094 works only with column binary and is limited to
31 72 columns of data. Each row of the printed line is represented by
32 72b of data (two 36b words). A complete print line consists of 12 rows
33 (24 36b words).
34
35 The printer can also echo part of what it prints, namely, the digit rows
36 plus the 8+3 and 8+4 combinations. This was intended for verification of
37 check printing. Echoed data is interspersed with output data in the
38 following order:
39
40 output row 9 to row 1
41 echo row "8+4"
42 output row 0
43 echo row "8+3"
44 output row 11
45 echo row 9
46 output row 12
47 echo row 8 to row 1
48 */
49
50 #include "i7094_defs.h"
51
52 #define UNIT_V_CONS (UNIT_V_UF + 0) /* print to console */
53 #define UNIT_CONS (1u << UNIT_V_CONS)
54 #define UNIT_V_BZ (UNIT_V_UF + 1)
55 #define UNIT_V_48 (UNIT_V_UF + 2)
56 #define UNIT_BZ (1 << UNIT_V_BZ)
57 #define UNIT_48 (1 << UNIT_V_48)
58 #define GET_PCHAIN(x) (((x) >> UNIT_V_BZ) & (UNIT_BZ|UNIT_48))
59
60 #define LPT_BINLNT 24 /* bin buffer length */
61 #define LPT_ECHLNT 22 /* echo buffer length */
62 #define LPT_CHRLNT 80 /* char buffer length */
63
64 #define LPS_INIT 0 /* init state */
65 #define LPS_DATA 1 /* print data state */
66 #define ECS_DATA 2 /* echo data state */
67 #define LPS_END 3 /* end state */
68
69 #define LPB_9ROW 0 /* bin buf: 9 row */
70 #define LPB_8ROW 2 /* 8 row */
71 #define LPB_4ROW 10 /* 4 row */
72 #define LPB_3ROW 12 /* 3 row */
73 #define LPB_1ROW 16 /* 1 row */
74 #define LPB_12ROW 22 /* 12 row */
75
76 #define ECB_84ROW 0 /* echo buf: 8-4 row */
77 #define ECB_83ROW 2 /* 8-3 row */
78 #define ECB_9ROW 4 /* 9 row */
79
80 #define ECHO_F 0100 /* echo map: flag */
81 #define ECHO_MASK 0037 /* mask */
82
83 #define CMD_BIN 1 /* cmd: bcd/bin */
84 #define CMD_ECHO 2 /* cmd: wrs/rds */
85
86 uint32 lpt_sta = 0; /* state */
87 uint32 lpt_bptr = 0; /* buffer ptr */
88 uint32 lpt_cmd = 0; /* modes */
89 uint32 lpt_tstart = 27500; /* timing */
90 uint32 lpt_tstop = 27500;
91 uint32 lpt_tleft = 150;
92 uint32 lpt_tright = 4000;
93 t_uint64 lpt_chob = 0;
94 uint32 lpt_chob_v = 0;
95 t_uint64 lpt_bbuf[LPT_BINLNT]; /* binary buffer */
96 t_uint64 lpt_ebuf[LPT_ECHLNT]; /* echo buffer */
97
98
99 /* Echo ordering map */
100
101 static const uint8 echo_map[LPT_BINLNT + LPT_ECHLNT] = {
102 0, 1, 2, 3, 4, 5, 6, 7, /* write 9 to 1 */
103 8, 9, 10, 11, 12, 13, 14, 15,
104 16, 17,
105 0+ECHO_F, 1+ECHO_F, /* echo 8+4 */
106 18, 19, /* write 0 */
107 2+ECHO_F, 3+ECHO_F, /* echo 8+3 */
108 20, 21, /* write 11 */
109 4+ECHO_F, 5+ECHO_F, /* echo 9 */
110 22, 23, /* write 12 */
111 6+ECHO_F, 7+ECHO_F, 8+ECHO_F, 9+ECHO_F, /* echo 8 to 1 */
112 10+ECHO_F, 11+ECHO_F, 12+ECHO_F, 13+ECHO_F,
113 14+ECHO_F, 15+ECHO_F, 16+ECHO_F, 17+ECHO_F,
114 18+ECHO_F, 19+ECHO_F, 20+ECHO_F, 21+ECHO_F
115 };
116
117 extern uint32 ind_ioc;
118 extern t_uint64 bit_masks[36];
119 extern uint32 col_masks[12];
120 extern char bcd_to_ascii_a[64];
121 extern char bcd_to_ascii_h[64];
122 extern char bcd_to_pca[64];
123 extern char bcd_to_pch[64];
124
125 char *pch_table[4] = {
126 bcd_to_ascii_h, bcd_to_ascii_a, bcd_to_pch, bcd_to_pca,
127 };
128
129 t_stat lpt_reset (DEVICE *dptr);
130 t_stat lpt_svc (UNIT *uptr);
131 t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit);
132 t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 flags);
133 t_stat lpt_end_line (UNIT *uptr);
134
135 extern char colbin_to_bcd (uint32 colbin);
136
137 /* LPT data structures
138
139 lpt_dev LPT device descriptor
140 lpt_unit LPT unit descriptor
141 lpt_reg LPT register list
142 */
143
144 DIB lpt_dib = { &lpt_chsel, &lpt_chwr };
145
146 UNIT lpt_unit = {
147 UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_CONS+UNIT_TEXT, 0)
148 };
149
150 REG lpt_reg[] = {
151 { ORDATA (STATE, lpt_sta, 2) },
152 { ORDATA (CMD, lpt_cmd, 2) },
153 { ORDATA (CHOB, lpt_chob, 36) },
154 { FLDATA (CHOBV, lpt_chob_v, 0) },
155 { DRDATA (BPTR, lpt_bptr, 6), PV_LEFT },
156 { BRDATA (BUF, lpt_bbuf, 8, 36, LPT_BINLNT) },
157 { BRDATA (EBUF, lpt_ebuf, 8, 36, LPT_ECHLNT) },
158 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },
159 { DRDATA (TSTART, lpt_tstart, 24), PV_LEFT + REG_NZ },
160 { DRDATA (TSTOP, lpt_tstop, 24), PV_LEFT + REG_NZ },
161 { DRDATA (TLEFT, lpt_tleft, 24), PV_LEFT + REG_NZ },
162 { DRDATA (TRIGHT, lpt_tright, 24), PV_LEFT + REG_NZ },
163 { NULL }
164 };
165
166 MTAB lpt_mod[] = {
167 { UNIT_CONS, UNIT_CONS, "default to console", "DEFAULT" },
168 { UNIT_CONS, 0 , "no default device", "NODEFAULT" },
169 { UNIT_48, UNIT_48, "48 character chain", "48" },
170 { UNIT_48, 0, "64 character chain", "64" },
171 { UNIT_BZ, UNIT_BZ, "business set", "BUSINESS" },
172 { UNIT_BZ, 0, "Fortran set", "FORTRAN" },
173 { 0 }
174 };
175
176 DEVICE lpt_dev = {
177 "LPT", &lpt_unit, lpt_reg, lpt_mod,
178 1, 10, 31, 1, 8, 7,
179 NULL, NULL, &lpt_reset,
180 NULL, NULL, NULL,
181 &lpt_dib, DEV_DISABLE
182 };
183
184 /* Channel select routine */
185
186 t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit)
187 {
188 if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */
189
190 switch (sel) { /* case on cmd */
191
192 case CHSL_RDS: /* read */
193 case CHSL_WRS: /* write */
194 if (!(lpt_unit.flags & (UNIT_ATT|UNIT_CONS))) /* not attached? */
195 return SCPE_UNATT;
196 if (sim_is_active (&lpt_unit)) /* busy? */
197 return ERR_STALL;
198 lpt_cmd = ((unit & 02)? CMD_BIN: 0) | /* save modes */
199 ((sel == CHSL_RDS)? CMD_ECHO: 0);
200 lpt_sta = LPS_INIT; /* initial state */
201 sim_activate (&lpt_unit, lpt_tstart); /* start reader */
202 break;
203
204 default: /* other */
205 return STOP_ILLIOP;
206 }
207
208 return SCPE_OK;
209 }
210
211 /* Channel write routine
212
213 - Normal mode is processed here
214 - Echo mode is processed in the service routine (like a read) */
215
216 t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 eorfl)
217 {
218 uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */
219
220 lpt_chob = val & DMASK; /* store data */
221 lpt_chob_v = 1; /* set valid */
222 if (lpt_sta == ECS_DATA) return SCPE_OK;
223 if (lpt_sta == LPS_DATA) {
224 lpt_bbuf[lpt_bptr++] = lpt_chob; /* store data */
225 if (eorfl || /* end record, or */
226 ((lpt_cmd & CMD_BIN)? /* last word in buffer? */
227 (lpt_bptr > (LPB_1ROW + 1)): /* (binary mode) */
228 (lpt_bptr > (LPB_12ROW + 1)))) { /* (normal mode) */
229 ch6_set_flags (CH_A, u, CHF_EOR); /* set eor */
230 return lpt_end_line (&lpt_unit);
231 }
232 return SCPE_OK;
233 }
234 return SCPE_IERR;
235 }
236
237 /* Unit timeout */
238
239 t_stat lpt_svc (UNIT *uptr)
240 {
241 uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */
242 uint32 i, map;
243
244 switch (lpt_sta) { /* case on state */
245
246 case LPS_INIT: /* initial state */
247 for (i = 0; i < LPT_BINLNT; i++) /* clear data buffer */
248 lpt_bbuf[i] = 0;
249 for (i = 0; i < LPT_ECHLNT; i++) /* clear echo buffer */
250 lpt_ebuf[i] = 0;
251 if (lpt_cmd & CMD_BIN) lpt_bptr = LPB_1ROW; /* set buffer ptr */
252 else lpt_bptr = LPB_9ROW;
253 if (lpt_cmd & CMD_ECHO) lpt_sta = ECS_DATA; /* set data state */
254 else lpt_sta = LPS_DATA;
255 ch6_req_wr (CH_A, u); /* request channel */
256 lpt_chob = 0; /* clr, inval buffer */
257 lpt_chob_v = 0;
258 sim_activate (uptr, lpt_tleft); /* go again */
259 break;
260
261 case LPS_DATA: /* print data state */
262 if (!ch6_qconn (CH_A, u)) /* disconnect? */
263 return lpt_end_line (uptr); /* line is done */
264 if (lpt_chob_v) lpt_chob_v = 0; /* valid? clear */
265 else ind_ioc = 1; /* no, io check */
266 ch6_req_wr (CH_A, u); /* request chan again */
267 sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright);
268 break;
269
270 case ECS_DATA: /* echo data state */
271 map = echo_map[lpt_bptr++]; /* map column */
272 if (map == ECHO_F) { /* first echo? */
273 lpt_ebuf[ECB_84ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_4ROW];
274 lpt_ebuf[ECB_84ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_4ROW + 1];
275 lpt_ebuf[ECB_83ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_3ROW];
276 lpt_ebuf[ECB_83ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_3ROW + 1];
277 for (i = 0; i < 18; i++) /* copy rows 9.. 1 */
278 lpt_ebuf[ECB_9ROW + i] = lpt_bbuf[LPB_9ROW + i];
279 }
280 if (map & ECHO_F) { /* echo cycle */
281 ch6_req_rd (CH_A, u, lpt_ebuf[map & ECHO_MASK], 0);
282 if (lpt_bptr >= (LPT_BINLNT + LPT_ECHLNT))
283 return lpt_end_line (uptr); /* done? */
284 sim_activate (uptr, lpt_tleft); /* short timer */
285 }
286 else { /* print cycle */
287 if (lpt_chob_v) lpt_chob_v = 0; /* valid? clear */
288 else ind_ioc = 1; /* no, io check */
289 lpt_bbuf[map] = lpt_chob; /* store in buffer */
290 sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright);
291 }
292 if (!(echo_map[lpt_bptr] & ECHO_F)) /* print word next? */
293 ch6_req_wr (CH_A, u); /* req channel */
294 break;
295
296 case LPS_END: /* end state */
297 if (ch6_qconn (CH_A, u)) { /* lpt still conn? */
298 lpt_sta = LPS_INIT; /* initial state */
299 sim_activate (uptr, 1); /* next line */
300 }
301 break;
302 }
303
304 return SCPE_OK;
305 }
306
307 /* End line routine */
308
309 t_stat lpt_end_line (UNIT *uptr)
310 {
311 uint32 i, col, row, bufw, colbin;
312 char *pch, bcd, lpt_cbuf[LPT_CHRLNT + 1];
313 t_uint64 dat;
314
315 pch = pch_table[GET_PCHAIN (lpt_unit.flags)]; /* get print chain */
316 for (col = 0; col < (LPT_CHRLNT + 1); col++) /* clear ascii buf */
317 lpt_cbuf[col] = ' ';
318 for (col = 0; col < 72; col++) { /* proc 72 columns */
319 colbin = 0;
320 dat = bit_masks[35 - (col % 36)]; /* mask for column */
321 for (row = 0; row < 12; row++) { /* proc 12 rows */
322 bufw = (row * 2) + (col / 36); /* index to buffer */
323 if (lpt_bbuf[bufw] & dat) colbin |= col_masks[row];
324 }
325 bcd = colbin_to_bcd (colbin); /* column bin -> BCD */
326 lpt_cbuf[col] = pch[bcd]; /* -> ASCII */
327 }
328 for (i = LPT_CHRLNT; (i > 0) &&
329 (lpt_cbuf[i - 1] == ' '); --i) ; /* trim spaces */
330 lpt_cbuf[i] = 0; /* append nul */
331 if (uptr->flags & UNIT_ATT) { /* file? */
332 fputs (lpt_cbuf, uptr->fileref); /* write line */
333 fputc ('\n', uptr->fileref); /* append nl */
334 uptr->pos = ftell (uptr->fileref); /* update position */
335 if (ferror (uptr->fileref)) { /* error? */
336 perror ("LPT I/O error");
337 clearerr (uptr->fileref);
338 return SCPE_IOERR;
339 }
340 }
341 else if (uptr->flags & UNIT_CONS) { /* print to console? */
342 for (i = 0; lpt_cbuf[i] != 0; i++) sim_putchar (lpt_cbuf[i]);
343 sim_putchar ('\r');
344 sim_putchar ('\n');
345 }
346 else return SCPE_UNATT; /* otherwise error */
347 lpt_sta = LPS_END; /* end line state */
348 sim_cancel (uptr); /* cancel current */
349 sim_activate (uptr, lpt_tstop); /* long timer */
350 return SCPE_OK;
351 }
352
353 /* Reset routine */
354
355 t_stat lpt_reset (DEVICE *dptr)
356 {
357 uint32 i;
358
359 for (i = 0; i < LPT_BINLNT; i++) lpt_bbuf[i] = 0; /* clear bin buf */
360 for (i = 0; i < LPT_ECHLNT; i++) lpt_ebuf[i] = 0; /* clear echo buf */
361 lpt_sta = 0; /* clear state */
362 lpt_cmd = 0; /* clear modes */
363 lpt_bptr = 0; /* clear buf ptr */
364 lpt_chob = 0;
365 lpt_chob_v = 0;
366 sim_cancel (&lpt_unit); /* stop printer */
367 return SCPE_OK;
368 }