First Commit of my working state
[simh.git] / I1620 / i1620_lp.c
CommitLineData
196ba1fc
PH
1/* i1620_lp.c: IBM 1443 line printer simulator\r
2\r
3 Copyright (c) 2002-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 1443 line printer\r
27\r
28 19-Jan-07 RMS Added UNIT_TEXT flag\r
29 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility\r
30 29-Dec-03 RMS Fixed bug in scheduling\r
31 25-Apr-03 RMS Revised for extended file support\r
32*/\r
33\r
34#include "i1620_defs.h"\r
35\r
36#define LPT_BSIZE 197 /* buffer size */\r
37\r
38#define K_IMM 0x10 /* control now */\r
39#define K_LIN 0x20 /* spc lines */\r
40#define K_CH10 0x40 /* chan 10 */\r
41#define K_LCNT 0x03 /* line count */\r
42#define K_CHAN 0x0F /* channel */\r
43\r
44extern uint8 M[MAXMEMSIZE];\r
45extern uint8 ind[NUM_IND];\r
46extern UNIT cpu_unit;\r
47extern uint32 io_stop;\r
48\r
49uint32 cct[CCT_LNT] = { 03 }; /* car ctrl tape */\r
50int32 cct_lnt = 66, cct_ptr = 0; /* cct len, ptr */\r
51int32 lpt_bptr = 0; /* lpt buf ptr */\r
52char lpt_buf[LPT_BSIZE + 1]; /* lpt buf */\r
53int32 lpt_savctrl = 0; /* saved spc ctrl */\r
54\r
55t_stat lpt_svc (UNIT *uptr);\r
56t_stat lpt_reset (DEVICE *dptr);\r
57t_stat lpt_attach (UNIT *uptr, char *cptr);\r
58void lpt_buf_init (void);\r
59t_stat lpt_num (uint32 pa, uint32 len, uint32 f1);\r
60t_stat lpt_print (void);\r
61t_stat lpt_space (int32 lines, int32 lflag);\r
62\r
63#define CHP(ch,val) ((val) & (1 << (ch)))\r
64\r
65/* LPT data structures\r
66\r
67 lpt_dev LPT device descriptor\r
68 lpt_unit LPT unit descriptor\r
69 lpt_reg LPT register list\r
70*/\r
71\r
72UNIT lpt_unit = {\r
73 UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 50)\r
74 };\r
75\r
76REG lpt_reg[] = {\r
77 { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE + 1) },\r
78 { DRDATA (BPTR, lpt_bptr, 8) },\r
79 { HRDATA (PCTL, lpt_savctrl, 8) },\r
80 { FLDATA (PRCHK, ind[IN_PRCHK], 0) },\r
81 { FLDATA (PRCH9, ind[IN_PRCH9], 0) },\r
82 { FLDATA (PRCH12, ind[IN_PRCH12], 0) },\r
83 { FLDATA (PRBSY, ind[IN_PRBSY], 0) },\r
84 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },\r
85 { BRDATA (CCT, cct, 8, 32, CCT_LNT) },\r
86 { DRDATA (CCTP, cct_ptr, 8), PV_LEFT },\r
87 { DRDATA (CCTL, cct_lnt, 8), REG_RO + PV_LEFT },\r
88 { NULL }\r
89 };\r
90\r
91DEVICE lpt_dev = {\r
92 "LPT", &lpt_unit, lpt_reg, NULL,\r
93 1, 10, 31, 1, 8, 7,\r
94 NULL, NULL, &lpt_reset,\r
95 NULL, &lpt_attach, NULL\r
96 };\r
97\r
98/* Data tables */\r
99\r
100/* Numeric (flag plus digit) to lineprinter (ASCII) */\r
101\r
102const char num_to_lpt[32] = {\r
103 '0', '1', '2', '3', '4', '5', '6', '7',\r
104 '8', '9', '|', ' ', '@', ':', ' ', 'G',\r
105 '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\r
106 'Q', 'R', 'W', ' ', '*', ' ', -1, 'X'\r
107 };\r
108\r
109/* Alphameric (digit pair) to lineprinter (ASCII) */\r
110\r
111const char alp_to_lpt[256] = {\r
112 ' ', -1, '?', '.', ')', -1, -1, -1, /* 00 */\r
113 -1, -1, -1, -1, -1, -1, -1, -1,\r
114 '+', -1, '!', '$', '*', ' ', -1, -1, /* 10 */\r
115 -1, -1, -1, -1, -1, -1, -1, -1,\r
116 '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */\r
117 -1, -1, -1, -1, -1, -1, -1, -1,\r
118 -1, -1, '0', '=', '@', ':', -1, -1, /* 30 */\r
119 -1, -1, -1, -1, -1, -1, -1, -1,\r
120 -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */\r
121 'H', 'I', -1, -1, -1, -1, -1, -1,\r
122 '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */\r
123 'Q', 'R', -1, -1, -1, -1, -1, -1,\r
124 -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */\r
125 'Y', 'Z', -1, -1, -1, -1, -1, -1,\r
126 '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */\r
127 '8', '9', -1, -1, -1, -1, -1, -1,\r
128 -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */\r
129 -1, -1, -1, -1, -1, -1, -1, -1,\r
130 -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */\r
131 -1, -1, -1, -1, -1, -1, -1, -1,\r
132 -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */\r
133 -1, -1, -1, -1, -1, -1, -1, -1,\r
134 -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */\r
135 -1, -1, -1, -1, -1, -1, -1, -1,\r
136 -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */\r
137 -1, -1, -1, -1, -1, -1, -1, -1,\r
138 -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */\r
139 -1, -1, -1, -1, -1, -1, -1, -1,\r
140 -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */\r
141 -1, -1, -1, -1, -1, -1, -1, -1,\r
142 -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */\r
143 -1, -1, -1, -1, -1, -1, -1, -1\r
144 };\r
145\r
146/* Line printer IO routine\r
147 \r
148 - Hard errors halt the system.\r
149 - Invalid characters print a blank, set the WRCHK and PRCHK\r
150 flags, and halt the system if IO stop is set.\r
151*/\r
152\r
153t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1)\r
154{\r
155int8 lpc;\r
156uint8 z, d;\r
157t_stat r, sta;\r
158\r
159sta = SCPE_OK;\r
160sim_cancel (&lpt_unit); /* "stall" until */\r
161ind[IN_PRBSY] = 0; /* printer free */\r
162\r
163switch (op) { /* decode op */\r
164\r
165 case OP_K: /* control */\r
166 lpt_savctrl = (f0 << 4) | f1; /* form ctrl */\r
167 if (lpt_savctrl & K_IMM) return lpt_print (); /* immediate? */\r
168 break;\r
169\r
170 case OP_DN:\r
171 return lpt_num (pa, 20000 - (pa % 20000), f1); /* dump numeric */\r
172\r
173 case OP_WN:\r
174 return lpt_num (pa, 0, f1); /* write numeric */\r
175\r
176 case OP_WA:\r
177 for ( ; lpt_bptr < LPT_BSIZE; lpt_bptr++) { /* only fill buf */\r
178 d = M[pa] & DIGIT; /* get digit */\r
179 z = M[pa - 1] & DIGIT; /* get zone */\r
180 if ((d & REC_MARK) == REC_MARK) break; /* 8-2 char? */\r
181 lpc = alp_to_lpt[(z << 4) | d]; /* translate pair */\r
182 if (lpc < 0) { /* bad char? */\r
183 ind[IN_WRCHK] = ind[IN_PRCHK] = 1; /* wr chk */\r
184 if (io_stop) sta = STOP_INVCHR; /* set return status */\r
185 }\r
186 lpt_buf[lpt_bptr] = lpc & 0x7F; /* fill buffer */\r
187 pa = ADDR_A (pa, 2); /* incr mem addr */\r
188 }\r
189 if ((f1 & 1) == 0) { ; /* print now? */\r
190 r = lpt_print (); /* print line */\r
191 if (r != SCPE_OK) return r;\r
192 }\r
193 return sta;\r
194\r
195 default: /* invalid function */\r
196 return STOP_INVFNC;\r
197 }\r
198\r
199return SCPE_OK;\r
200}\r
201\r
202/* Print numeric */\r
203\r
204t_stat lpt_num (uint32 pa, uint32 len, uint32 f1)\r
205{\r
206uint32 end;\r
207uint8 d;\r
208int8 lpc;\r
209t_stat r, sta;\r
210\r
211sta = SCPE_OK;\r
212end = pa + len;\r
213for ( ; lpt_bptr < LPT_BSIZE; lpt_bptr++) { /* only fill buf */\r
214 d = M[pa]; /* get digit */\r
215 if (len? (pa >= end): /* end reached? */\r
216 ((d & REC_MARK) == REC_MARK)) break;\r
217 lpc = num_to_lpt[d]; /* translate */\r
218 if (lpc < 0) { /* bad char? */\r
219 ind[IN_WRCHK] = ind[IN_PRCHK] = 1; /* wr chk */\r
220 if (io_stop) sta = STOP_INVCHR; /* set return status */\r
221 }\r
222 lpt_buf[lpt_bptr++] = lpc & 0x7F; /* fill buffer */\r
223 PP (pa); /* incr mem addr */\r
224 }\r
225if ((f1 & 1) == 0) { /* print now? */\r
226 r = lpt_print (); /* print line */\r
227 if (r != SCPE_OK) return r;\r
228 }\r
229return sta;\r
230}\r
231\r
232/* Print and space */\r
233\r
234t_stat lpt_print (void)\r
235{\r
236int32 i, chan, ctrl = lpt_savctrl;\r
237\r
238if ((lpt_unit.flags & UNIT_ATT) == 0) { /* not attached? */\r
239 ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */\r
240 return SCPE_UNATT;\r
241 }\r
242\r
243ind[IN_PRBSY] = 1; /* print busy */\r
244sim_activate (&lpt_unit, lpt_unit.wait); /* start timer */\r
245\r
246for (i = LPT_WIDTH; i <= LPT_BSIZE; i++) /* clear unprintable */\r
247 lpt_buf[i] = ' ';\r
248while ((lpt_bptr > 0) && (lpt_buf[lpt_bptr - 1] == ' '))\r
249 lpt_buf[--lpt_bptr] = 0; /* trim buffer */\r
250if (lpt_bptr) { /* any line? */\r
251 fputs (lpt_buf, lpt_unit.fileref); /* print */\r
252 lpt_unit.pos = ftell (lpt_unit.fileref); /* update pos */\r
253 lpt_buf_init (); /* reinit buf */\r
254 if (ferror (lpt_unit.fileref)) { /* error? */\r
255 ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */\r
256 perror ("LPT I/O error");\r
257 clearerr (lpt_unit.fileref);\r
258 return SCPE_IOERR;\r
259 }\r
260 }\r
261\r
262lpt_savctrl = 0x61; /* reset ctrl */\r
263if ((ctrl & K_LIN) == ((ctrl & K_IMM)? 0: K_LIN)) /* space lines? */\r
264 return lpt_space (ctrl & K_LCNT, FALSE);\r
265chan = lpt_savctrl & K_CHAN; /* basic chan */\r
266if (lpt_savctrl & K_CH10) { /* chan 10-12? */\r
267 if (chan == 0) chan = 10;\r
268 else if (chan == 3) chan = 11;\r
269 else if (chan == 4) chan = 12;\r
270 else chan = 0;\r
271 }\r
272if ((chan == 0) || (chan > 12)) return STOP_INVFNC;\r
273for (i = 1; i < cct_lnt + 1; i++) { /* sweep thru cct */\r
274 if (CHP (chan, cct[(cct_ptr + i) % cct_lnt]))\r
275 return lpt_space (i, TRUE);\r
276 }\r
277return STOP_CCT; /* runaway channel */\r
278}\r
279\r
280/* Space routine - space or skip n lines\r
281 \r
282 Inputs:\r
283 count = number of lines to space or skip\r
284 sflag = skip (TRUE) or space (FALSE)\r
285*/\r
286\r
287t_stat lpt_space (int32 count, int32 sflag)\r
288{\r
289int32 i;\r
290\r
291cct_ptr = (cct_ptr + count) % cct_lnt; /* adv cct, mod lnt */\r
292if (sflag && CHP (0, cct[cct_ptr])) /* skip, top of form? */\r
293 fputs ("\n\f", lpt_unit.fileref); /* nl, ff */\r
294else {\r
295 for (i = 0; i < count; i++) /* count lines */\r
296 fputc ('\n', lpt_unit.fileref);\r
297 }\r
298lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */\r
299ind[IN_PRCH9] = CHP (9, cct[cct_ptr]) != 0; /* set indicators */\r
300ind[IN_PRCH12] = CHP (12, cct[cct_ptr]) != 0;\r
301if (ferror (lpt_unit.fileref)) { /* error? */\r
302 ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */\r
303 perror ("LPT I/O error");\r
304 clearerr (lpt_unit.fileref);\r
305 return SCPE_IOERR;\r
306 }\r
307return SCPE_OK;\r
308}\r
309\r
310/* Unit service - clear printer busy */\r
311\r
312t_stat lpt_svc (UNIT *uptr)\r
313{\r
314ind[IN_PRBSY] = 0;\r
315return SCPE_OK;\r
316}\r
317\r
318/* Initialize lpt buffer */\r
319\r
320void lpt_buf_init (void)\r
321{\r
322int32 i;\r
323\r
324lpt_bptr = 0;\r
325for (i = 0; i < LPT_WIDTH + 1; i++) lpt_buf[i] = 0;\r
326return;\r
327}\r
328\r
329/* Reset routine */\r
330\r
331t_stat lpt_reset (DEVICE *dptr)\r
332{\r
333lpt_buf_init (); /* clear buffer */\r
334cct_ptr = 0; /* clear cct ptr */\r
335lpt_savctrl = 0x61; /* clear cct action */\r
336ind[IN_PRCHK] = ind[IN_PRBSY] = 0; /* clear indicators */\r
337ind[IN_PRCH9] = ind[IN_PRCH12] = 0;\r
338return SCPE_OK;\r
339}\r
340\r
341/* Attach routine */\r
342\r
343t_stat lpt_attach (UNIT *uptr, char *cptr)\r
344{\r
345lpt_reset (&lpt_dev);\r
346return attach_unit (uptr, cptr);\r
347}\r