First Commit of my working state
[simh.git] / HP2100 / hp2100_lpt.c
1 /* hp2100_lpt.c: HP 2100 12845B 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 lpt 12845B 2607 line printer
27
28 22-Jan-07 RMS Added UNIT_TEXT flag
29 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified)
30 19-Nov-04 JDB Added restart when set online, etc.
31 29-Sep-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON
32 Fixed status returns for error conditions
33 Fixed TOF handling so form remains on line 0
34 03-Jun-04 RMS Fixed timing (found by Dave Bryan)
35 26-Apr-04 RMS Fixed SFS x,C and SFC x,C
36 Implemented DMA SRQ (follows FLG)
37 25-Apr-03 RMS Revised for extended file support
38 24-Oct-02 RMS Cloned from 12653A
39
40 The 2607 provides three status bits via the interface:
41
42 bit 15 -- printer ready (online)
43 bit 14 -- paper out
44 bit 0 -- printer idle
45
46 The expected status returns are:
47
48 140001 -- power off or cable disconnected
49 100001 -- power on, paper loaded, printer ready
50 100000 -- power on, paper loaded, printer busy
51 040000 -- power on, paper out (at bottom-of-form)
52 000000 -- power on, paper out (not at BOF) / print button up / platen open
53
54 Manual Note: "2-33. PAPER OUT SIGNAL. [...] The signal is asserted only
55 when the format tape in the line printer has reached the bottom of form."
56
57 These simulator commands provide the listed printer states:
58
59 SET LPT POWEROFF --> power off or cable disconnected
60 SET LPT POWERON --> power on
61 SET LPT OFFLINE --> print button up
62 SET LPT ONLINE --> print button down
63 ATT LPT <file> --> paper loaded
64 DET LPT --> paper out
65
66 Reference:
67 - 12845A Line Printer Operating and Service Manual (12845-90001, Aug-1972)
68 */
69
70 #include "hp2100_defs.h"
71
72 #define LPT_PAGELNT 60 /* page length */
73
74 #define LPT_NBSY 0000001 /* not busy */
75 #define LPT_PAPO 0040000 /* paper out */
76 #define LPT_RDY 0100000 /* ready */
77 #define LPT_PWROFF LPT_RDY | LPT_PAPO | LPT_NBSY /* power-off status */
78
79 #define LPT_CTL 0100000 /* control output */
80 #define LPT_CHAN 0000100 /* skip to chan */
81 #define LPT_SKIPM 0000077 /* line count mask */
82 #define LPT_CHANM 0000007 /* channel mask */
83
84 #define UNIT_V_POWEROFF (UNIT_V_UF + 0) /* unit powered off */
85 #define UNIT_V_OFFLINE (UNIT_V_UF + 1) /* unit offline */
86 #define UNIT_POWEROFF (1 << UNIT_V_POWEROFF)
87 #define UNIT_OFFLINE (1 << UNIT_V_OFFLINE)
88
89 extern uint32 PC;
90 extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];
91
92 int32 lpt_ctime = 4; /* char time */
93 int32 lpt_ptime = 10000; /* print time */
94 int32 lpt_stopioe = 0; /* stop on error */
95 int32 lpt_lcnt = 0; /* line count */
96 static int32 lpt_cct[8] = {
97 1, 1, 1, 2, 3, LPT_PAGELNT/2, LPT_PAGELNT/4, LPT_PAGELNT/6
98 };
99
100 DEVICE lpt_dev;
101 int32 lptio (int32 inst, int32 IR, int32 dat);
102 t_stat lpt_svc (UNIT *uptr);
103 t_stat lpt_reset (DEVICE *dptr);
104 t_stat lpt_restart (UNIT *uptr, int32 value, char *cptr, void *desc);
105 t_stat lpt_attach (UNIT *uptr, char *cptr);
106
107 /* LPT data structures
108
109 lpt_dev LPT device descriptor
110 lpt_unit LPT unit descriptor
111 lpt_reg LPT register list
112 */
113
114 DIB lpt_dib = { LPT, 0, 0, 0, 0, 0, &lptio };
115
116 UNIT lpt_unit = {
117 UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0)
118 };
119
120 REG lpt_reg[] = {
121 { ORDATA (BUF, lpt_unit.buf, 7) },
122 { FLDATA (CMD, lpt_dib.cmd, 0) },
123 { FLDATA (CTL, lpt_dib.ctl, 0) },
124 { FLDATA (FLG, lpt_dib.flg, 0) },
125 { FLDATA (FBF, lpt_dib.fbf, 0) },
126 { FLDATA (SRQ, lpt_dib.srq, 0) },
127 { DRDATA (LCNT, lpt_lcnt, 7) },
128 { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },
129 { DRDATA (CTIME, lpt_ctime, 31), PV_LEFT },
130 { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT },
131 { FLDATA (STOP_IOE, lpt_stopioe, 0) },
132 { ORDATA (DEVNO, lpt_dib.devno, 6), REG_HRO },
133 { NULL }
134 };
135
136 MTAB lpt_mod[] = {
137 { UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", NULL },
138 { UNIT_POWEROFF, 0, "power on", "POWERON", lpt_restart },
139 { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL },
140 { UNIT_OFFLINE, 0, "online", "ONLINE", lpt_restart },
141 { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO",
142 &hp_setdev, &hp_showdev, &lpt_dev },
143 { 0 }
144 };
145
146 DEVICE lpt_dev = {
147 "LPT", &lpt_unit, lpt_reg, lpt_mod,
148 1, 10, 31, 1, 8, 8,
149 NULL, NULL, &lpt_reset,
150 NULL, &lpt_attach, NULL,
151 &lpt_dib, DEV_DISABLE
152 };
153
154 /* IO instructions */
155
156 int32 lptio (int32 inst, int32 IR, int32 dat)
157 {
158 int32 dev;
159
160 dev = IR & I_DEVMASK; /* get device no */
161 switch (inst) { /* case on opcode */
162
163 case ioFLG: /* flag clear/set */
164 if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */
165 break;
166
167 case ioSFC: /* skip flag clear */
168 if (FLG (dev) == 0) PC = (PC + 1) & VAMASK;
169 break;
170
171 case ioSFS: /* skip flag set */
172 if (FLG (dev) != 0) PC = (PC + 1) & VAMASK;
173 break;
174
175 case ioOTX: /* output */
176 lpt_unit.buf = dat & (LPT_CTL | 0177);
177 break;
178
179 case ioLIX: /* load */
180 dat = 0; /* default sta = 0 */
181 case ioMIX: /* merge */
182 if (lpt_unit.flags & UNIT_POWEROFF) /* power off? */
183 dat = dat | LPT_PWROFF;
184 else if (!(lpt_unit.flags & UNIT_OFFLINE)) { /* online? */
185 if (lpt_unit.flags & UNIT_ATT) { /* paper loaded? */
186 dat = dat | LPT_RDY;
187 if (!sim_is_active (&lpt_unit)) /* printer busy? */
188 dat = dat | LPT_NBSY;
189 }
190 else if (lpt_lcnt == LPT_PAGELNT - 1) /* paper out, at BOF? */
191 dat = dat | LPT_PAPO;
192 }
193 break;
194
195 case ioCRS: /* control reset (action unverif) */
196 case ioCTL: /* control clear/set */
197 if (IR & I_CTL) { /* CLC */
198 clrCMD (dev); /* clear ctl, cmd */
199 clrCTL (dev);
200 }
201 else { /* STC */
202 setCMD (dev); /* set ctl, cmd */
203 setCTL (dev);
204 sim_activate (&lpt_unit, /* schedule op */
205 (lpt_unit.buf & LPT_CTL)? lpt_ptime: lpt_ctime);
206 }
207 break;
208
209 default:
210 break;
211 }
212
213 if (IR & I_HC) { clrFSR (dev); } /* H/C option */
214 return dat;
215 }
216
217 /* Unit service */
218
219 t_stat lpt_svc (UNIT *uptr)
220 {
221 int32 i, skip, chan, dev;
222
223 dev = lpt_dib.devno; /* get dev no */
224 if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
225 return IORETURN (lpt_stopioe, SCPE_UNATT);
226 else if (uptr->flags & UNIT_OFFLINE) /* offline? */
227 return IORETURN (lpt_stopioe, STOP_OFFLINE);
228 else if (uptr->flags & UNIT_POWEROFF) /* powered off? */
229 return IORETURN (lpt_stopioe, STOP_PWROFF);
230 clrCMD (dev); /* clear cmd */
231 setFSR (dev); /* set flag, fbf */
232 if (uptr->buf & LPT_CTL) { /* control word? */
233 if (uptr->buf & LPT_CHAN) {
234 chan = uptr->buf & LPT_CHANM;
235 if (chan == 0) { /* top of form? */
236 fputc ('\f', uptr->fileref); /* ffeed */
237 lpt_lcnt = 0; /* reset line cnt */
238 skip = 0;
239 }
240 else if (chan == 1) skip = LPT_PAGELNT - lpt_lcnt - 1;
241 else skip = lpt_cct[chan] - (lpt_lcnt % lpt_cct[chan]);
242 }
243 else {
244 skip = uptr->buf & LPT_SKIPM;
245 if (skip == 0) fputc ('\r', uptr->fileref);
246 }
247 for (i = 0; i < skip; i++) fputc ('\n', uptr->fileref);
248 lpt_lcnt = (lpt_lcnt + skip) % LPT_PAGELNT;
249 }
250 else fputc (uptr->buf & 0177, uptr->fileref); /* no, just add char */
251 if (ferror (uptr->fileref)) {
252 perror ("LPT I/O error");
253 clearerr (uptr->fileref);
254 return SCPE_IOERR;
255 }
256 lpt_unit.pos = ftell (uptr->fileref); /* update pos */
257 return SCPE_OK;
258 }
259
260 /* Reset routine - called from SCP, flags in DIB */
261
262 t_stat lpt_reset (DEVICE *dptr)
263 {
264 lpt_dib.cmd = lpt_dib.ctl = 0; /* clear cmd, ctl */
265 lpt_dib.flg = lpt_dib.fbf = lpt_dib.srq = 1; /* set flg, fbf, srq */
266 lpt_unit.buf = 0;
267 sim_cancel (&lpt_unit); /* deactivate unit */
268 return SCPE_OK;
269 }
270
271 /* Restart I/O routine
272
273 If I/O is started via STC, and the printer is powered off, offline,
274 or out of paper, the CTL and CMD flip-flops will set, a service event
275 will be scheduled, and the service routine will be entered. If
276 STOP_IOE is not set, the I/O operation will "hang" at that point
277 until the printer is powered on, set online, or paper is supplied
278 (attached).
279
280 If a pending operation is "hung" when this routine is called, it is
281 restarted, which clears CTL and sets FBF and FLG, completing the
282 original I/O request.
283 */
284
285 t_stat lpt_restart (UNIT *uptr, int32 value, char *cptr, void *desc)
286 {
287 if (lpt_dib.cmd && lpt_dib.ctl && !sim_is_active (uptr))
288 sim_activate (uptr, 0); /* reschedule I/O */
289 return SCPE_OK;
290 }
291
292 /* Attach routine */
293
294 t_stat lpt_attach (UNIT *uptr, char *cptr)
295 {
296 lpt_lcnt = 0; /* top of form */
297 lpt_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */
298 return attach_unit (uptr, cptr);
299 }