First Commit of my working state
[simh.git] / PDP10 / pdp10_lp20.c
1 /* pdp10_lp20.c: PDP-10 LP20 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 lp20 line printer
27
28 19-Jan-07 RMS Added UNIT_TEXT flag
29 04-Sep-05 RMS Fixed missing return (found by Peter Schorn)
30 07-Jul-05 RMS Removed extraneous externs
31 18-Mar-05 RMS Added attached test to detach routine
32 29-Dec-03 RMS Fixed bug in scheduling
33 25-Apr-03 RMS Revised for extended file support
34 29-Sep-02 RMS Added variable vector support
35 Modified to use common Unibus routines
36 New data structures
37 30-May-02 RMS Widened POS to 32b
38 06-Jan-02 RMS Added enable/disable support
39 30-Nov-01 RMS Added extended SET/SHOW support
40 */
41
42 #include "pdp10_defs.h"
43
44 #define UNIT_DUMMY (1 << UNIT_V_UF)
45 #define LP_WIDTH 132 /* printer width */
46
47 /* DAVFU RAM */
48
49 #define DV_SIZE 143 /* DAVFU size */
50 #define DV_DMASK 077 /* data mask per byte */
51 #define DV_TOF 0 /* top of form channel */
52 #define DV_MAX 11 /* max channel number */
53
54 /* Translation RAM */
55
56 #define TX_SIZE 256 /* translation RAM */
57 #define TX_AMASK (TX_SIZE - 1)
58 #define TX_DMASK 07777
59 #define TX_V_FL 8 /* flags */
60 #define TX_M_FL 017
61 /* define TX_INTR 04000 /* interrupt */
62 #define TX_DELH 02000 /* delimiter */
63 /* define TX_XLAT 01000 /* translate */
64 /* define TX_DVFU 00400 /* DAVFU */
65 #define TX_SLEW 00020 /* chan vs slew */
66 #define TX_VMASK 00017 /* spacing mask */
67 #define TX_CHR 0 /* states: pr char */
68 #define TX_RAM 1 /* pr translation */
69 #define TX_DVU 2 /* DAVFU action */
70 #define TX_INT 3 /* interrupt */
71 #define TX_GETFL(x) (((x) >> TX_V_FL) & TX_M_FL)
72
73 /* LPCSRA (765400) */
74
75 #define CSA_GO 0000001 /* go */
76 #define CSA_PAR 0000002 /* parity enable NI */
77 #define CSA_V_FNC 2 /* function */
78 #define CSA_M_FNC 03
79 #define FNC_PR 0 /* print */
80 #define FNC_TST 1 /* test */
81 #define FNC_DVU 2 /* load DAVFU */
82 #define FNC_RAM 3 /* load translation RAM */
83 #define FNC_INTERNAL 1 /* internal function */
84 #define CSA_FNC (CSA_M_FNC << CSA_V_FNC)
85 #define CSA_V_UAE 4 /* Unibus addr extension */
86 #define CSA_UAE (03 << CSA_V_UAE)
87 #define CSA_IE 0000100 /* interrupt enable */
88 #define CSA_DONE 0000200 /* done */
89 #define CSA_INIT 0000400 /* init */
90 #define CSA_ECLR 0001000 /* clear errors */
91 #define CSA_DELH 0002000 /* delimiter hold */
92 #define CSA_ONL 0004000 /* online */
93 #define CSA_DVON 0010000 /* DAVFU online */
94 #define CSA_UNDF 0020000 /* undefined char */
95 #define CSA_PZRO 0040000 /* page counter zero */
96 #define CSA_ERR 0100000 /* error */
97 #define CSA_RW (CSA_DELH | CSA_IE | CSA_UAE | CSA_FNC | CSA_PAR | CSA_GO)
98 #define CSA_MBZ (CSA_ECLR | CSA_INIT)
99 #define CSA_GETUAE(x) (((x) & CSA_UAE) << (16 - CSA_V_UAE))
100 #define CSA_GETFNC(x) (((x) >> CSA_V_FNC) & CSA_M_FNC)
101
102 /* LPCSRB (765402) */
103
104 #define CSB_GOE 0000001 /* go error */
105 #define CSB_DTE 0000002 /* DEM timing error NI */
106 #define CSB_MTE 0000004 /* MSYN error (Ubus timeout) */
107 #define CSB_RPE 0000010 /* RAM parity error NI */
108 #define CSB_MPE 0000020 /* MEM parity error NI */
109 #define CSB_LPE 0000040 /* LPT parity error NI */
110 #define CSB_DVOF 0000100 /* DAVFU not ready */
111 #define CSB_OFFL 0000200 /* offline */
112 #define CSB_TEST 0003400 /* test mode */
113 #define CSB_OVFU 0004000 /* optical VFU NI */
114 #define CSB_PBIT 0010000 /* data parity bit NI */
115 #define CSB_NRDY 0020000 /* printer error NI */
116 #define CSB_LA180 0040000 /* LA180 printer NI */
117 #define CSB_VLD 0100000 /* valid data NI */
118 #define CSB_ECLR (CSB_GOE | CSB_DTE | CSB_MTE | CSB_RPE | CSB_MPE | CSB_LPE)
119 #define CSB_ERR (CSB_ECLR | CSB_DVOF | CSB_OFFL)
120 #define CSB_RW CSB_TEST
121 #define CSB_MBZ (CSB_DTE | CSB_RPE | CSB_MPE | CSB_LPE | CSB_OVFU |\
122 CSB_PBIT | CSB_NRDY | CSB_LA180 | CSB_VLD)
123
124 /* LPBA (765404) */
125
126 /* LPBC (765506) */
127
128 #define BC_MASK 0007777 /* <15:12> MBZ */
129
130 /* LPPAGC (765510) */
131
132 #define PAGC_MASK 0007777 /* <15:12> MBZ */
133
134 /* LPRDAT (765512) */
135
136 #define RDAT_MASK 0007777 /* <15:12> MBZ */
137
138 /* LPCOLC/LPCBUF (765514) */
139
140 /* LPCSUM/LPPDAT (765516) */
141
142 extern d10 *M; /* main memory */
143 extern int32 int_req;
144
145 int32 lpcsa = 0; /* control/status A */
146 int32 lpcsb = 0; /* control/status B */
147 int32 lpba = 0; /* bus address */
148 int32 lpbc = 0; /* byte count */
149 int32 lppagc = 0; /* page count */
150 int32 lprdat = 0; /* RAM data */
151 int32 lpcbuf = 0; /* character buffer */
152 int32 lpcolc = 0; /* column count */
153 int32 lppdat = 0; /* printer data */
154 int32 lpcsum = 0; /* checksum */
155 int32 dvptr = 0; /* davfu pointer */
156 int32 dvlnt = 0; /* davfu length */
157 int32 lp20_irq = 0; /* int request */
158 int32 lp20_stopioe = 0; /* stop on error */
159 int16 txram[TX_SIZE] = { 0 }; /* translation RAM */
160 int16 davfu[DV_SIZE] = { 0 }; /* DAVFU */
161
162 DEVICE lp20_dev;
163 t_stat lp20_rd (int32 *data, int32 pa, int32 access);
164 t_stat lp20_wr (int32 data, int32 pa, int32 access);
165 int32 lp20_inta (void);
166 t_stat lp20_svc (UNIT *uptr);
167 t_stat lp20_reset (DEVICE *dptr);
168 t_stat lp20_attach (UNIT *uptr, char *ptr);
169 t_stat lp20_detach (UNIT *uptr);
170 t_stat lp20_clear_vfu (UNIT *uptr, int32 val, char *cptr, void *desc);
171 t_bool lp20_print (int32 c);
172 t_bool lp20_adv (int32 c, t_bool advdvu);
173 t_bool lp20_davfu (int32 c);
174 void update_lpcs (int32 flg);
175
176 /* LP data structures
177
178 lp20_dev LPT device descriptor
179 lp20_unit LPT unit descriptor
180 lp20_reg LPT register list
181 */
182
183 DIB lp20_dib = {
184 IOBA_LP20, IOLN_LP20, &lp20_rd, &lp20_wr,
185 1, IVCL (LP20), VEC_LP20, { &lp20_inta }
186 };
187
188 UNIT lp20_unit = {
189 UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT
190 };
191
192 REG lp20_reg[] = {
193 { ORDATA (LPCSA, lpcsa, 16) },
194 { ORDATA (LPCSB, lpcsb, 16) },
195 { ORDATA (LPBA, lpba, 16) },
196 { ORDATA (LPBC, lpbc, 12) },
197 { ORDATA (LPPAGC, lppagc, 12) },
198 { ORDATA (LPRDAT, lprdat, 12) },
199 { ORDATA (LPCBUF, lpcbuf, 8) },
200 { ORDATA (LPCOLC, lpcolc, 8) },
201 { ORDATA (LPPDAT, lppdat, 8) },
202 { ORDATA (LPCSUM, lpcsum, 8) },
203 { ORDATA (DVPTR, dvptr, 7) },
204 { ORDATA (DVLNT, dvlnt, 7), REG_RO + REG_NZ },
205 { FLDATA (INT, int_req, INT_V_LP20) },
206 { FLDATA (IRQ, lp20_irq, 0) },
207 { FLDATA (ERR, lpcsa, CSR_V_ERR) },
208 { FLDATA (DONE, lpcsa, CSR_V_DONE) },
209 { FLDATA (IE, lpcsa, CSR_V_IE) },
210 { DRDATA (POS, lp20_unit.pos, T_ADDR_W), PV_LEFT },
211 { DRDATA (TIME, lp20_unit.wait, 24), PV_LEFT },
212 { FLDATA (STOP_IOE, lp20_stopioe, 0) },
213 { BRDATA (TXRAM, txram, 8, 12, TX_SIZE) },
214 { BRDATA (DAVFU, davfu, 8, 12, DV_SIZE) },
215 { ORDATA (DEVADDR, lp20_dib.ba, 32), REG_HRO },
216 { ORDATA (DEVVEC, lp20_dib.vec, 16), REG_HRO },
217 { NULL }
218 };
219
220 MTAB lp20_mod[] = {
221 { UNIT_DUMMY, 0, NULL, "VFUCLEAR", &lp20_clear_vfu },
222 { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
223 &set_addr, &show_addr, NULL },
224 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
225 &set_vec, &show_vec, NULL },
226 { 0 }
227 };
228
229 DEVICE lp20_dev = {
230 "LP20", &lp20_unit, lp20_reg, lp20_mod,
231 1, 10, 31, 1, 8, 8,
232 NULL, NULL, &lp20_reset,
233 NULL, &lp20_attach, &lp20_detach,
234 &lp20_dib, DEV_DISABLE | DEV_UBUS
235 };
236
237 /* Line printer routines
238
239 lp20_rd I/O page read
240 lp20_wr I/O page write
241 lp20_svc process event (printer ready)
242 lp20_reset process reset
243 lp20_attach process attach
244 lp20_detach process detach
245 */
246
247 t_stat lp20_rd (int32 *data, int32 pa, int32 access)
248 {
249 update_lpcs (0); /* update csr's */
250 switch ((pa >> 1) & 07) { /* case on PA<3:1> */
251
252 case 00: /* LPCSA */
253 *data = lpcsa = lpcsa & ~CSA_MBZ;
254 break;
255
256 case 01: /* LPCSB */
257 *data = lpcsb = lpcsb & ~CSB_MBZ;
258 break;
259
260 case 02: /* LPBA */
261 *data = lpba;
262 break;
263
264 case 03: /* LPBC */
265 *data = lpbc = lpbc & BC_MASK;
266 break;
267
268 case 04: /* LPPAGC */
269 *data = lppagc = lppagc & PAGC_MASK;
270 break;
271
272 case 05: /* LPRDAT */
273 *data = lprdat = lprdat & RDAT_MASK;
274 break;
275
276 case 06: /* LPCOLC/LPCBUF */
277 *data = (lpcolc << 8) | lpcbuf;
278 break;
279
280 case 07: /* LPCSUM/LPPDAT */
281 *data = (lpcsum << 8) | lppdat;
282 break;
283 } /* end case PA */
284
285 return SCPE_OK;
286 }
287
288 t_stat lp20_wr (int32 data, int32 pa, int32 access)
289 {
290 update_lpcs (0); /* update csr's */
291 switch ((pa >> 1) & 07) { /* case on PA<3:1> */
292
293 case 00: /* LPCSA */
294 if (access == WRITEB) data = (pa & 1)?
295 (lpcsa & 0377) | (data << 8): (lpcsa & ~0377) | data;
296 if (data & CSA_ECLR) { /* error clear? */
297 lpcsa = (lpcsa | CSA_DONE) & ~CSA_GO; /* set done, clr go */
298 lpcsb = lpcsb & ~CSB_ECLR; /* clear err */
299 sim_cancel (&lp20_unit); /* cancel I/O */
300 }
301 if (data & CSA_INIT) lp20_reset (&lp20_dev); /* init? */
302 if (data & CSA_GO) { /* go set? */
303 if ((lpcsa & CSA_GO) == 0) { /* not set before? */
304 if (lpcsb & CSB_ERR) lpcsb = lpcsb | CSB_GOE;
305 lpcsum = 0; /* clear checksum */
306 sim_activate (&lp20_unit, lp20_unit.wait);
307 }
308 }
309 else sim_cancel (&lp20_unit); /* go clr, stop DMA */
310 lpcsa = (lpcsa & ~CSA_RW) | (data & CSA_RW);
311 break;
312
313 case 01: /* LPCSB */
314 break; /* ignore writes to TEST */
315
316 case 02: /* LPBA */
317 if (access == WRITEB) data = (pa & 1)?
318 (lpba & 0377) | (data << 8): (lpba & ~0377) | data;
319 lpba = data;
320 break;
321
322 case 03: /* LPBC */
323 if (access == WRITEB) data = (pa & 1)?
324 (lpbc & 0377) | (data << 8): (lpbc & ~0377) | data;
325 lpbc = data & BC_MASK;
326 lpcsa = lpcsa & ~CSA_DONE;
327 break;
328
329 case 04: /* LPPAGC */
330 if (access == WRITEB) data = (pa & 1)?
331 (lppagc & 0377) | (data << 8): (lppagc & ~0377) | data;
332 lppagc = data & PAGC_MASK;
333 break;
334
335 case 05: /* LPRDAT */
336 if (access == WRITEB) data = (pa & 1)?
337 (lprdat & 0377) | (data << 8): (lprdat & ~0377) | data;
338 lprdat = data & RDAT_MASK;
339 txram[lpcbuf & TX_AMASK] = lprdat; /* load RAM */
340 break;
341
342 case 06: /* LPCOLC/LPCBUF */
343 if ((access == WRITEB) && (pa & 1)) /* odd byte */
344 lpcolc = data & 0377;
345 else {
346 lpcbuf = data & 0377; /* even byte, word */
347 if (access == WRITE) lpcolc = (data >> 8) & 0377;
348 }
349 break;
350
351 case 07: /* LPCSUM/LPPDAT */
352 break; /* read only */
353 } /* end case PA */
354
355 update_lpcs (0);
356 return SCPE_OK;
357 }
358
359 /* Line printer service
360
361 The translation RAM case table is derived from the LP20 spec and
362 verified against the LP20 RAM simulator in TOPS10 7.04 LPTSPL.
363 The equations are:
364
365 flags := inter, delim, xlate, paper, delim_hold (from CSRA)
366 actions : = print_input, print_xlate, davfu_action, interrupt
367
368 if (inter) {
369 if (!xlate || delim || delim_hold) interrupt;
370 else if (paper) davfu_action;
371 else print_xlate;
372 }
373 else if (paper) {
374 if (xlate || delim || delim_hold) davfu_action;
375 else print_input;
376 }
377 else {
378 if (xlate || delim || delim_hold) print_xlate;
379 else print_input;
380 }
381 */
382
383 t_stat lp20_svc (UNIT *uptr)
384 {
385 int32 fnc, i, tbc, temp, txst;
386 int32 dvld = -2; /* must be even */
387 uint16 wd10;
388 t_bool cont;
389 a10 ba;
390
391 static const uint32 txcase[32] = {
392 TX_CHR, TX_RAM, TX_CHR, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU,
393 TX_RAM, TX_RAM, TX_DVU, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU,
394 TX_INT, TX_INT, TX_INT, TX_INT, TX_RAM, TX_INT, TX_DVU, TX_INT,
395 TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT
396 };
397
398 lpcsa = lpcsa & ~CSA_GO;
399 ba = CSA_GETUAE (lpcsa) | lpba;
400 fnc = CSA_GETFNC (lpcsa);
401 tbc = 010000 - lpbc;
402 if (((fnc & FNC_INTERNAL) == 0) && ((lp20_unit.flags & UNIT_ATT) == 0)) {
403 update_lpcs (CSA_ERR);
404 return IORETURN (lp20_stopioe, SCPE_UNATT);
405 }
406 if ((fnc == FNC_PR) && (dvlnt == 0)) {
407 update_lpcs (CSA_ERR);
408 return SCPE_OK;
409 }
410
411 for (i = 0, cont = TRUE; (i < tbc) && cont; ba++, i++) {
412 if (Map_ReadW (ba, 2, &wd10)) { /* get word, err? */
413 lpcsb = lpcsb | CSB_MTE; /* set NXM error */
414 update_lpcs (CSA_ERR); /* set done */
415 break;
416 }
417 lpcbuf = (wd10 >> ((ba & 1)? 8: 0)) & 0377; /* get character */
418 lpcsum = (lpcsum + lpcbuf) & 0377; /* add into checksum */
419 switch (fnc) { /* switch on function */
420
421 /* Translation RAM load */
422
423 case FNC_RAM: /* RAM load */
424 txram[(i >> 1) & TX_AMASK] = wd10 & TX_DMASK;
425 break;
426
427 /* DAVFU RAM load. The DAVFU RAM is actually loaded in bytes, delimited by
428 a start (354 to 356) and stop (357) byte pair. If the number of bytes
429 loaded is odd, or no bytes are loaded, the DAVFU is invalid.
430 */
431
432 case FNC_DVU: /* DVU load */
433 if ((lpcbuf >= 0354) && (lpcbuf <= 0356)) /* start DVU load? */
434 dvld = dvlnt = 0; /* reset lnt */
435 else if (lpcbuf == 0357) { /* stop DVU load? */
436 dvptr = 0; /* reset ptr */
437 if (dvld & 1) dvlnt = 0; /* if odd, invalid */
438 }
439 else if (dvld == 0) { /* even state? */
440 temp = lpcbuf & DV_DMASK;
441 dvld = 1;
442 }
443 else if (dvld == 1) { /* odd state? */
444 if (dvlnt < DV_SIZE) davfu[dvlnt++] =
445 temp | ((lpcbuf & DV_DMASK) << 6);
446 dvld = 0;
447 }
448 break;
449
450 /* Print characters */
451
452 case FNC_PR: /* print */
453 lprdat = txram[lpcbuf]; /* get RAM char */
454 txst = (TX_GETFL (lprdat) << 1) | /* get state */
455 ((lpcsa & CSA_DELH)? 1: 0); /* plus delim hold */
456 if (lprdat & TX_DELH) lpcsa = lpcsa | CSA_DELH;
457 else lpcsa = lpcsa & ~CSA_DELH;
458 lpcsa = lpcsa & ~CSA_UNDF; /* assume char ok */
459 switch (txcase[txst]) { /* case on state */
460
461 case TX_CHR: /* take char */
462 cont = lp20_print (lpcbuf);
463 break;
464
465 case TX_RAM: /* take translation */
466 cont = lp20_print (lprdat);
467 break;
468
469 case TX_DVU: /* DAVFU action */
470 if (lprdat & TX_SLEW)
471 cont = lp20_adv (lprdat & TX_VMASK, TRUE);
472 else cont = lp20_davfu (lprdat & TX_VMASK);
473 break;
474
475 case TX_INT: /* interrupt */
476 lpcsa = lpcsa | CSA_UNDF; /* set flag */
477 cont = FALSE; /* force stop */
478 break;
479 } /* end case char state */
480 break;
481
482 case FNC_TST: /* test */
483 break;
484 } /* end case function */
485 } /* end for */
486 lpba = ba & 0177777;
487 lpcsa = (lpcsa & ~CSA_UAE) | ((ba >> (16 - CSA_V_UAE)) & CSA_UAE);
488 lpbc = (lpbc + i) & BC_MASK;
489 if (lpbc) update_lpcs (CSA_MBZ); /* intr, but not done */
490 else update_lpcs (CSA_DONE); /* intr and done */
491 if ((fnc == FNC_PR) && ferror (lp20_unit.fileref)) {
492 perror ("LP I/O error");
493 clearerr (uptr->fileref);
494 return SCPE_IOERR;
495 }
496 return SCPE_OK;
497 }
498
499 /* Print routines
500
501 lp20_print print a character
502 lp20_adv advance n lines
503 lp20_davfu advance to channel on VFU
504
505 Return TRUE to continue printing, FALSE to stop
506 */
507
508 t_bool lp20_print (int32 c)
509 {
510 t_bool r = TRUE;
511 int32 i, rpt = 1;
512
513 lppdat = c & 0177; /* mask char to 7b */
514 if (lppdat == 000) return TRUE; /* NUL? no op */
515 if (lppdat == 012) return lp20_adv (1, TRUE); /* LF? adv carriage */
516 if (lppdat == 014) return lp20_davfu (DV_TOF); /* FF? top of form */
517 if (lppdat == 015) lpcolc = 0; /* CR? reset col cntr */
518 else if (lppdat == 011) { /* TAB? simulate */
519 lppdat = ' '; /* with spaces */
520 if (lpcolc >= 128) {
521 r = lp20_adv (1, TRUE); /* eol? adv carriage */
522 rpt = 8; /* adv to col 9 */
523 }
524 else rpt = 8 - (lpcolc & 07); /* else adv 1 to 8 */
525 }
526 else {
527 if (lppdat < 040) lppdat = ' '; /* cvt non-prnt to spc */
528 if (lpcolc >= LP_WIDTH) /* line full? */
529 r = lp20_adv (1, TRUE); /* adv carriage */
530 }
531 for (i = 0; i < rpt; i++)
532 fputc (lppdat, lp20_unit.fileref);
533 lp20_unit.pos = ftell (lp20_unit.fileref);
534 lpcolc = lpcolc + rpt;
535 return r;
536 }
537
538 t_bool lp20_adv (int32 cnt, t_bool dvuadv)
539 {
540 int32 i;
541
542 if (cnt == 0) return TRUE;
543 lpcolc = 0; /* reset col cntr */
544 for (i = 0; i < cnt; i++)
545 fputc ('\n', lp20_unit.fileref);
546 lp20_unit.pos = ftell (lp20_unit.fileref); /* print 'n' newlines */
547 if (dvuadv) dvptr = (dvptr + cnt) % dvlnt; /* update DAVFU ptr */
548 if (davfu[dvptr] & (1 << DV_TOF)) { /* at top of form? */
549 if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */
550 lpcsa = lpcsa & ~CSA_PZRO; /* update status */
551 return TRUE;
552 }
553 else {
554 lpcsa = lpcsa | CSA_PZRO; /* stop if zero */
555 return FALSE;
556 }
557 }
558 return TRUE;
559 }
560
561 t_bool lp20_davfu (int32 cnt)
562 {
563 int i;
564
565 if (cnt > DV_MAX) cnt = 7; /* inval chan? */
566 for (i = 0; i < dvlnt; i++) { /* search DAVFU */
567 dvptr = dvptr + 1; /* adv DAVFU ptr */
568 if (dvptr >= dvlnt) dvptr = 0; /* wrap at end */
569 if (davfu[dvptr] & (1 << cnt)) { /* channel stop set? */
570 if (cnt) return lp20_adv (i + 1, FALSE); /* ~TOF, adv */
571 if (lpcolc) lp20_adv (1, FALSE); /* TOF, need newline? */
572 fputc ('\f', lp20_unit.fileref); /* print form feed */
573 lp20_unit.pos = ftell (lp20_unit.fileref);
574 if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */
575 lpcsa = lpcsa & ~CSA_PZRO; /* update status */
576 return TRUE;
577 }
578 else {
579 lpcsa = lpcsa | CSA_PZRO; /* stop if zero */
580 return FALSE;
581 }
582 }
583 } /* end for */
584 dvlnt = 0; /* DAVFU error */
585 return FALSE;
586 }
587
588 /* Update LPCSA, optionally request interrupt */
589
590 void update_lpcs (int32 flg)
591 {
592 if (flg) lp20_irq = 1; /* set int req */
593 lpcsa = (lpcsa | flg) & ~(CSA_MBZ | CSA_ERR | CSA_ONL | CSA_DVON);
594 lpcsb = (lpcsb | CSB_OFFL | CSB_DVOF) & ~CSB_MBZ;
595 if (lp20_unit.flags & UNIT_ATT) {
596 lpcsa = lpcsa | CSA_ONL;
597 lpcsb = lpcsb & ~CSB_OFFL;
598 }
599 else lpcsa = lpcsa & ~CSA_DONE;
600 if (dvlnt) {
601 lpcsa = lpcsa | CSA_DVON;
602 lpcsb = lpcsb & ~CSB_DVOF;
603 }
604 if (lpcsb & CSB_ERR) lpcsa = lpcsa | CSA_ERR;
605 if ((lpcsa & CSA_IE) && lp20_irq) int_req = int_req | INT_LP20;
606 else int_req = int_req & ~INT_LP20;
607 return;
608 }
609
610 /* Acknowledge interrupt (clear internal request) */
611
612 int32 lp20_inta (void)
613 {
614 lp20_irq = 0; /* clear int req */
615 return lp20_dib.vec;
616 }
617
618 t_stat lp20_reset (DEVICE *dptr)
619 {
620 lpcsa = CSA_DONE;
621 lpcsb = 0;
622 lpba = lpbc = lppagc = lpcolc = 0; /* clear registers */
623 lprdat = lppdat = lpcbuf = lpcsum = 0;
624 lp20_irq = 0; /* clear int req */
625 dvptr = 0; /* reset davfu ptr */
626 sim_cancel (&lp20_unit); /* deactivate unit */
627 update_lpcs (0); /* update status */
628 return SCPE_OK;
629 }
630
631 t_stat lp20_attach (UNIT *uptr, char *cptr)
632 {
633 t_stat reason;
634
635 reason = attach_unit (uptr, cptr); /* attach file */
636 if (lpcsa & CSA_ONL) return reason; /* just file chg? */
637 if (sim_is_active (&lp20_unit)) update_lpcs (0); /* busy? no int */
638 else update_lpcs (CSA_MBZ); /* interrupt */
639 return reason;
640 }
641
642 t_stat lp20_detach (UNIT *uptr)
643 {
644 t_stat reason;
645
646 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */
647 reason = detach_unit (uptr);
648 sim_cancel (&lp20_unit);
649 lpcsa = lpcsa & ~CSA_GO;
650 update_lpcs (CSA_MBZ);
651 return reason;
652 }
653
654 t_stat lp20_clear_vfu (UNIT *uptr, int32 val, char *cptr, void *desc)
655 {
656 int i;
657
658 if (!get_yn ("Clear DAVFU? [N]", FALSE)) return SCPE_OK;
659 for (i = 0; i < DV_SIZE; i++) davfu[i] = 0;
660 dvlnt = dvptr = 0;
661 update_lpcs (0);
662 return SCPE_OK;
663 }