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