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