First Commit of my working state
[simh.git] / I7094 / i7094_lp.c
CommitLineData
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
86uint32 lpt_sta = 0; /* state */\r
87uint32 lpt_bptr = 0; /* buffer ptr */\r
88uint32 lpt_cmd = 0; /* modes */\r
89uint32 lpt_tstart = 27500; /* timing */\r
90uint32 lpt_tstop = 27500;\r
91uint32 lpt_tleft = 150;\r
92uint32 lpt_tright = 4000;\r
93t_uint64 lpt_chob = 0;\r
94uint32 lpt_chob_v = 0;\r
95t_uint64 lpt_bbuf[LPT_BINLNT]; /* binary buffer */\r
96t_uint64 lpt_ebuf[LPT_ECHLNT]; /* echo buffer */\r
97\r
98\r
99/* Echo ordering map */\r
100\r
101static 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
117extern uint32 ind_ioc;\r
118extern t_uint64 bit_masks[36];\r
119extern uint32 col_masks[12];\r
120extern char bcd_to_ascii_a[64];\r
121extern char bcd_to_ascii_h[64];\r
122extern char bcd_to_pca[64];\r
123extern char bcd_to_pch[64];\r
124\r
125char *pch_table[4] = {\r
126 bcd_to_ascii_h, bcd_to_ascii_a, bcd_to_pch, bcd_to_pca,\r
127 };\r
128\r
129t_stat lpt_reset (DEVICE *dptr);\r
130t_stat lpt_svc (UNIT *uptr);\r
131t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit);\r
132t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 flags);\r
133t_stat lpt_end_line (UNIT *uptr);\r
134\r
135extern 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
144DIB lpt_dib = { &lpt_chsel, &lpt_chwr };\r
145\r
146UNIT lpt_unit = {\r
147 UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_CONS+UNIT_TEXT, 0)\r
148 };\r
149\r
150REG 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
166MTAB 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
176DEVICE 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
186t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit)\r
187{\r
188if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */\r
189\r
190switch (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
208return 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
216t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 eorfl)\r
217{\r
218uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */\r
219\r
220lpt_chob = val & DMASK; /* store data */\r
221lpt_chob_v = 1; /* set valid */\r
222if (lpt_sta == ECS_DATA) return SCPE_OK;\r
223if (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
234return SCPE_IERR;\r
235}\r
236\r
237/* Unit timeout */\r
238\r
239t_stat lpt_svc (UNIT *uptr)\r
240{\r
241uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */\r
242uint32 i, map;\r
243\r
244switch (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
304return SCPE_OK;\r
305}\r
306\r
307/* End line routine */\r
308\r
309t_stat lpt_end_line (UNIT *uptr)\r
310{\r
311uint32 i, col, row, bufw, colbin;\r
312char *pch, bcd, lpt_cbuf[LPT_CHRLNT + 1];\r
313t_uint64 dat;\r
314\r
315pch = pch_table[GET_PCHAIN (lpt_unit.flags)]; /* get print chain */\r
316for (col = 0; col < (LPT_CHRLNT + 1); col++) /* clear ascii buf */\r
317 lpt_cbuf[col] = ' '; \r
318for (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
328for (i = LPT_CHRLNT; (i > 0) &&\r
329 (lpt_cbuf[i - 1] == ' '); --i) ; /* trim spaces */\r
330lpt_cbuf[i] = 0; /* append nul */\r
331if (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
341else 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
346else return SCPE_UNATT; /* otherwise error */\r
347lpt_sta = LPS_END; /* end line state */\r
348sim_cancel (uptr); /* cancel current */\r
349sim_activate (uptr, lpt_tstop); /* long timer */\r
350return SCPE_OK;\r
351}\r
352\r
353/* Reset routine */\r
354\r
355t_stat lpt_reset (DEVICE *dptr)\r
356{\r
357uint32 i;\r
358\r
359for (i = 0; i < LPT_BINLNT; i++) lpt_bbuf[i] = 0; /* clear bin buf */\r
360for (i = 0; i < LPT_ECHLNT; i++) lpt_ebuf[i] = 0; /* clear echo buf */\r
361lpt_sta = 0; /* clear state */\r
362lpt_cmd = 0; /* clear modes */\r
363lpt_bptr = 0; /* clear buf ptr */\r
364lpt_chob = 0;\r
365lpt_chob_v = 0;\r
366sim_cancel (&lpt_unit); /* stop printer */\r
367return SCPE_OK;\r
368}\r