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