First Commit of my working state
[simh.git] / SDS / sds_lp.c
1 /* sds_lp.c: SDS 940 line printer simulator
2
3 Copyright (c) 2001-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 line printer
27
28 19-Jan-07 RMS Added UNIT_TEXT flag
29 25-Apr-03 RMS Revised for extended file support
30 */
31
32 #include "sds_defs.h"
33
34 #define LPT_V_LN 9
35 #define LPT_M_LN 07
36 #define LPT_GETLN(x) (((x) >> LPT_V_LN) & LPT_M_LN)
37 #define CHP(ch,val) ((val) & (1 << (ch))) /* CCL chan test */
38 #define SET_XFR 1 /* set xfr */
39 #define SET_EOR 2 /* print, set eor */
40 #define SET_SPC 4 /* space */
41
42 extern char sds_to_ascii[64];
43 extern uint32 xfr_req;
44 extern int32 stop_invins, stop_invdev, stop_inviop;
45 int32 lpt_spc = 0; /* space instr */
46 int32 lpt_sta = 0; /* timeout state */
47 int32 lpt_bptr = 0; /* line buf ptr */
48 int32 lpt_err = 0; /* error */
49 int32 lpt_ccl = 1, lpt_ccp = 0; /* cctl lnt, ptr */
50 int32 lpt_ctime = 10; /* char time */
51 int32 lpt_ptime = 1000; /* print time */
52 int32 lpt_stime = 10000; /* space time */
53 int32 lpt_stopioe = 1; /* stop on err */
54 char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */
55 uint8 lpt_cct[CCT_LNT] = { 0377 }; /* car ctl tape */
56 DSPT lpt_tplt[] = { /* template */
57 { 1, 0 },
58 { 0, 0 }
59 };
60
61 DEVICE lpt_dev;
62 t_stat lpt_svc (UNIT *uptr);
63 t_stat lpt_reset (DEVICE *dptr);
64 t_stat lpt_attach (UNIT *uptr, char *cptr);
65 t_stat lpt_crctl (int32 ch);
66 t_stat lpt_status (UNIT *uptr);
67 t_stat lpt_bufout (UNIT *uptr);
68 void lpt_end_op (int32 fl);
69 t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat);
70
71 /* LPT data structures
72
73 lpt_dev LPT device descriptor
74 lpt_unit LPT unit descriptor
75 lpt_reg LPT register list
76 */
77
78 DIB lpt_dib = { CHAN_W, DEV_LPT, XFR_LPT, lpt_tplt, &lpt };
79
80 UNIT lpt_unit = {
81 UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0)
82 };
83
84 REG lpt_reg[] = {
85 { BRDATA (BUF, lpt_buf, 8, 8, LPT_WIDTH) },
86 { DRDATA (BPTR, lpt_bptr, 8), PV_LEFT },
87 { FLDATA (XFR, xfr_req, XFR_V_LPT) },
88 { FLDATA (ERR, lpt_err, 0) },
89 { ORDATA (STA, lpt_sta, 3) },
90 { BRDATA (CCT, lpt_cct, 8, 8, CCT_LNT) },
91 { DRDATA (CCTP, lpt_ccp, 8), PV_LEFT },
92 { DRDATA (CCTL, lpt_ccl, 8), REG_RO + PV_LEFT },
93 { ORDATA (SPCINST, lpt_spc, 24) },
94 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },
95 { DRDATA (CTIME, lpt_ctime, 24), REG_NZ + PV_LEFT },
96 { DRDATA (PTIME, lpt_ptime, 24), REG_NZ + PV_LEFT },
97 { DRDATA (STIME, lpt_stime, 24), REG_NZ + PV_LEFT },
98 { FLDATA (STOP_IOE, lpt_stopioe, 0) },
99 { NULL }
100 };
101
102 MTAB lpt_mod[] = {
103 { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
104 &set_chan, &show_chan, NULL },
105 { 0 }
106 };
107
108 DEVICE lpt_dev = {
109 "LPT", &lpt_unit, lpt_reg, lpt_mod,
110 1, 10, 31, 1, 8, 7,
111 NULL, NULL, &lpt_reset,
112 NULL, &lpt_attach, NULL,
113 &lpt_dib, DEV_DISABLE
114 };
115
116 /* Line printer routine
117
118 conn - inst = EOM0, dat = NULL
119 eom1 - inst = EOM1, dat = NULL
120 sks - inst = SKS, dat = ptr to result
121 disc - inst = device number, dat = NULL
122 wreor - inst = device number, dat = NULL
123 read - inst = device number, dat = ptr to data
124 write - inst = device number, dat = ptr to result
125
126 The line printer is an asynchronous output device, that is, it
127 can never set the channel rate error flag.
128 */
129
130 t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat)
131 {
132 int32 i, t, new_ch;
133 char asc;
134
135 switch (fnc) { /* case function */
136
137 case IO_CONN: /* connect */
138 new_ch = I_GETEOCH (inst); /* get new chan */
139 if (new_ch != lpt_dib.chan) return SCPE_IERR; /* wrong chan? */
140 for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = 0; /* clr buffer */
141 lpt_bptr = 0; /* clr buf ptr */
142 lpt_err = 0; /* err = 0 */
143 xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */
144 lpt_sta = lpt_sta | SET_XFR; /* need xfr */
145 sim_activate (&lpt_unit, lpt_ctime); /* start timer */
146 break;
147
148 case IO_EOM1: /* EOM mode 1 */
149 new_ch = I_GETEOCH (inst); /* get new chan */
150 if (new_ch != lpt_dib.chan) CRETIOP; /* wrong chan? */
151 if (inst & 0400) { /* space? */
152 lpt_spc = inst; /* save instr */
153 lpt_sta = lpt_sta | SET_SPC; /* need space */
154 sim_cancel (&lpt_unit); /* cancel timer */
155 sim_activate (&lpt_unit, lpt_stime); /* start timer */
156 }
157 break;
158
159 case IO_DISC: /* disconnect */
160 lpt_end_op (0); /* normal term */
161 return lpt_bufout (&lpt_unit); /* dump output */
162
163 case IO_WREOR: /* write eor */
164 lpt_sta = (lpt_sta | SET_EOR) & ~SET_XFR; /* need eor */
165 sim_activate (&lpt_unit, lpt_ptime); /* start timer */
166 break;
167
168 case IO_SKS: /* SKS */
169 new_ch = I_GETSKCH (inst); /* sks chan */
170 if (new_ch != lpt_dib.chan) return SCPE_IERR; /* wrong chan? */
171 t = I_GETSKCND (inst); /* sks cond */
172 if (((t == 020) && (!CHP (7, lpt_cct[lpt_ccp]))) || /* 14062: !ch 7 */
173 ((t == 010) && (lpt_unit.flags & UNIT_ATT)) || /* 12062: !online */
174 (t == 004) && !lpt_err) *dat = 1; /* 11062: !err */
175 break;
176
177 case IO_WRITE: /* write */
178 asc = sds_to_ascii[(*dat) & 077]; /* convert data */
179 xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */
180 if (lpt_bptr < LPT_WIDTH) lpt_buf[lpt_bptr++] = asc;/* store data */
181 lpt_sta = lpt_sta | SET_XFR; /* need xfr */
182 sim_activate (&lpt_unit, lpt_ctime); /* start ch timer */
183 break;
184
185 default:
186 CRETINS;
187 }
188
189 return SCPE_OK;
190 }
191
192 /* Unit service and write */
193
194 t_stat lpt_svc (UNIT *uptr)
195 {
196 t_stat r = SCPE_OK;
197 static const char *lpt_stabl[] = {
198 "\r", "\n", "\n\n", "\n\n\n",
199 "\n\n\n\n", "\n\n\n\n\n",
200 "\n\n\n\n\n\n", "\n\n\n\n\n\n\n"
201 };
202
203 if (lpt_sta & SET_XFR) chan_set_ordy (lpt_dib.chan); /* need lpt xfr? */
204 if (lpt_sta & SET_EOR) { /* printing? */
205 chan_set_flag (lpt_dib.chan, CHF_EOR); /* set eor flg */
206 r = lpt_bufout (uptr); /* output buf */
207 }
208 if (lpt_sta & SET_SPC) { /* spacing? */
209 if (uptr->flags & UNIT_ATT) { /* attached? */
210 int32 ln = LPT_GETLN (lpt_spc); /* get lines, ch */
211 if (lpt_spc & 0200) /* n lines? */
212 fputs (lpt_stabl[ln], uptr->fileref); /* upspace */
213 else lpt_crctl (ln); /* carriage ctl */
214 }
215 r = lpt_status (uptr); /* update status */
216 }
217 lpt_sta = 0; /* clear state */
218 return r;
219 }
220
221 /* Trim and output buffer */
222
223 t_stat lpt_bufout (UNIT *uptr)
224 {
225 int32 i;
226
227 if ((uptr->flags & UNIT_ATT) && lpt_bptr) { /* attached? */
228 for (i = LPT_WIDTH - 1; (i >= 0) && (lpt_buf[i] == ' '); i--)
229 lpt_buf[i] = 0; /* trim line */
230 fputs (lpt_buf, uptr->fileref); /* write line */
231 lpt_bptr = 0;
232 }
233 return lpt_status (uptr); /* return status */
234 }
235
236 /* Status update after I/O */
237
238 t_stat lpt_status (UNIT *uptr)
239 {
240 if (uptr->flags & UNIT_ATT) { /* attached? */
241 uptr->pos = ftell (uptr->fileref); /* update position */
242 if (ferror (uptr->fileref)) { /* I/O error? */
243 lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */
244 perror ("LPT I/O error"); /* print msg */
245 clearerr (uptr->fileref);
246 return SCPE_IOERR; /* ret error */
247 }
248 }
249 else {
250 lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */
251 CRETIOE (lpt_stopioe, SCPE_UNATT); /* ret error */
252 }
253 return SCPE_OK;
254 }
255
256 /* Terminate LPT operation */
257
258 void lpt_end_op (int32 fl)
259 {
260 if (fl) chan_set_flag (lpt_dib.chan, fl); /* set flags */
261 xfr_req = xfr_req & ~XFR_LPT; /* clear xfr */
262 sim_cancel (&lpt_unit); /* stop */
263 if (fl & CHF_ERR) { /* error? */
264 chan_disc (lpt_dib.chan); /* disconnect */
265 lpt_err = 1; /* set lpt err */
266 }
267 return;
268 }
269
270 /* Carriage control */
271
272 t_stat lpt_crctl (int32 ch)
273 {
274 int32 i, j;
275
276 if ((ch == 1) && CHP (ch, lpt_cct[0])) { /* top of form? */
277 fputs ("\f\n", lpt_unit.fileref); /* ff + nl */
278 lpt_ccp = 0; /* top of page */
279 return SCPE_OK;
280 }
281 for (i = 1; i < lpt_ccl + 1; i++) { /* sweep thru cct */
282 lpt_ccp = (lpt_ccp + 1) %lpt_ccl; /* adv pointer */
283 if (CHP (ch, lpt_cct[lpt_ccp])) { /* chan punched? */
284 for (j = 0; j < i; j++) fputc ('\n', lpt_unit.fileref);
285 return SCPE_OK;
286 }
287 }
288 return STOP_CCT; /* runaway channel */
289 }
290
291 /* Reset routine */
292
293 t_stat lpt_reset (DEVICE *dptr)
294 {
295 chan_disc (lpt_dib.chan); /* disconnect */
296 lpt_spc = 0; /* clr state */
297 lpt_sta = 0;
298 xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */
299 sim_cancel (&lpt_unit); /* deactivate */
300 return SCPE_OK;
301 }
302
303 /* Attach routine */
304
305 t_stat lpt_attach (UNIT *uptr, char *cptr)
306 {
307 lpt_ccp = 0; /* top of form */
308 return attach_unit (uptr, cptr);
309 }