1 /* h316_lp.c: Honeywell 316/516 line printer
3 Copyright (c) 1999-2007, Robert M. Supnik
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
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.
28 09-Jun-07 RMS Fixed lost last print line (from Theo Engel)
29 19-Jan-06 RMS Added UNIT_TEXT flag
30 03-Apr-06 RMS Fixed bug in blanks backscanning (from Theo Engel)
31 01-Dec-04 RMS Fixed bug in DMA/DMC support
32 24-Oct-03 RMS Added DMA/DMC support
33 25-Apr-03 RMS Revised for extended file support
34 30-May-02 RMS Widened POS to 32b
36 The Series 16 line printer is an unbuffered Analex shuttle printer.
37 Because it was unbuffered, the CPU had to scan out an entire line's
38 worth of characters (60 words) for every character on the print drum
39 (64 characters). Because it was a shuttle printer, the entire
40 process must be repeated first for the odd columns and then for the
41 even columns. After scanning the odd columns, the printer carriage
42 shuttled right by one column; after scanning the even columns, the
43 carriage shuttled left. This halved the number of hammers required,
44 reducing cost but increasing mechanical complexity.
46 The real printer is very timing dependent. If the CPU misses a
47 scan, then the wrong characters are printed. If the printer protocol
48 is violated, then results are unpredictable. The simulated printer
49 is much more forgiving. Rather than simulating the fixed drum and
50 hammer timing of the real printer, the simulator is driven by the
51 program's OTA instructions. If the program misses a time slot, the
52 simulator will still print the "correct" result. A timing based
53 simulation would be very hard to do in the absense of accurate
56 Printer state is maintained in a set of position and state variables:
58 lpt_wdpos word count within a line scan (0-59)
59 lpt_drpos drum position (0-63)
60 lpt_crpos carriage position (0-1)
61 lpt_svcst service state (shuttle, paper advance)
62 lpt_svcch channel for paper advance (0 = no adv)
63 lpt_rdy transfer ready flag
64 lpt_prdn printing done flag
66 lpt_eor DMA/DMC end of range
69 #include "h316_defs.h"
71 #define LPT_WIDTH 120 /* width */
72 #define LPT_SCAN (LPT_WIDTH / 2) /* words/scan */
73 #define LPT_DRUM 64 /* drum rows */
74 #define LPT_SVCSH 01 /* shuttle */
75 #define LPT_SVCPA 02 /* paper advance */
77 extern int32 dev_int
, dev_enb
;
78 extern int32 stop_inst
;
79 extern uint32 chan_req
;
81 int32 lpt_wdpos
= 0; /* word position */
82 int32 lpt_drpos
= 0; /* drum position */
83 int32 lpt_crpos
= 0; /* carriage position */
84 int32 lpt_svcst
= 0; /* service state */
85 int32 lpt_svcch
= 0; /* service channel */
86 int32 lpt_rdy
= 0; /* transfer flag */
87 int32 lpt_prdn
= 1; /* printing done */
88 int32 lpt_dma
= 0; /* use DMA/DMC */
89 int32 lpt_eor
= 0; /* DMA/DMC end range */
90 char lpt_buf
[LPT_WIDTH
+ 1] = { 0 }; /* line buffer */
91 int32 lpt_xtime
= 5; /* transfer time */
92 int32 lpt_etime
= 50; /* end of scan time */
93 int32 lpt_ptime
= 5000; /* paper adv time */
94 int32 lpt_stopioe
= 0; /* stop on error */
96 int32
lptio (int32 inst
, int32 fnc
, int32 dat
, int32 dev
);
97 t_stat
lpt_svc (UNIT
*uptr
);
98 t_stat
lpt_reset (DEVICE
*dptr
);
100 /* LPT data structures
102 lpt_dev LPT device descriptor
103 lpt_unit LPT unit descriptor
104 lpt_mod LPT modifiers
105 lpt_reg LPT register list
108 DIB lpt_dib
= { LPT
, IOBUS
, 1, &lptio
};
110 UNIT lpt_unit
= { UDATA (&lpt_svc
, UNIT_SEQ
+UNIT_ATTABLE
+UNIT_TEXT
, 0) };
113 { DRDATA (WDPOS
, lpt_wdpos
, 6) },
114 { DRDATA (DRPOS
, lpt_drpos
, 6) },
115 { FLDATA (CRPOS
, lpt_crpos
, 0) },
116 { FLDATA (RDY
, lpt_rdy
, 0) },
117 { FLDATA (EOR
, lpt_eor
, 0) },
118 { FLDATA (DMA
, lpt_dma
, 0) },
119 { FLDATA (PRDN
, lpt_prdn
, 0) },
120 { FLDATA (INTREQ
, dev_int
, INT_V_LPT
) },
121 { FLDATA (ENABLE
, dev_enb
, INT_V_LPT
) },
122 { ORDATA (SVCST
, lpt_svcst
, 2) },
123 { ORDATA (SVCCH
, lpt_svcch
, 2) },
124 { BRDATA (BUF
, lpt_buf
, 8, 8, 120) },
125 { DRDATA (POS
, lpt_unit
.pos
, T_ADDR_W
), PV_LEFT
},
126 { DRDATA (XTIME
, lpt_xtime
, 24), PV_LEFT
},
127 { DRDATA (ETIME
, lpt_etime
, 24), PV_LEFT
},
128 { DRDATA (PTIME
, lpt_ptime
, 24), PV_LEFT
},
129 { FLDATA (STOP_IOE
, lpt_stopioe
, 0) },
134 "LPT", &lpt_unit
, lpt_reg
, NULL
,
136 NULL
, NULL
, &lpt_reset
,
138 &lpt_dib
, DEV_DISABLE
143 int32
lptio (int32 inst
, int32 fnc
, int32 dat
, int32 dev
)
145 int32 ch
= lpt_dib
.chan
- 1; /* DMA/DMC chan */
148 switch (inst
) { /* case on opcode */
150 case ioOCP
: /* OCP */
151 switch (fnc
) { /* case on fnc */
153 case 000: case 002: case 004: /* paper adv */
154 lpt_svcst
= lpt_svcst
| LPT_SVCPA
; /* set state */
155 lpt_svcch
= fnc
>> 1; /* save channel */
156 sim_activate (&lpt_unit
, lpt_ptime
);
157 CLR_INT (INT_LPT
); /* clear int */
160 case 003: /* init scan DMA/DMC */
161 lpt_prdn
= 0; /* clear pr done */
162 lpt_wdpos
= 0; /* init scan pos */
164 if (ch
>= 0) lpt_dma
= 1; /* try for DMA/DMC */
166 if (!sim_is_active (&lpt_unit
)) {
168 if (lpt_dma
) SET_CH_REQ (ch
);
170 CLR_INT (INT_LPT
); /* clear int */
173 case 007: /* init scan IO bus */
174 lpt_prdn
= 0; /* clear pr done */
175 lpt_wdpos
= 0; /* init scan pos */
177 lpt_dma
= 0; /* IO bus */
178 if (!sim_is_active (&lpt_unit
)) lpt_rdy
= 1;
179 CLR_INT (INT_LPT
); /* clear int */
183 return IOBADFNC (dat
);
187 case ioSKS
: /* SKS */
188 switch (fnc
) { /* case on fnc */
190 case 000: /* if xfer rdy */
191 if (lpt_rdy
) return IOSKIP (dat
);
194 case 002: /* if !alarm */
195 if (lpt_unit
.flags
& UNIT_ATT
) return IOSKIP (dat
);
198 case 003: /* if odd col */
199 if (lpt_crpos
) return IOSKIP (dat
);
202 case 004: /* if !interrupt */
203 if (!TST_INTREQ (INT_LPT
)) return IOSKIP (dat
);
206 case 011: /* if line printed */
207 if (lpt_prdn
) return IOSKIP (dat
);
210 case 012: /* if !shuttling */
211 if (!(lpt_svcst
& LPT_SVCSH
)) return IOSKIP (dat
);
215 if (lpt_prdn
&& !(lpt_svcst
& LPT_SVCSH
)) return IOSKIP (dat
);
218 case 014: /* if !advancing */
219 if (!(lpt_svcst
& LPT_SVCPA
)) return IOSKIP (dat
);
223 if (lpt_prdn
&& !(lpt_svcst
& LPT_SVCPA
)) return IOSKIP (dat
);
227 if (!(lpt_svcst
& (LPT_SVCSH
| LPT_SVCPA
))) return IOSKIP (dat
);
231 if (lpt_prdn
&& !(lpt_svcst
& (LPT_SVCSH
| LPT_SVCPA
)))
236 return IOBADFNC (dat
);
240 case ioOTA
: /* OTA */
241 if (fnc
) return IOBADFNC (dat
); /* only fnc 0 */
242 if (lpt_rdy
) { /* xfer ready? */
243 lpt_rdy
= 0; /* clear xfer */
244 chr
= (dat
>> (lpt_crpos
? 0: 8)) & 077; /* get 6b char */
245 if (chr
== lpt_drpos
) { /* match drum pos? */
246 if (chr
< 040) chr
= chr
| 0100;
247 lpt_buf
[2 * lpt_wdpos
+ lpt_crpos
] = chr
;
249 lpt_wdpos
++; /* adv scan pos */
250 if (lpt_wdpos
>= LPT_SCAN
) { /* end of scan? */
251 lpt_wdpos
= 0; /* reset scan pos */
252 lpt_drpos
++; /* adv drum pos */
253 if (lpt_drpos
>= LPT_DRUM
) { /* end of drum? */
254 lpt_drpos
= 0; /* reset drum pos */
255 lpt_crpos
= lpt_crpos
^ 1; /* shuttle */
256 lpt_svcst
= lpt_svcst
| LPT_SVCSH
;
257 sim_activate (&lpt_unit
, lpt_ptime
);
258 } /* end if shuttle */
259 else sim_activate (&lpt_unit
, lpt_etime
);
260 } /* end if endscan */
261 else sim_activate (&lpt_unit
, lpt_xtime
);
262 return IOSKIP (dat
); /* skip return */
266 case ioEND
: /* end DMA/DMC */
267 lpt_eor
= 1; /* set end range */
276 t_stat
lpt_svc (UNIT
*uptr
)
279 int32 ch
= lpt_dib
.chan
- 1; /* DMA/DMC chan */
280 static const char *lpt_cc
[] = {
287 if ((lpt_unit
.flags
& UNIT_ATT
) == 0) /* attached? */
288 return IORETURN (lpt_stopioe
, SCPE_UNATT
);
289 if (lpt_dma
) { /* DMA/DMC? */
290 if (lpt_eor
) SET_INT (INT_LPT
); /* end range? intr */
292 lpt_rdy
= 1; /* set ready */
293 SET_CH_REQ (ch
); /* get more data */
296 else lpt_rdy
= 1; /* IO, continue scan */
297 if (lpt_dma
&& lpt_eor
) SET_INT (INT_LPT
); /* end of range? */
298 if (lpt_svcst
& LPT_SVCSH
) { /* shuttling? */
299 SET_INT (INT_LPT
); /* interrupt */
300 if (lpt_crpos
== 0) { /* done shuttling? */
301 for (i
= LPT_WIDTH
- 1; i
>= 0; i
--) { /* backscan for blanks */
302 if (lpt_buf
[i
] != ' ') break;
305 fputs (lpt_buf
, uptr
->fileref
); /* output buf */
306 uptr
->pos
= ftell (uptr
->fileref
); /* update pos */
307 for (i
= 0; i
< LPT_WIDTH
; i
++) lpt_buf
[i
] = ' '; /* clear buf */
308 lpt_prdn
= 1; /* print done */
311 if (lpt_svcst
& LPT_SVCPA
) { /* paper advance */
312 SET_INT (INT_LPT
); /* interrupt */
313 fputs (lpt_cc
[lpt_svcch
& 03], uptr
->fileref
); /* output eol */
314 uptr
->pos
= ftell (uptr
->fileref
); /* update pos */
322 t_stat
lpt_reset (DEVICE
*dptr
)
326 lpt_wdpos
= lpt_drpos
= lpt_crpos
= 0; /* clear positions */
327 lpt_svcst
= lpt_svcch
= 0; /* idle state */
328 lpt_rdy
= 0; /* not rdy to xfer */
329 lpt_prdn
= 1; /* printing done */
332 for (i
= 0; i
< LPT_WIDTH
; i
++) lpt_buf
[i
] = ' '; /* clear buffer */
333 lpt_buf
[LPT_WIDTH
] = 0;
334 CLR_INT (INT_LPT
); /* clear int, enb */
336 sim_cancel (&lpt_unit
); /* deactivate unit */