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