First Commit of my working state
[simh.git] / Interdata / id_lp.c
1 /* id_lp.c: Interdata line printer
2
3 Copyright (c) 2001-2008, 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 M46-206 line printer
27
28 27-May-08 RMS Fixed bug in printing test (from Davis Johnson)
29 19-Jan-07 RMS Added UNIT_TEXT flag
30 25-Apr-03 RMS Revised for extended file support
31 */
32
33 #include "id_defs.h"
34 #include <ctype.h>
35
36 /* Device definitions */
37
38 #define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */
39 #define UNIT_UC (1 << UNIT_V_UC)
40 #define SPC_BASE 0x40 /* spacing base */
41 #define VFU_BASE 0x78 /* VFU base */
42 #define VFU_WIDTH 0x8 /* VFU width */
43 #define LF 0xA
44 #define VT 0xB
45 #define VT_VFU 4 /* VFU chan for VT */
46 #define FF 0xC
47 #define FF_VFU 8 /* VFU chan for FF */
48 #define CR 0xD
49 #define VFUP(ch,val) ((val) & (1 << (ch))) /* VFU chan test */
50
51 /* Status byte, * = dynamic */
52
53 #define STA_PAPE 0x40 /* *paper empty */
54 #define STA_MASK (STA_BSY) /* static status */
55
56 uint32 lpt_sta = STA_BSY; /* status */
57 char lpxb[LPT_WIDTH + 1]; /* line buffer */
58 uint32 lpt_bptr = 0; /* buf ptr */
59 uint32 lpt_spnd = 0; /* space pending */
60 uint32 lpt_vfup = 0; /* VFU ptr */
61 uint32 lpt_vful = 1; /* VFU lnt */
62 uint8 lpt_vfut[VFU_LNT] = { 0xFF }; /* VFU tape */
63 uint32 lpt_arm = 0; /* int armed */
64 int32 lpt_ctime = 10; /* char time */
65 int32 lpt_stime = 1000; /* space time */
66 int32 lpt_stopioe = 0; /* stop on err */
67
68 extern uint32 int_req[INTSZ], int_enb[INTSZ];
69
70 DEVICE lpt_dev;
71 uint32 lpt (uint32 dev, uint32 op, uint32 dat);
72 t_stat lpt_svc (UNIT *uptr);
73 t_stat lpt_reset (DEVICE *dptr);
74 t_stat lpt_attach (UNIT *uptr, char *cptr);
75 t_stat lpt_bufout (UNIT *uptr);
76 t_stat lpt_vfu (UNIT *uptr, int32 ch);
77 t_stat lpt_spc (UNIT *uptr, int32 cnt);
78
79 /* LPT data structures
80
81 lpt_dev LPT device descriptor
82 lpt_unit LPT unit descriptors
83 lpt_reg LPT register list
84 */
85
86 DIB lpt_dib = { d_LPT, -1, v_LPT, NULL, &lpt, NULL };
87
88 UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_UC+UNIT_TEXT, 0) };
89
90 REG lpt_reg[] = {
91 { HRDATA (STA, lpt_sta, 8) },
92 { HRDATA (BUF, lpt_unit.buf, 7) },
93 { BRDATA (DBUF, lpxb, 16, 7, LPT_WIDTH) },
94 { HRDATA (DBPTR, lpt_bptr, 8) },
95 { HRDATA (VFUP, lpt_vfup, 8) },
96 { HRDATA (VFUL, lpt_vful, 8) },
97 { BRDATA (VFUT, lpt_vfut, 16, 8, VFU_LNT) },
98 { FLDATA (IREQ, int_req[l_LPT], i_LPT) },
99 { FLDATA (IENB, int_enb[l_LPT], i_LPT) },
100 { FLDATA (IARM, lpt_arm, 0) },
101 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },
102 { DRDATA (CTIME, lpt_ctime, 24), PV_LEFT },
103 { DRDATA (STIME, lpt_stime, 24), PV_LEFT },
104 { FLDATA (STOP_IOE, lpt_stopioe, 0) },
105 { HRDATA (DEVNO, lpt_dib.dno, 8), REG_HRO },
106 { NULL }
107 };
108
109 MTAB lpt_mod[] = {
110 { UNIT_UC, 0, "lower case", "LC", NULL },
111 { UNIT_UC, UNIT_UC, "upper case", "UC", NULL },
112 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
113 &set_dev, &show_dev, NULL },
114 { 0 }
115 };
116
117 DEVICE lpt_dev = {
118 "LPT", &lpt_unit, lpt_reg, lpt_mod,
119 1, 10, 31, 1, 16, 7,
120 NULL, NULL, &lpt_reset,
121 NULL, &lpt_attach, NULL,
122 &lpt_dib, DEV_DISABLE
123 };
124
125 /* Line printer: IO routine */
126
127 uint32 lpt (uint32 dev, uint32 op, uint32 dat)
128 {
129 int32 t;
130
131 switch (op) { /* case IO op */
132
133 case IO_ADR: /* select */
134 return BY; /* byte only */
135
136 case IO_OC: /* command */
137 lpt_arm = int_chg (v_LPT, dat, lpt_arm); /* upd int ctrl */
138 break;
139
140 case IO_WD: /* write */
141 t = lpt_unit.buf = dat & 0x7F; /* mask char */
142 lpt_sta = STA_BSY; /* set busy */
143 if (lpt_spnd || ((t >= LF) && (t <= CR))) /* space op? */
144 sim_activate (&lpt_unit, lpt_stime);
145 else sim_activate (&lpt_unit, lpt_ctime); /* normal char */
146 break;
147
148 case IO_SS: /* status */
149 t = lpt_sta & STA_MASK; /* status byte */
150 if ((lpt_unit.flags & UNIT_ATT) == 0) /* test paper out */
151 t = t | STA_EX | STA_PAPE | STA_BSY;
152 return t;
153 }
154
155 return 0;
156 }
157
158 /* Unit service */
159
160 t_stat lpt_svc (UNIT *uptr)
161 {
162 int32 t;
163 t_stat r = SCPE_OK;
164
165 lpt_sta = 0; /* clear busy */
166 if (lpt_arm) SET_INT (v_LPT); /* armed? intr */
167 if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
168 return IORETURN (lpt_stopioe, SCPE_UNATT);
169 t = uptr->buf; /* get character */
170 if (lpt_spnd || ((t >= LF) && (t < CR))) { /* spc pend or spc op? */
171 lpt_spnd = 0;
172 if (lpt_bufout (uptr) != SCPE_OK) /* print */
173 return SCPE_IOERR;
174 if ((t == 1) || (t == LF)) lpt_spc (uptr, 1); /* single space */
175 else if (t == VT) r = lpt_vfu (uptr, VT_VFU - 1); /* VT->VFU */
176 else if (t == 0xC) r = lpt_vfu (uptr, FF_VFU - 1); /* FF->VFU */
177 else if ((t >= SPC_BASE) && (t < VFU_BASE))
178 lpt_spc (uptr, t - SPC_BASE); /* space */
179 else if ((t >= VFU_BASE) && (t < VFU_BASE + VFU_WIDTH))
180 r = lpt_vfu (uptr, t - VFU_BASE); /* VFU */
181 else fputs ("\r", uptr->fileref); /* overprint */
182 uptr->pos = ftell (uptr->fileref); /* update position */
183 if (ferror (lpt_unit.fileref)) {
184 perror ("LPT I/O error");
185 clearerr (uptr->fileref);
186 return SCPE_IOERR;
187 }
188 }
189 else if (t == CR) { /* CR? */
190 lpt_spnd = 1; /* set spc pend */
191 return lpt_bufout (uptr); /* print line */
192 }
193 else if (t >= 0x20) { /* printable? */
194 if ((uptr->flags & UNIT_UC) && islower (t)) /* UC only? */
195 t = toupper (t);
196 if (lpt_bptr < LPT_WIDTH) lpxb[lpt_bptr++] = t;
197 }
198 return r;
199 }
200
201 /* Printing and spacing routines */
202
203 t_stat lpt_bufout (UNIT *uptr)
204 {
205 int32 i;
206 t_stat r = SCPE_OK;
207
208 if (lpt_bptr == 0) return SCPE_OK; /* any char in buf? */
209 for (i = LPT_WIDTH - 1; (i >= 0) && (lpxb[i] == ' '); i--)
210 lpxb[i] = 0; /* backscan line */
211 if (lpxb[0]) { /* any char left? */
212 fputs (lpxb, uptr->fileref); /* write line */
213 lpt_unit.pos = ftell (uptr->fileref); /* update position */
214 if (ferror (uptr->fileref)) {
215 perror ("LPT I/O error");
216 clearerr (uptr->fileref);
217 r = SCPE_IOERR;
218 }
219 }
220 lpt_bptr = 0; /* reset buffer */
221 for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' ';
222 lpxb[LPT_WIDTH] = 0;
223 return r;
224 }
225
226 t_stat lpt_vfu (UNIT *uptr, int32 ch)
227 {
228 uint32 i, j;
229
230 if ((ch == (FF_VFU - 1)) && VFUP (ch, lpt_vfut[0])) { /* top of form? */
231 fputs ("\n\f", uptr->fileref); /* nl + ff */
232 lpt_vfup = 0; /* top of page */
233 return SCPE_OK;
234 }
235 for (i = 1; i < lpt_vful + 1; i++) { /* sweep thru cct */
236 lpt_vfup = (lpt_vfup + 1) % lpt_vful; /* adv pointer */
237 if (VFUP (ch, lpt_vfut[lpt_vfup])) { /* chan punched? */
238 for (j = 0; j < i; j++) fputc ('\n', uptr->fileref);
239 return SCPE_OK;
240 }
241 }
242 return STOP_VFU; /* runaway channel */
243 }
244
245 t_stat lpt_spc (UNIT *uptr, int32 cnt)
246 {
247 int32 i;
248
249 if (cnt == 0) fputc ('\r', uptr->fileref);
250 else {
251 for (i = 0; i < cnt; i++) fputc ('\n', uptr->fileref);
252 lpt_vfup = (lpt_vfup + cnt) % lpt_vful;
253 }
254 return SCPE_OK;
255 }
256
257 /* Reset routine */
258
259 t_stat lpt_reset (DEVICE *dptr)
260 {
261 int32 i;
262
263 sim_cancel (&lpt_unit); /* deactivate */
264 lpt_sta = 0; /* clr busy */
265 lpt_bptr = 0; /* clr buf ptr */
266 for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' '; /* clr buf */
267 lpxb[LPT_WIDTH] = 0;
268 CLR_INT (v_LPT); /* clearr int */
269 CLR_ENB (v_LPT); /* disable int */
270 lpt_arm = 0; /* disarm int */
271 return SCPE_OK;
272 }
273
274 /* Attach routine */
275
276 t_stat lpt_attach (UNIT *uptr, char *cptr)
277 {
278 lpt_vfup = 0; /* top of form */
279 return attach_unit (uptr, cptr);
280 }
281
282 /* Carriage control load routine */
283
284 t_stat lp_load (FILE *fileref, char *cptr, char *fnam)
285 {
286 int32 col, ptr, mask, vfubuf[VFU_LNT];
287 uint32 rpt;
288 t_stat r;
289 char cbuf[CBUFSIZE], gbuf[CBUFSIZE];
290
291 if (*cptr != 0) return SCPE_ARG;
292 ptr = 0;
293 for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */
294 mask = 0;
295 if (*cptr == '(') { /* repeat count? */
296 cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */
297 rpt = get_uint (gbuf, 10, VFU_LNT, &r); /* repeat count */
298 if (r != SCPE_OK) return SCPE_FMT;
299 }
300 else rpt = 1;
301 while (*cptr != 0) { /* get col no's */
302 cptr = get_glyph (cptr, gbuf, ','); /* get next field */
303 col = get_uint (gbuf, 10, 7, &r); /* column number */
304 if (r != SCPE_OK) return SCPE_FMT;
305 mask = mask | (1 << col); /* set bit */
306 }
307 for ( ; rpt > 0; rpt--) { /* store vals */
308 if (ptr >= VFU_LNT) return SCPE_FMT;
309 vfubuf[ptr++] = mask;
310 }
311 }
312 if (ptr == 0) return SCPE_FMT;
313 lpt_vful = ptr;
314 lpt_vfup = 0;
315 for (rpt = 0; rpt < lpt_vful; rpt++) lpt_vfut[rpt] = vfubuf[rpt];
316 return SCPE_OK;
317 }