First Commit of my working state
[simh.git] / I1401 / i1401_lp.c
1 /* i1401_lp.c: IBM 1403 line printer simulator
2
3 Copyright (c) 1993-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 1403 line printer
27
28 19-Jan-07 RMS Added UNIT_TEXT flag
29 07-Mar-05 RMS Fixed bug in write_line (reported by Van Snyder)
30 25-Apr-03 RMS Revised for extended file support
31 30-May-02 RMS Widened POS to 32b
32 13-Apr-01 RMS Revised for register arrays
33 */
34
35 #include "i1401_defs.h"
36
37 extern uint8 M[];
38 extern char bcd_to_ascii_old[64];
39 extern char bcd_to_ascii_a[64], bcd_to_ascii_h[64];
40 extern char bcd_to_pca[64], bcd_to_pch[64];
41 extern int32 iochk, ind[64];
42 extern t_bool conv_old;
43
44 int32 cct[CCT_LNT] = { 03 };
45 int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0;
46
47 t_stat lpt_reset (DEVICE *dptr);
48 t_stat lpt_attach (UNIT *uptr, char *cptr);
49 t_stat space (int32 lines, int32 lflag);
50
51 char *pch_table_old[4] = {
52 bcd_to_ascii_old, bcd_to_pca, bcd_to_pch, bcd_to_ascii_old
53 };
54 char *pch_table[4] = {
55 bcd_to_ascii_a, bcd_to_pca, bcd_to_pch, bcd_to_ascii_h
56 };
57
58 #define UNIT_V_FT (UNIT_V_UF + 0)
59 #define UNIT_V_48 (UNIT_V_UF + 1)
60 #define UNIT_FT (1 << UNIT_V_FT)
61 #define UNIT_48 (1 << UNIT_V_48)
62 #define GET_PCHAIN(x) (((x) >> UNIT_V_FT) & (UNIT_FT|UNIT_48))
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 (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0)
74 };
75
76 REG lpt_reg[] = {
77 { FLDATA (ERR, ind[IN_LPT], 0) },
78 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },
79 { BRDATA (CCT, cct, 8, 32, CCT_LNT) },
80 { DRDATA (LINES, lines, 8), PV_LEFT },
81 { DRDATA (CCTP, cctptr, 8), PV_LEFT },
82 { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT },
83 { NULL }
84 };
85
86 MTAB lpt_mod[] = {
87 { UNIT_48, UNIT_48, "48 character chain", "48" },
88 { UNIT_48, 0, "64 character chain", "64" },
89 { UNIT_FT, UNIT_FT, "Fortran set", "FORTRAN" },
90 { UNIT_FT, 0, "business set", "BUSINESS" },
91 { UNIT_FT|UNIT_48, 0, NULL, "PCF" }, /* obsolete */
92 { UNIT_FT|UNIT_48, UNIT_48, NULL, "PCA" },
93 { UNIT_FT|UNIT_48, UNIT_FT|UNIT_48, NULL, "PCH" },
94 { 0 }
95 };
96
97 DEVICE lpt_dev = {
98 "LPT", &lpt_unit, lpt_reg, lpt_mod,
99 1, 10, 31, 1, 8, 7,
100 NULL, NULL, &lpt_reset,
101 NULL, &lpt_attach, NULL
102 };
103
104 /* Print routine
105
106 Modifiers have been checked by the caller
107 SQUARE = word mark mode
108 S = suppress automatic newline
109 */
110
111 t_stat write_line (int32 ilnt, int32 mod)
112 {
113 int32 i, t, wm, sup;
114 char *bcd2asc;
115 static char lbuf[LPT_WIDTH + 1]; /* + null */
116
117 if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */
118 wm = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_SQUARE);
119 sup = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_S);
120 ind[IN_LPT] = 0; /* clear error */
121 if (conv_old) /* get print chain */
122 bcd2asc = pch_table_old[GET_PCHAIN (lpt_unit.flags)];
123 else bcd2asc = pch_table[GET_PCHAIN (lpt_unit.flags)];
124 for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */
125 t = M[LPT_BUF + i];
126 if (wm) lbuf[i] = (t & WM)? '1': ' '; /* wmarks -> 1 or sp */
127 else lbuf[i] = bcd2asc[t & CHAR]; /* normal */
128 }
129 lbuf[LPT_WIDTH] = 0; /* trailing null */
130 for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0;
131 fputs (lbuf, lpt_unit.fileref); /* write line */
132 if (lines) space (lines, lflag); /* cc action? do it */
133 else if (sup == 0) space (1, FALSE); /* default? 1 line */
134 else {
135 fputc ('\r', lpt_unit.fileref); /* sup -> overprint */
136 lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */
137 }
138 lines = lflag = 0; /* clear cc action */
139 if (ferror (lpt_unit.fileref)) { /* error? */
140 ind[IN_LPT] = 1;
141 perror ("Line printer I/O error");
142 clearerr (lpt_unit.fileref);
143 if (iochk) return SCPE_IOERR;
144 }
145 return SCPE_OK;
146 }
147
148 /* Carriage control routine
149
150 The modifier has not been checked, its format is
151 <5:4> = 00, skip to channel now
152 = 01, space lines after
153 = 10, space lines now
154 = 11, skip to channel after
155 <3:0> = number of lines or channel number
156 */
157
158 t_stat carriage_control (int32 mod)
159 {
160 int32 i, action;
161
162 action = (mod & ZONE) >> V_ZONE; /* get mod type */
163 mod = mod & DIGIT; /* isolate value */
164
165 switch (action) {
166
167 case 0: /* to channel now */
168 if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK;
169 for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */
170 if (CHP (mod, cct[(cctptr + i) % cctlnt]))
171 return space (i, TRUE);
172 }
173 return STOP_CCT; /* runaway channel */
174
175 case 1: /* space after */
176 if (mod <= 3) {
177 lines = mod; /* save # lines */
178 lflag = FALSE; /* flag spacing */
179 ind[IN_CC9] = ind[IN_CC12] = 0;
180 }
181 return SCPE_OK;
182
183 case 2: /* space now */
184 if (mod <= 3) return space (mod, FALSE);
185 return SCPE_OK;
186
187 case 3: /* to channel after */
188 if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */
189 ind[IN_CC9] = ind[IN_CC12] = 0;
190 for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */
191 if (CHP (mod, cct[(cctptr + i) % cctlnt])) {
192 lines = i; /* save # lines */
193 lflag = TRUE; /* flag skipping */
194 return SCPE_OK;
195 }
196 }
197 return STOP_CCT; /* runaway channel */
198 }
199
200 return SCPE_OK;
201 }
202
203 /* Space routine - space or skip n lines
204
205 Inputs:
206 count = number of lines to space or skip
207 sflag = skip (TRUE) or space (FALSE)
208 */
209
210 t_stat space (int32 count, int32 sflag)
211 {
212 int32 i;
213
214 if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT;
215 cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */
216 if (sflag && CHP (0, cct[cctptr])) /* skip, top of form? */
217 fputs ("\n\f", lpt_unit.fileref); /* nl, ff */
218 else {
219 for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref);
220 }
221 lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */
222 ind[IN_CC9] = CHP (9, cct[cctptr]) != 0; /* set indicators */
223 ind[IN_CC12] = CHP (12, cct[cctptr]) != 0;
224 return SCPE_OK;
225 }
226
227 /* Reset routine */
228
229 t_stat lpt_reset (DEVICE *dptr)
230 {
231 cctptr = 0; /* clear cct ptr */
232 lines = lflag = 0; /* no cc action */
233 ind[IN_LPT] = 0;
234 return SCPE_OK;
235 }
236
237 /* Attach routine */
238
239 t_stat lpt_attach (UNIT *uptr, char *cptr)
240 {
241 cctptr = 0; /* clear cct ptr */
242 lines = 0; /* no cc action */
243 ind[IN_LPT] = 0;
244 return attach_unit (uptr, cptr);
245 }