First Commit of my working state
[simh.git] / S3 / s3_lp.c
1 /* s3_lp.c: IBM 1403 line printer simulator
2
3 Copyright (c) 2001-2005, Charles E. Owen
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 Charles E. Owen 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 Charles E. Owen.
25
26 lpt 1403 line printer
27
28 25-Apr-03 RMS Revised for extended file support
29 08-Oct-02 RMS Added impossible function catcher
30 */
31
32 #include "s3_defs.h"
33
34 extern uint8 M[];
35 extern char bcd_to_ascii[64];
36 extern int32 iochk, ind[64];
37 int32 cct[CCT_LNT] = { 03 };
38 int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0;
39 t_stat lpt_reset (DEVICE *dptr);
40 t_stat lpt_attach (UNIT *uptr, char *cptr);
41 t_stat write_line (int32 ilnt, int32 mod);
42 t_stat space (int32 lines, int32 lflag);
43 t_stat carriage_control (int32 action, int32 mod);
44 extern unsigned char ebcdic_to_ascii[256];
45
46 #define UNIT_V_PCHAIN (UNIT_V_UF + 0)
47 #define UNIT_M_PCHAIN 03
48 #define M_UCS 00 /* Universal */
49 #define M_PCF 00 /* full */
50 #define M_PCA 01 /* business */
51 #define M_PCH 02 /* Fortran */
52 #define UNIT_PCHAIN (UNIT_M_PCHAIN << UNIT_V_PCHAIN)
53 #define UCS (M_UCS << UNIT_V_PCHAIN)
54 #define PCF (M_PCF << UNIT_V_PCHAIN)
55 #define PCA (M_PCA << UNIT_V_PCHAIN)
56 #define PCH (M_PCH << UNIT_V_PCHAIN)
57 #define GET_PCHAIN(x) (((x) >> UNIT_V_PCHAIN) & UNIT_M_PCHAIN)
58 #define CHP(ch,val) ((val) & (1 << (ch)))
59
60 int32 LPDAR; /* Data Address */
61 int32 LPFLR; /* Forms Length */
62 int32 LPIAR; /* Image address */
63 int32 linectr; /* current line # */
64 int32 lpterror = 0;
65 int32 CC9 = 0;
66 int32 CC12 = 0;
67
68 /* LPT data structures
69
70 lpt_dev LPT device descriptor
71 lpt_unit LPT unit descriptor
72 lpt_reg LPT register list
73 */
74
75 UNIT lpt_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) };
76
77 REG lpt_reg[] = {
78 { FLDATA (ERR, lpterror, 0) },
79 { HRDATA (LPDAR, LPDAR, 16) },
80 { HRDATA (LPFLR, LPFLR, 8) },
81 { HRDATA (LPIAR, LPIAR, 16) },
82 { DRDATA (LINECT, linectr, 8) },
83 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },
84 { BRDATA (CCT, cct, 8, 32, CCT_LNT) },
85 { DRDATA (LINES, lines, 8), PV_LEFT },
86 { DRDATA (CCTP, cctptr, 8), PV_LEFT },
87 { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT },
88 { GRDATA (CHAIN, lpt_unit.flags, 10, 2, UNIT_V_PCHAIN), REG_HRO },
89 { NULL }
90 };
91
92 MTAB lpt_mod[] = {
93 { UNIT_PCHAIN, UCS, "UCS", "UCS", NULL },
94 { UNIT_PCHAIN, PCA, "A chain", "PCA", NULL },
95 { UNIT_PCHAIN, PCH, "H chain", "PCH", NULL },
96 { 0 }
97 };
98
99 DEVICE lpt_dev = {
100 "LPT", &lpt_unit, lpt_reg, lpt_mod,
101 1, 10, 31, 1, 8, 7,
102 NULL, NULL, &lpt_reset,
103 NULL, NULL, NULL
104 };
105
106
107 /* -------------------------------------------------------------------- */
108
109 /* Printer: master routine */
110
111 int32 lpt (int32 op, int32 m, int32 n, int32 data)
112 {
113 int32 iodata;
114 switch (op) {
115 case 0: /* SIO 1403 */
116 iodata = 0;
117 printf("\0");
118 switch (n) {
119 case 0x00: /* Spacing only */
120 if (data > 0 && data < 4)
121 iodata = carriage_control(2, data);
122 break;
123 case 0x02: /* Print & space */
124 iodata = write_line(0, 0);
125 if (data > 3) data = 0;
126 if (iodata == SCPE_OK)
127 iodata = carriage_control(2, data);
128 break;
129 case 0x04: /* Skip only */
130 iodata = carriage_control(4, data);
131 break;
132 case 0x06: /* Print and skip */
133 iodata = write_line(0, 0);
134 if (iodata == SCPE_OK)
135 iodata = carriage_control(4, data);
136 break;
137 default:
138 return STOP_INVDEV;
139 }
140 return iodata;
141 case 1: /* LIO 1403 */
142 switch (n) {
143 case 0x00: /* LPFLR */
144 LPFLR = (data >> 8) & 0xff;
145 break;
146 case 0x04:
147 LPIAR = data & 0xffff;
148 break;
149 case 0x06:
150 LPDAR = data & 0xffff;
151 break;
152 default:
153 return STOP_INVDEV;
154 }
155 return SCPE_OK;
156 case 2: /* TIO 1403 */
157 iodata = 0;
158 switch (n) {
159 case 0x00: /* Not ready/check */
160 if (lpterror)
161 iodata = 1;
162 if ((lpt_unit.flags & UNIT_ATT) == 0)
163 iodata = 1;
164 break;
165 case 0x02: /* Buffer Busy */
166 iodata = 0;
167 break;
168 case 0x04: /* Carriage Busy */
169 iodata = 0;
170 break;
171 case 0x06: /* Printer busy */
172 iodata = 0;
173 break;
174 default:
175 return (STOP_INVDEV << 16);
176 }
177 return ((SCPE_OK << 16) | iodata);
178 case 3: /* SNS 1403 */
179 switch (n) {
180 case 0x00: /* Line count */
181 iodata = (linectr << 8);
182 break;
183 case 0x02: /* Timing data */
184 iodata = 0;
185 break;
186 case 0x03: /* Check data */
187 iodata = 0;
188 break;
189 case 0x04: /* LPIAR */
190 iodata = LPIAR;
191 break;
192 case 0x06: /* LPDAR */
193 iodata = LPDAR;
194 break;
195 default:
196 return (STOP_INVDEV << 16);
197 }
198 return ((SCPE_OK << 16) | iodata);
199 case 4: /* APL 1403 */
200 iodata = 0;
201 return ((SCPE_OK << 16) | iodata);
202 default:
203 break;
204 }
205 printf (">>LPT non-existent function %d\n", op);
206 return SCPE_OK;
207 }
208
209
210 /* Print routine
211
212 Modifiers have been checked by the caller
213 S = suppress automatic newline
214 */
215
216 t_stat write_line (int32 ilnt, int32 mod)
217 {
218 int32 i, t, lc;
219 static char lbuf[LPT_WIDTH + 1]; /* + null */
220
221 if ((lpt_unit.flags & UNIT_ATT) == 0)
222 return SCPE_UNATT;
223
224 lpterror = 0;
225 lc = LPDAR; /* clear error */
226 for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */
227 t = M[lc];
228 lbuf[i] = ebcdic_to_ascii[t & 0xff];
229 M[lc] = 0x40; /* HJS MOD */
230 lc++;
231 }
232 for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0;
233 fputs (lbuf, lpt_unit.fileref); /* write line */
234 if (lines) space (lines, lflag); /* cc action? do it */
235 else if (mod == 0) space (1, FALSE); /* default? 1 line */
236 else {
237 fputc ('\r', lpt_unit.fileref); /* sup -> overprint */
238 lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */
239 }
240 lines = lflag = 0; /* clear cc action */
241 if (ferror (lpt_unit.fileref)) { /* error? */
242 perror ("Line printer I/O error");
243 clearerr (lpt_unit.fileref);
244 lpterror = 1;
245 }
246 return SCPE_OK;
247 }
248
249 /* Carriage control routine
250
251 Parameters:
252 action = 00, skip to channel now
253 = 01, space lines after
254 = 02, space lines now
255 = 03, skip to channel after
256 = 04, skip to line number
257 mod = number of lines or channel number or line number
258 */
259
260 t_stat carriage_control (int32 action, int32 mod)
261 {
262 int32 i;
263
264 if ((lpt_unit.flags & UNIT_ATT) == 0)
265 return SCPE_UNATT;
266
267 switch (action) {
268 case 0: /* to channel now */
269 if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK;
270 for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */
271 if (CHP (mod, cct[(cctptr + i) % cctlnt]))
272 return space (i, TRUE);
273 }
274 return STOP_INVDEV; /* runaway channel */
275 case 1: /* space after */
276 if (mod <= 3) {
277 lines = mod; /* save # lines */
278 lflag = FALSE; /* flag spacing */
279 CC9 = CC12 = 0;
280 }
281 return SCPE_OK;
282 case 2: /* space now */
283 if (mod <= 3) return space (mod, FALSE);
284 return SCPE_OK;
285 case 3: /* to channel after */
286 if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */
287 CC9 = CC12 = 0;
288 for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */
289 if (CHP (mod, cct[(cctptr + i) % cctlnt])) {
290 lines = i; /* save # lines */
291 lflag = TRUE; /* flag skipping */
292 return SCPE_OK;
293 }
294 }
295 return STOP_INVDEV;
296 case 4: /* To line # */
297 if (mod < 2) {
298 fputs ("\n\f", lpt_unit.fileref); /* nl, ff */
299 linectr = 1;
300 } else {
301 if (mod <= linectr) {
302 fputs ("\n\f", lpt_unit.fileref);
303 linectr = 1;
304 }
305 while (1) {
306 if (linectr == mod)
307 break;
308 space(1, 0);
309 }
310 }
311 return SCPE_OK;
312 }
313 return SCPE_OK;
314 }
315
316 /* Space routine - space or skip n lines
317
318 Inputs:
319 count = number of lines to space or skip
320 sflag = skip (TRUE) or space (FALSE)
321 */
322
323 t_stat space (int32 count, int32 sflag)
324 {
325 int32 i;
326
327 if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT;
328 cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */
329 if (sflag && CHP (0, cct[cctptr])) { /* skip, top of form? */
330 fputs ("\n\f", lpt_unit.fileref); /* nl, ff */
331 linectr = 1;
332 } else {
333 for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref);
334 }
335 lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */
336 CC9 = CHP (9, cct[cctptr]) != 0; /* set indicators */
337 CC12 = CHP (12, cct[cctptr]) != 0;
338 linectr += count;
339 if (linectr > LPFLR)
340 linectr -= LPFLR;
341 return SCPE_OK;
342 }
343
344 /* Reset routine */
345
346 t_stat lpt_reset (DEVICE *dptr)
347 {
348 cctptr = 0; /* clear cct ptr */
349 lines = linectr = lflag = 0; /* no cc action */
350 lpterror = 0;
351 return SCPE_OK;
352 }
353
354 /* Attach routine */
355
356 t_stat lpt_attach (UNIT *uptr, char *cptr)
357 {
358 cctptr = 0; /* clear cct ptr */
359 lines = 0; /* no cc action */
360 lpterror = 0;
361 linectr = 0;
362 return attach_unit (uptr, cptr);
363 }