First Commit of my working state
[simh.git] / H316 / h316_lp.c
1 /* h316_lp.c: Honeywell 316/516 line printer
2
3 Copyright (c) 1999-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 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
35
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.
45
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
54 instruction timing.
55
56 Printer state is maintained in a set of position and state variables:
57
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
65 lpt_dma use DMA/DMC
66 lpt_eor DMA/DMC end of range
67 */
68
69 #include "h316_defs.h"
70
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 */
76
77 extern int32 dev_int, dev_enb;
78 extern int32 stop_inst;
79 extern uint32 chan_req;
80
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 */
95
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);
99
100 /* LPT data structures
101
102 lpt_dev LPT device descriptor
103 lpt_unit LPT unit descriptor
104 lpt_mod LPT modifiers
105 lpt_reg LPT register list
106 */
107
108 DIB lpt_dib = { LPT, IOBUS, 1, &lptio };
109
110 UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) };
111
112 REG lpt_reg[] = {
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) },
130 { NULL }
131 };
132
133 DEVICE lpt_dev = {
134 "LPT", &lpt_unit, lpt_reg, NULL,
135 1, 10, 31, 1, 8, 8,
136 NULL, NULL, &lpt_reset,
137 NULL, NULL, NULL,
138 &lpt_dib, DEV_DISABLE
139 };
140
141 /* IO routine */
142
143 int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev)
144 {
145 int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */
146 int32 chr;
147
148 switch (inst) { /* case on opcode */
149
150 case ioOCP: /* OCP */
151 switch (fnc) { /* case on fnc */
152
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 */
158 break;
159
160 case 003: /* init scan DMA/DMC */
161 lpt_prdn = 0; /* clear pr done */
162 lpt_wdpos = 0; /* init scan pos */
163 lpt_eor = 0;
164 if (ch >= 0) lpt_dma = 1; /* try for DMA/DMC */
165 else lpt_dma = 0;
166 if (!sim_is_active (&lpt_unit)) {
167 lpt_rdy = 1;
168 if (lpt_dma) SET_CH_REQ (ch);
169 }
170 CLR_INT (INT_LPT); /* clear int */
171 break;
172
173 case 007: /* init scan IO bus */
174 lpt_prdn = 0; /* clear pr done */
175 lpt_wdpos = 0; /* init scan pos */
176 lpt_eor = 0;
177 lpt_dma = 0; /* IO bus */
178 if (!sim_is_active (&lpt_unit)) lpt_rdy = 1;
179 CLR_INT (INT_LPT); /* clear int */
180 break;
181
182 default:
183 return IOBADFNC (dat);
184 }
185 break;
186
187 case ioSKS: /* SKS */
188 switch (fnc) { /* case on fnc */
189
190 case 000: /* if xfer rdy */
191 if (lpt_rdy) return IOSKIP (dat);
192 break;
193
194 case 002: /* if !alarm */
195 if (lpt_unit.flags & UNIT_ATT) return IOSKIP (dat);
196 break;
197
198 case 003: /* if odd col */
199 if (lpt_crpos) return IOSKIP (dat);
200 break;
201
202 case 004: /* if !interrupt */
203 if (!TST_INTREQ (INT_LPT)) return IOSKIP (dat);
204 break;
205
206 case 011: /* if line printed */
207 if (lpt_prdn) return IOSKIP (dat);
208 break;
209
210 case 012: /* if !shuttling */
211 if (!(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat);
212 break;
213
214 case 013:
215 if (lpt_prdn && !(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat);
216 break;
217
218 case 014: /* if !advancing */
219 if (!(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat);
220 break;
221
222 case 015:
223 if (lpt_prdn && !(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat);
224 break;
225
226 case 016:
227 if (!(lpt_svcst & (LPT_SVCSH | LPT_SVCPA))) return IOSKIP (dat);
228 break;
229
230 case 017:
231 if (lpt_prdn && !(lpt_svcst & (LPT_SVCSH | LPT_SVCPA)))
232 return IOSKIP (dat);
233 break;
234
235 default:
236 return IOBADFNC (dat);
237 }
238 break;
239
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;
248 }
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 */
263 }
264 break;
265
266 case ioEND: /* end DMA/DMC */
267 lpt_eor = 1; /* set end range */
268 break;
269 } /* end case op */
270
271 return dat;
272 }
273
274 /* Unit service */
275
276 t_stat lpt_svc (UNIT *uptr)
277 {
278 int32 i;
279 int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */
280 static const char *lpt_cc[] = {
281 "\r",
282 "\n",
283 "\n\f",
284 "\n"
285 };
286
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 */
291 else {
292 lpt_rdy = 1; /* set ready */
293 SET_CH_REQ (ch); /* get more data */
294 }
295 }
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;
303 }
304 lpt_buf[i + 1] = 0;
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 */
309 }
310 }
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 */
315 }
316 lpt_svcst = 0;
317 return SCPE_OK;
318 }
319
320 /* Reset routine */
321
322 t_stat lpt_reset (DEVICE *dptr)
323 {
324 int32 i;
325
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 */
330 lpt_eor = 0;
331 lpt_dma = 0;
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 */
335 CLR_ENB (INT_LPT);
336 sim_cancel (&lpt_unit); /* deactivate unit */
337 return SCPE_OK;
338 }