First Commit of my working state
[simh.git] / H316 / h316_lp.c
CommitLineData
196ba1fc
PH
1/* h316_lp.c: Honeywell 316/516 line printer\r
2\r
3 Copyright (c) 1999-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 lpt line printer\r
27\r
28 09-Jun-07 RMS Fixed lost last print line (from Theo Engel)\r
29 19-Jan-06 RMS Added UNIT_TEXT flag\r
30 03-Apr-06 RMS Fixed bug in blanks backscanning (from Theo Engel)\r
31 01-Dec-04 RMS Fixed bug in DMA/DMC support\r
32 24-Oct-03 RMS Added DMA/DMC support\r
33 25-Apr-03 RMS Revised for extended file support\r
34 30-May-02 RMS Widened POS to 32b\r
35\r
36 The Series 16 line printer is an unbuffered Analex shuttle printer.\r
37 Because it was unbuffered, the CPU had to scan out an entire line's\r
38 worth of characters (60 words) for every character on the print drum\r
39 (64 characters). Because it was a shuttle printer, the entire\r
40 process must be repeated first for the odd columns and then for the\r
41 even columns. After scanning the odd columns, the printer carriage\r
42 shuttled right by one column; after scanning the even columns, the\r
43 carriage shuttled left. This halved the number of hammers required,\r
44 reducing cost but increasing mechanical complexity.\r
45\r
46 The real printer is very timing dependent. If the CPU misses a\r
47 scan, then the wrong characters are printed. If the printer protocol\r
48 is violated, then results are unpredictable. The simulated printer\r
49 is much more forgiving. Rather than simulating the fixed drum and\r
50 hammer timing of the real printer, the simulator is driven by the\r
51 program's OTA instructions. If the program misses a time slot, the\r
52 simulator will still print the "correct" result. A timing based\r
53 simulation would be very hard to do in the absense of accurate\r
54 instruction timing.\r
55\r
56 Printer state is maintained in a set of position and state variables:\r
57\r
58 lpt_wdpos word count within a line scan (0-59)\r
59 lpt_drpos drum position (0-63)\r
60 lpt_crpos carriage position (0-1)\r
61 lpt_svcst service state (shuttle, paper advance)\r
62 lpt_svcch channel for paper advance (0 = no adv)\r
63 lpt_rdy transfer ready flag\r
64 lpt_prdn printing done flag\r
65 lpt_dma use DMA/DMC\r
66 lpt_eor DMA/DMC end of range\r
67*/\r
68\r
69#include "h316_defs.h"\r
70\r
71#define LPT_WIDTH 120 /* width */\r
72#define LPT_SCAN (LPT_WIDTH / 2) /* words/scan */\r
73#define LPT_DRUM 64 /* drum rows */\r
74#define LPT_SVCSH 01 /* shuttle */\r
75#define LPT_SVCPA 02 /* paper advance */\r
76\r
77extern int32 dev_int, dev_enb;\r
78extern int32 stop_inst;\r
79extern uint32 chan_req;\r
80\r
81int32 lpt_wdpos = 0; /* word position */\r
82int32 lpt_drpos = 0; /* drum position */\r
83int32 lpt_crpos = 0; /* carriage position */\r
84int32 lpt_svcst = 0; /* service state */\r
85int32 lpt_svcch = 0; /* service channel */\r
86int32 lpt_rdy = 0; /* transfer flag */\r
87int32 lpt_prdn = 1; /* printing done */\r
88int32 lpt_dma = 0; /* use DMA/DMC */\r
89int32 lpt_eor = 0; /* DMA/DMC end range */\r
90char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */\r
91int32 lpt_xtime = 5; /* transfer time */\r
92int32 lpt_etime = 50; /* end of scan time */\r
93int32 lpt_ptime = 5000; /* paper adv time */\r
94int32 lpt_stopioe = 0; /* stop on error */\r
95\r
96int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev);\r
97t_stat lpt_svc (UNIT *uptr);\r
98t_stat lpt_reset (DEVICE *dptr);\r
99\r
100/* LPT data structures\r
101\r
102 lpt_dev LPT device descriptor\r
103 lpt_unit LPT unit descriptor\r
104 lpt_mod LPT modifiers\r
105 lpt_reg LPT register list\r
106*/\r
107\r
108DIB lpt_dib = { LPT, IOBUS, 1, &lptio };\r
109\r
110UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) };\r
111\r
112REG lpt_reg[] = {\r
113 { DRDATA (WDPOS, lpt_wdpos, 6) },\r
114 { DRDATA (DRPOS, lpt_drpos, 6) },\r
115 { FLDATA (CRPOS, lpt_crpos, 0) },\r
116 { FLDATA (RDY, lpt_rdy, 0) },\r
117 { FLDATA (EOR, lpt_eor, 0) },\r
118 { FLDATA (DMA, lpt_dma, 0) },\r
119 { FLDATA (PRDN, lpt_prdn, 0) },\r
120 { FLDATA (INTREQ, dev_int, INT_V_LPT) },\r
121 { FLDATA (ENABLE, dev_enb, INT_V_LPT) },\r
122 { ORDATA (SVCST, lpt_svcst, 2) },\r
123 { ORDATA (SVCCH, lpt_svcch, 2) },\r
124 { BRDATA (BUF, lpt_buf, 8, 8, 120) },\r
125 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },\r
126 { DRDATA (XTIME, lpt_xtime, 24), PV_LEFT },\r
127 { DRDATA (ETIME, lpt_etime, 24), PV_LEFT },\r
128 { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT },\r
129 { FLDATA (STOP_IOE, lpt_stopioe, 0) },\r
130 { NULL }\r
131 };\r
132\r
133DEVICE lpt_dev = {\r
134 "LPT", &lpt_unit, lpt_reg, NULL,\r
135 1, 10, 31, 1, 8, 8,\r
136 NULL, NULL, &lpt_reset,\r
137 NULL, NULL, NULL,\r
138 &lpt_dib, DEV_DISABLE\r
139 };\r
140\r
141/* IO routine */\r
142\r
143int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev)\r
144{\r
145int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */\r
146int32 chr;\r
147\r
148switch (inst) { /* case on opcode */\r
149\r
150 case ioOCP: /* OCP */\r
151 switch (fnc) { /* case on fnc */\r
152\r
153 case 000: case 002: case 004: /* paper adv */\r
154 lpt_svcst = lpt_svcst | LPT_SVCPA; /* set state */\r
155 lpt_svcch = fnc >> 1; /* save channel */\r
156 sim_activate (&lpt_unit, lpt_ptime);\r
157 CLR_INT (INT_LPT); /* clear int */\r
158 break;\r
159\r
160 case 003: /* init scan DMA/DMC */\r
161 lpt_prdn = 0; /* clear pr done */\r
162 lpt_wdpos = 0; /* init scan pos */\r
163 lpt_eor = 0;\r
164 if (ch >= 0) lpt_dma = 1; /* try for DMA/DMC */\r
165 else lpt_dma = 0;\r
166 if (!sim_is_active (&lpt_unit)) {\r
167 lpt_rdy = 1;\r
168 if (lpt_dma) SET_CH_REQ (ch);\r
169 }\r
170 CLR_INT (INT_LPT); /* clear int */\r
171 break;\r
172\r
173 case 007: /* init scan IO bus */\r
174 lpt_prdn = 0; /* clear pr done */\r
175 lpt_wdpos = 0; /* init scan pos */\r
176 lpt_eor = 0;\r
177 lpt_dma = 0; /* IO bus */\r
178 if (!sim_is_active (&lpt_unit)) lpt_rdy = 1;\r
179 CLR_INT (INT_LPT); /* clear int */\r
180 break;\r
181\r
182 default:\r
183 return IOBADFNC (dat);\r
184 }\r
185 break;\r
186\r
187 case ioSKS: /* SKS */\r
188 switch (fnc) { /* case on fnc */\r
189\r
190 case 000: /* if xfer rdy */\r
191 if (lpt_rdy) return IOSKIP (dat);\r
192 break;\r
193\r
194 case 002: /* if !alarm */\r
195 if (lpt_unit.flags & UNIT_ATT) return IOSKIP (dat);\r
196 break;\r
197\r
198 case 003: /* if odd col */\r
199 if (lpt_crpos) return IOSKIP (dat);\r
200 break;\r
201\r
202 case 004: /* if !interrupt */\r
203 if (!TST_INTREQ (INT_LPT)) return IOSKIP (dat);\r
204 break;\r
205\r
206 case 011: /* if line printed */\r
207 if (lpt_prdn) return IOSKIP (dat);\r
208 break;\r
209\r
210 case 012: /* if !shuttling */\r
211 if (!(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat);\r
212 break;\r
213\r
214 case 013:\r
215 if (lpt_prdn && !(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat);\r
216 break;\r
217\r
218 case 014: /* if !advancing */\r
219 if (!(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat);\r
220 break;\r
221\r
222 case 015:\r
223 if (lpt_prdn && !(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat);\r
224 break;\r
225\r
226 case 016:\r
227 if (!(lpt_svcst & (LPT_SVCSH | LPT_SVCPA))) return IOSKIP (dat);\r
228 break;\r
229\r
230 case 017:\r
231 if (lpt_prdn && !(lpt_svcst & (LPT_SVCSH | LPT_SVCPA)))\r
232 return IOSKIP (dat);\r
233 break;\r
234\r
235 default:\r
236 return IOBADFNC (dat);\r
237 }\r
238 break;\r
239\r
240 case ioOTA: /* OTA */\r
241 if (fnc) return IOBADFNC (dat); /* only fnc 0 */\r
242 if (lpt_rdy) { /* xfer ready? */\r
243 lpt_rdy = 0; /* clear xfer */\r
244 chr = (dat >> (lpt_crpos? 0: 8)) & 077; /* get 6b char */\r
245 if (chr == lpt_drpos) { /* match drum pos? */\r
246 if (chr < 040) chr = chr | 0100;\r
247 lpt_buf[2 * lpt_wdpos + lpt_crpos] = chr;\r
248 }\r
249 lpt_wdpos++; /* adv scan pos */\r
250 if (lpt_wdpos >= LPT_SCAN) { /* end of scan? */\r
251 lpt_wdpos = 0; /* reset scan pos */\r
252 lpt_drpos++; /* adv drum pos */\r
253 if (lpt_drpos >= LPT_DRUM) { /* end of drum? */\r
254 lpt_drpos = 0; /* reset drum pos */\r
255 lpt_crpos = lpt_crpos ^ 1; /* shuttle */\r
256 lpt_svcst = lpt_svcst | LPT_SVCSH;\r
257 sim_activate (&lpt_unit, lpt_ptime);\r
258 } /* end if shuttle */\r
259 else sim_activate (&lpt_unit, lpt_etime);\r
260 } /* end if endscan */\r
261 else sim_activate (&lpt_unit, lpt_xtime);\r
262 return IOSKIP (dat); /* skip return */\r
263 }\r
264 break;\r
265\r
266 case ioEND: /* end DMA/DMC */\r
267 lpt_eor = 1; /* set end range */\r
268 break;\r
269 } /* end case op */\r
270\r
271return dat;\r
272}\r
273\r
274/* Unit service */\r
275\r
276t_stat lpt_svc (UNIT *uptr)\r
277{\r
278int32 i;\r
279int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */\r
280static const char *lpt_cc[] = {\r
281 "\r",\r
282 "\n",\r
283 "\n\f",\r
284 "\n"\r
285 };\r
286\r
287if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */\r
288 return IORETURN (lpt_stopioe, SCPE_UNATT);\r
289if (lpt_dma) { /* DMA/DMC? */\r
290 if (lpt_eor) SET_INT (INT_LPT); /* end range? intr */\r
291 else {\r
292 lpt_rdy = 1; /* set ready */\r
293 SET_CH_REQ (ch); /* get more data */\r
294 }\r
295 }\r
296else lpt_rdy = 1; /* IO, continue scan */\r
297if (lpt_dma && lpt_eor) SET_INT (INT_LPT); /* end of range? */\r
298if (lpt_svcst & LPT_SVCSH) { /* shuttling? */\r
299 SET_INT (INT_LPT); /* interrupt */\r
300 if (lpt_crpos == 0) { /* done shuttling? */\r
301 for (i = LPT_WIDTH - 1; i >= 0; i--) { /* backscan for blanks */\r
302 if (lpt_buf[i] != ' ') break; \r
303 }\r
304 lpt_buf[i + 1] = 0;\r
305 fputs (lpt_buf, uptr->fileref); /* output buf */\r
306 uptr->pos = ftell (uptr->fileref); /* update pos */\r
307 for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = ' '; /* clear buf */\r
308 lpt_prdn = 1; /* print done */\r
309 }\r
310 }\r
311if (lpt_svcst & LPT_SVCPA) { /* paper advance */\r
312 SET_INT (INT_LPT); /* interrupt */\r
313 fputs (lpt_cc[lpt_svcch & 03], uptr->fileref); /* output eol */\r
314 uptr->pos = ftell (uptr->fileref); /* update pos */\r
315 }\r
316lpt_svcst = 0;\r
317return SCPE_OK;\r
318}\r
319\r
320/* Reset routine */\r
321\r
322t_stat lpt_reset (DEVICE *dptr)\r
323{\r
324int32 i;\r
325\r
326lpt_wdpos = lpt_drpos = lpt_crpos = 0; /* clear positions */\r
327lpt_svcst = lpt_svcch = 0; /* idle state */\r
328lpt_rdy = 0; /* not rdy to xfer */\r
329lpt_prdn = 1; /* printing done */\r
330lpt_eor = 0;\r
331lpt_dma = 0;\r
332for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = ' '; /* clear buffer */\r
333lpt_buf[LPT_WIDTH] = 0;\r
334CLR_INT (INT_LPT); /* clear int, enb */\r
335CLR_ENB (INT_LPT);\r
336sim_cancel (&lpt_unit); /* deactivate unit */\r
337return SCPE_OK;\r
338}\r