| 1 | /* hp2100_lps.c: HP 2100 12653A/2767 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 | lps 12653A 2767 line printer\r |
| 27 | 12566B microcircuit interface with loopback diagnostic connector\r |
| 28 | \r |
| 29 | 10-May-07 RMS Added UNIT_TEXT flag\r |
| 30 | 11-Jan-07 JDB CLC cancels I/O event if DIAG (jumper W9 in "A" pos)\r |
| 31 | Added ioCRS state to I/O decoders\r |
| 32 | 19-Nov-04 JDB Added restart when set online, etc.\r |
| 33 | Fixed col count for non-printing chars\r |
| 34 | 01-Oct-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON\r |
| 35 | Fixed status returns for error conditions\r |
| 36 | Fixed handling of non-printing characters\r |
| 37 | Fixed handling of characters after column 80\r |
| 38 | Improved timing model accuracy for RTE\r |
| 39 | Added fast/realistic timing\r |
| 40 | Added debug printouts\r |
| 41 | 03-Jun-04 RMS Fixed timing (found by Dave Bryan)\r |
| 42 | 26-Apr-04 RMS Fixed SFS x,C and SFC x,C\r |
| 43 | Implemented DMA SRQ (follows FLG)\r |
| 44 | 25-Apr-03 RMS Revised for extended file support\r |
| 45 | 24-Oct-02 RMS Added microcircuit test features\r |
| 46 | 30-May-02 RMS Widened POS to 32b\r |
| 47 | 03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW\r |
| 48 | 07-Sep-01 RMS Moved function prototypes\r |
| 49 | 21-Nov-00 RMS Fixed flag, fbf power up state\r |
| 50 | Added command flop\r |
| 51 | 15-Oct-00 RMS Added variable device number support\r |
| 52 | \r |
| 53 | This module simulates two different devices. In "diagnostic mode," it\r |
| 54 | simulates a 12566B microcircuit interface card with a loopback connector and\r |
| 55 | the jumpers set as required for execution of the General Purpose Register\r |
| 56 | diagnostic. In non-diagnostic mode, it simulates a 12653A line printer\r |
| 57 | interface card and a 2767 line printer.\r |
| 58 | \r |
| 59 | The 12566B interface with the loopback connector ties the device command\r |
| 60 | output to the device flag input. Setting control therefore causes device\r |
| 61 | flag to set almost immediately. Device command is active only during that\r |
| 62 | interim. Under simulation, the loopback occurs within the STC handler, and\r |
| 63 | CMD is never set.\r |
| 64 | \r |
| 65 | The 2767 impact printer has a rotating drum with 80 columns of 64 raised\r |
| 66 | characters. ASCII codes 32 through 95 (SPACE through "_") form the print\r |
| 67 | repertoire. The printer responds to the control characters FF, LF, and CR.\r |
| 68 | \r |
| 69 | The 80 columns are divided into four zones of 20 characters each that are\r |
| 70 | addressed sequentially. Received characters are buffered in a 20-character\r |
| 71 | memory. When the 20th printable character is received, the current zone is\r |
| 72 | printed, and the memory is reset. In the absence of print command\r |
| 73 | characters, a zone print operation will commence after each group of 20\r |
| 74 | printable characters is transmitted to the printer.\r |
| 75 | \r |
| 76 | The print command characters have these actions:\r |
| 77 | \r |
| 78 | * CR -- print the characters in the current zone, reset to zone 1, and clear\r |
| 79 | the buffer memory.\r |
| 80 | * LF -- same as CR, plus advances the paper one line.\r |
| 81 | * FF -- same as CR, plus advances the paper to the top of the next form.\r |
| 82 | \r |
| 83 | The 2767 provides two status bits via the interface:\r |
| 84 | \r |
| 85 | bit 15 -- printer not ready\r |
| 86 | bit 0 -- printer busy\r |
| 87 | \r |
| 88 | The expected status returns are:\r |
| 89 | \r |
| 90 | 100001 -- power off or cable disconnected\r |
| 91 | 100001 -- initial power on, then changes to 000001 within sixty\r |
| 92 | seconds of initial power on\r |
| 93 | 000001 -- power on, paper unloaded or printer offline or not idle\r |
| 94 | 000000 -- power on, paper loaded and printer online and idle\r |
| 95 | \r |
| 96 | These simulator commands provide the listed printer states:\r |
| 97 | \r |
| 98 | SET LPS POWEROFF --> power off or cable disconnected\r |
| 99 | SET LPS POWERON --> power on\r |
| 100 | SET LPS OFFLINE --> printer offline\r |
| 101 | SET LPS ONLINE --> printer online\r |
| 102 | ATT LPS <file> --> paper loaded\r |
| 103 | DET LPS --> paper out\r |
| 104 | \r |
| 105 | References:\r |
| 106 | - 2767A Line Printer Operating and Service Manual (02767-90002, Oct-1973)\r |
| 107 | - 12566B, 12566B-001, 12566B-002, 12566B-003 Microcircuit Interface Kits\r |
| 108 | Operating and Service Manual (12566-90015, Apr-1976)\r |
| 109 | \r |
| 110 | The following implemented behaviors have been inferred from secondary sources\r |
| 111 | (diagnostics, operating system drivers, etc.), due to absent or contradictory\r |
| 112 | authoritative information; future correction may be needed:\r |
| 113 | \r |
| 114 | 1. Paper out sets BUSY instead of NOT READY.\r |
| 115 | 2. Print operation in progress sets BUSY instead of NOT READY.\r |
| 116 | 3. Characters not in the print repertoire are replaced with blanks.\r |
| 117 | 4. The 81st and succeeding characters overprint the current line.\r |
| 118 | */\r |
| 119 | \r |
| 120 | #include "hp2100_defs.h"\r |
| 121 | #include "hp2100_cpu.h"\r |
| 122 | \r |
| 123 | #define LPS_ZONECNT 20 /* zone char count */\r |
| 124 | #define LPS_PAGECNT 80 /* page char count */\r |
| 125 | #define LPS_PAGELNT 60 /* page line length */\r |
| 126 | #define LPS_FORMLNT 66 /* form line length */\r |
| 127 | \r |
| 128 | /* Printer power states */\r |
| 129 | \r |
| 130 | #define LPS_ON 0 /* power is on */\r |
| 131 | #define LPS_OFF 1 /* power is off */\r |
| 132 | #define LPS_TURNING_ON 2 /* power is turning on */\r |
| 133 | \r |
| 134 | #define LPS_BUSY 0000001 /* busy status */\r |
| 135 | #define LPS_NRDY 0100000 /* not ready status */\r |
| 136 | #define LPS_PWROFF LPS_BUSY | LPS_NRDY /* power-off status */\r |
| 137 | \r |
| 138 | #define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */\r |
| 139 | #define UNIT_V_POWEROFF (UNIT_V_UF + 1) /* unit powered off */\r |
| 140 | #define UNIT_V_OFFLINE (UNIT_V_UF + 2) /* unit offline */\r |
| 141 | #define UNIT_DIAG (1 << UNIT_V_DIAG)\r |
| 142 | #define UNIT_POWEROFF (1 << UNIT_V_POWEROFF)\r |
| 143 | #define UNIT_OFFLINE (1 << UNIT_V_OFFLINE)\r |
| 144 | \r |
| 145 | extern uint32 PC;\r |
| 146 | extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];\r |
| 147 | extern FILE *sim_deb;\r |
| 148 | \r |
| 149 | int32 lps_ccnt = 0; /* character count */\r |
| 150 | int32 lps_lcnt = 0; /* line count */\r |
| 151 | int32 lps_stopioe = 0; /* stop on error */\r |
| 152 | int32 lps_sta = 0; /* printer status */\r |
| 153 | int32 lps_timing = 1; /* timing type */\r |
| 154 | uint32 lps_power = LPS_ON; /* power state */\r |
| 155 | \r |
| 156 | /* Hardware timing:\r |
| 157 | (based on 1580 instr/msec) instr msec calc msec\r |
| 158 | ------------------------\r |
| 159 | - character transfer time : ctime = 2 2 us\r |
| 160 | - per-zone printing time : ptime = 55300 35 40\r |
| 161 | - per-line paper slew time : stime = 17380 11 13\r |
| 162 | - power-on ready delay time : rtime = 158000 100\r |
| 163 | \r |
| 164 | NOTE: the printer acknowledges before the print motion has stopped to allow\r |
| 165 | for continuous slew, so the set times are a bit less than the calculated\r |
| 166 | operation time from the manual.\r |
| 167 | \r |
| 168 | NOTE: the 2767 diagnostic checks completion times, so the realistic timing\r |
| 169 | must be used. Because simulator timing is in instructions, and because the\r |
| 170 | diagnostic uses the TIMER instruction (~1580 executions per millisecond) when\r |
| 171 | running on a 1000-E/F but a software timing loop (~400-600 executions per\r |
| 172 | millisecond) when running on anything else, realistic timings are decreased by\r |
| 173 | three-fourths when not executing on an E/F.\r |
| 174 | */\r |
| 175 | \r |
| 176 | int32 lps_ctime = 0; /* char xfer time */\r |
| 177 | int32 lps_ptime = 0; /* zone printing time */\r |
| 178 | int32 lps_stime = 0; /* paper slew time */\r |
| 179 | int32 lps_rtime = 0; /* power-on ready time */\r |
| 180 | \r |
| 181 | typedef int32 TIMESET[4]; /* set of controller times */\r |
| 182 | \r |
| 183 | int32 *const lps_timers[] = { &lps_ctime, &lps_ptime, &lps_stime, &lps_rtime };\r |
| 184 | \r |
| 185 | const TIMESET lps_times[2] = {\r |
| 186 | { 2, 55300, 17380, 158000 }, /* REALTIME */\r |
| 187 | { 2, 1000, 1000, 1000 } /* FASTTIME */\r |
| 188 | };\r |
| 189 | \r |
| 190 | DEVICE lps_dev;\r |
| 191 | int32 lpsio (int32 inst, int32 IR, int32 dat);\r |
| 192 | t_stat lps_svc (UNIT *uptr);\r |
| 193 | t_stat lps_reset (DEVICE *dptr);\r |
| 194 | t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc);\r |
| 195 | t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc);\r |
| 196 | t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc);\r |
| 197 | t_stat lps_attach (UNIT *uptr, char *cptr);\r |
| 198 | t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 199 | t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc);\r |
| 200 | \r |
| 201 | /* LPS data structures\r |
| 202 | \r |
| 203 | lps_dev LPS device descriptor\r |
| 204 | lps_unit LPS unit descriptor\r |
| 205 | lps_reg LPS register list\r |
| 206 | */\r |
| 207 | \r |
| 208 | DIB lps_dib = { LPS, 0, 0, 0, 0, 0, &lpsio };\r |
| 209 | \r |
| 210 | UNIT lps_unit = {\r |
| 211 | UDATA (&lps_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0)\r |
| 212 | };\r |
| 213 | \r |
| 214 | REG lps_reg[] = {\r |
| 215 | { ORDATA (BUF, lps_unit.buf, 16) },\r |
| 216 | { ORDATA (STA, lps_sta, 16) },\r |
| 217 | { ORDATA (POWER, lps_power, 2), REG_RO },\r |
| 218 | { FLDATA (CMD, lps_dib.cmd, 0) },\r |
| 219 | { FLDATA (CTL, lps_dib.ctl, 0) },\r |
| 220 | { FLDATA (FLG, lps_dib.flg, 0) },\r |
| 221 | { FLDATA (FBF, lps_dib.fbf, 0) },\r |
| 222 | { FLDATA (SRQ, lps_dib.srq, 0) },\r |
| 223 | { DRDATA (CCNT, lps_ccnt, 7), PV_LEFT },\r |
| 224 | { DRDATA (LCNT, lps_lcnt, 7), PV_LEFT },\r |
| 225 | { DRDATA (POS, lps_unit.pos, T_ADDR_W), PV_LEFT },\r |
| 226 | { DRDATA (CTIME, lps_ctime, 24), PV_LEFT },\r |
| 227 | { DRDATA (PTIME, lps_ptime, 24), PV_LEFT },\r |
| 228 | { DRDATA (STIME, lps_stime, 24), PV_LEFT },\r |
| 229 | { DRDATA (RTIME, lps_rtime, 24), PV_LEFT },\r |
| 230 | { FLDATA (TIMING, lps_timing, 0), REG_HRO },\r |
| 231 | { FLDATA (STOP_IOE, lps_stopioe, 0) },\r |
| 232 | { ORDATA (DEVNO, lps_dib.devno, 6), REG_HRO },\r |
| 233 | { NULL }\r |
| 234 | };\r |
| 235 | \r |
| 236 | MTAB lps_mod[] = {\r |
| 237 | { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL },\r |
| 238 | { UNIT_DIAG, 0, "printer mode", "PRINTER", NULL },\r |
| 239 | { UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", lps_poweroff },\r |
| 240 | { UNIT_POWEROFF, 0, "power on", "POWERON", lps_poweron },\r |
| 241 | { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL },\r |
| 242 | { UNIT_OFFLINE, 0, "online", "ONLINE", lps_restart },\r |
| 243 | { MTAB_XTD | MTAB_VDV, 0, NULL, "REALTIME",\r |
| 244 | &lps_set_timing, NULL, NULL },\r |
| 245 | { MTAB_XTD | MTAB_VDV, 1, NULL, "FASTTIME",\r |
| 246 | &lps_set_timing, NULL, NULL },\r |
| 247 | { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL,\r |
| 248 | NULL, &lps_show_timing, NULL },\r |
| 249 | { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO",\r |
| 250 | &hp_setdev, &hp_showdev, &lps_dev },\r |
| 251 | { 0 }\r |
| 252 | };\r |
| 253 | \r |
| 254 | DEVICE lps_dev = {\r |
| 255 | "LPS", &lps_unit, lps_reg, lps_mod,\r |
| 256 | 1, 10, 31, 1, 8, 8,\r |
| 257 | NULL, NULL, &lps_reset,\r |
| 258 | NULL, &lps_attach, NULL,\r |
| 259 | &lps_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG\r |
| 260 | };\r |
| 261 | \r |
| 262 | /* IO instructions */\r |
| 263 | \r |
| 264 | int32 lpsio (int32 inst, int32 IR, int32 dat)\r |
| 265 | {\r |
| 266 | int32 dev, sched;\r |
| 267 | \r |
| 268 | dev = IR & I_DEVMASK; /* get device no */\r |
| 269 | switch (inst) { /* case on opcode */\r |
| 270 | \r |
| 271 | case ioFLG: /* flag clear/set */\r |
| 272 | if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */\r |
| 273 | break;\r |
| 274 | \r |
| 275 | case ioSFC: /* skip flag clear */\r |
| 276 | if (FLG (dev) == 0) PC = (PC + 1) & VAMASK;\r |
| 277 | break;\r |
| 278 | \r |
| 279 | case ioSFS: /* skip flag set */\r |
| 280 | if (FLG (dev) != 0) PC = (PC + 1) & VAMASK;\r |
| 281 | break;\r |
| 282 | \r |
| 283 | case ioOTX: /* output */\r |
| 284 | if (DEBUG_PRS (lps_dev))\r |
| 285 | fprintf (sim_deb, ">>LPS OTx: Character %06o output\n", dat);\r |
| 286 | lps_unit.buf = dat;\r |
| 287 | break;\r |
| 288 | \r |
| 289 | case ioLIX: /* load */\r |
| 290 | dat = 0; /* default sta = 0 */\r |
| 291 | case ioMIX: /* merge */\r |
| 292 | if ((lps_unit.flags & UNIT_DIAG) == 0) { /* real lpt? */\r |
| 293 | if (lps_power == LPS_ON) { /* power on? */\r |
| 294 | if (((lps_unit.flags & UNIT_ATT) == 0) || /* paper out? */\r |
| 295 | (lps_unit.flags & UNIT_OFFLINE) || /* offline? */\r |
| 296 | sim_is_active (&lps_unit)) lps_sta = LPS_BUSY;\r |
| 297 | else lps_sta = 0;\r |
| 298 | }\r |
| 299 | else lps_sta = LPS_PWROFF;\r |
| 300 | }\r |
| 301 | dat = dat | lps_sta; /* diag, rtn status */\r |
| 302 | if (DEBUG_PRS (lps_dev))\r |
| 303 | fprintf (sim_deb, ">>LPS LIx: Status %06o returned\n", dat);\r |
| 304 | break;\r |
| 305 | \r |
| 306 | case ioCRS: /* control reset */\r |
| 307 | clrCTL (dev); /* clear control */\r |
| 308 | clrCMD (dev); /* clear command */\r |
| 309 | sim_cancel (&lps_unit); /* deactivate unit */\r |
| 310 | break;\r |
| 311 | \r |
| 312 | case ioCTL: /* control clear/set */\r |
| 313 | if (IR & I_CTL) { /* CLC */\r |
| 314 | clrCTL (dev); /* clear control */\r |
| 315 | if (lps_unit.flags & UNIT_DIAG) { /* diagnostic mode? */\r |
| 316 | clrCMD (dev); /* clear command (jumper W9-A) */\r |
| 317 | if (IR & I_HC) /* clear flag too? */\r |
| 318 | sim_cancel (&lps_unit); /* prevent FLG/SRQ */\r |
| 319 | }\r |
| 320 | }\r |
| 321 | else { /* STC */\r |
| 322 | setCTL (dev); /* set ctl */\r |
| 323 | setCMD (dev); /* set cmd */\r |
| 324 | if (lps_unit.flags & UNIT_DIAG) { /* diagnostic? */\r |
| 325 | lps_sta = lps_unit.buf; /* loop back data */\r |
| 326 | sim_activate (&lps_unit, 2); /* schedule flag */\r |
| 327 | }\r |
| 328 | else { /* real lpt, sched */\r |
| 329 | if (DEBUG_PRS (lps_dev)) fprintf (sim_deb,\r |
| 330 | ">>LPS STC: Character %06o scheduled for line %d, column %d, ",\r |
| 331 | lps_unit.buf, lps_lcnt + 1, lps_ccnt + 1);\r |
| 332 | if ((lps_unit.buf != '\f') &&\r |
| 333 | (lps_unit.buf != '\n') &&\r |
| 334 | (lps_unit.buf != '\r')) { /* normal char */\r |
| 335 | lps_ccnt = lps_ccnt + 1; /* incr char counter */\r |
| 336 | if (lps_ccnt % LPS_ZONECNT == 0) /* end of zone? */\r |
| 337 | sched = lps_ptime; /* print zone */\r |
| 338 | else sched = lps_ctime; /* xfer char */\r |
| 339 | }\r |
| 340 | else { /* print cmd */\r |
| 341 | if (lps_ccnt % LPS_ZONECNT == 0) /* last zone printed? */\r |
| 342 | sched = lps_ctime; /* yes, so just char time */\r |
| 343 | else sched = lps_ptime; /* no, so print needed */\r |
| 344 | lps_ccnt = 0; /* reset char counter */\r |
| 345 | if (lps_unit.buf == '\n') { /* line advance */\r |
| 346 | lps_lcnt = (lps_lcnt + 1) % LPS_PAGELNT;\r |
| 347 | if (lps_lcnt > 0) sched = sched + lps_stime;\r |
| 348 | else sched = sched + /* allow for perf skip */\r |
| 349 | lps_stime * (LPS_FORMLNT - LPS_PAGELNT);\r |
| 350 | }\r |
| 351 | else if (lps_unit.buf == '\f') { /* form advance */\r |
| 352 | sched = sched + lps_stime * (LPS_FORMLNT - lps_lcnt);\r |
| 353 | lps_lcnt = 0;\r |
| 354 | }\r |
| 355 | }\r |
| 356 | sim_activate (&lps_unit, sched);\r |
| 357 | if (DEBUG_PRS (lps_dev))\r |
| 358 | fprintf (sim_deb, "time = %d\n", sched);\r |
| 359 | }\r |
| 360 | }\r |
| 361 | break;\r |
| 362 | \r |
| 363 | default:\r |
| 364 | break;\r |
| 365 | }\r |
| 366 | \r |
| 367 | if (IR & I_HC) { clrFSR (dev); } /* H/C option */\r |
| 368 | return dat;\r |
| 369 | }\r |
| 370 | \r |
| 371 | /* Unit service */\r |
| 372 | \r |
| 373 | t_stat lps_svc (UNIT *uptr)\r |
| 374 | {\r |
| 375 | int32 dev;\r |
| 376 | int32 c = uptr->buf & 0177;\r |
| 377 | \r |
| 378 | if (lps_power == LPS_TURNING_ON) { /* printer warmed up? */\r |
| 379 | lps_power = LPS_ON; /* change state */\r |
| 380 | lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */\r |
| 381 | if (DEBUG_PRS (lps_dev))\r |
| 382 | fputs (">>LPS svc: Power state is ON\n", sim_deb);\r |
| 383 | return SCPE_OK; /* done */\r |
| 384 | }\r |
| 385 | dev = lps_dib.devno; /* get dev no */\r |
| 386 | if (uptr->flags & UNIT_DIAG) { /* diagnostic? */\r |
| 387 | clrCMD (dev); /* clear cmd */\r |
| 388 | setFSR (dev); /* set flag, fbf */\r |
| 389 | return SCPE_OK; /* done */\r |
| 390 | }\r |
| 391 | if ((uptr->flags & UNIT_ATT) == 0) /* attached? */\r |
| 392 | return IORETURN (lps_stopioe, SCPE_UNATT);\r |
| 393 | else if (uptr->flags & UNIT_OFFLINE) /* offline? */\r |
| 394 | return IORETURN (lps_stopioe, STOP_OFFLINE);\r |
| 395 | else if (uptr->flags & UNIT_POWEROFF) /* powered off? */\r |
| 396 | return IORETURN (lps_stopioe, STOP_PWROFF);\r |
| 397 | clrCMD (dev); /* clear cmd */\r |
| 398 | setFSR (dev); /* set flag, fbf */\r |
| 399 | if (((c < ' ') || (c > '_')) && /* non-printing char? */\r |
| 400 | (c != '\f') && (c != '\n') && (c != '\r')) {\r |
| 401 | if (DEBUG_PRS (lps_dev))\r |
| 402 | fprintf (sim_deb, ">>LPS svc: Character %06o erased\n", c);\r |
| 403 | c = ' '; /* replace with blank */\r |
| 404 | }\r |
| 405 | if (lps_ccnt > LPS_PAGECNT) { /* 81st character? */\r |
| 406 | fputc ('\r', uptr->fileref); /* return to line start */\r |
| 407 | uptr->pos = uptr->pos + 1; /* update pos */\r |
| 408 | lps_ccnt = 1; /* reset char counter */\r |
| 409 | if (DEBUG_PRS (lps_dev))\r |
| 410 | fputs (">>LPS svc: Line wraparound to column 1\n", sim_deb);\r |
| 411 | }\r |
| 412 | fputc (c, uptr->fileref); /* "print" char */\r |
| 413 | uptr->pos = uptr->pos + 1; /* update pos */\r |
| 414 | if (DEBUG_PRS (lps_dev))\r |
| 415 | fprintf (sim_deb, ">>LPS svc: Character %06o printed\n", c);\r |
| 416 | if ((lps_lcnt == 0) && (c == '\n')) { /* LF did TOF? */\r |
| 417 | fputc ('\f', uptr->fileref); /* do perf skip */\r |
| 418 | uptr->pos = uptr->pos + 1; /* update pos */\r |
| 419 | if (DEBUG_PRS (lps_dev))\r |
| 420 | fputs (">>LPS svc: Perforation skip to TOF\n", sim_deb);\r |
| 421 | }\r |
| 422 | if (ferror (uptr->fileref)) {\r |
| 423 | perror ("LPS I/O error");\r |
| 424 | clearerr (uptr->fileref);\r |
| 425 | return SCPE_IOERR;\r |
| 426 | }\r |
| 427 | return SCPE_OK;\r |
| 428 | }\r |
| 429 | \r |
| 430 | /* Reset routine - called from SCP, flags in DIB */\r |
| 431 | \r |
| 432 | t_stat lps_reset (DEVICE *dptr)\r |
| 433 | {\r |
| 434 | lps_dib.cmd = lps_dib.ctl = 0; /* clear cmd, ctl */\r |
| 435 | lps_dib.flg = lps_dib.fbf = lps_dib.srq = 1; /* set flg, fbf, srq */\r |
| 436 | lps_sta = lps_unit.buf = 0;\r |
| 437 | lps_power = LPS_ON; /* power is on */\r |
| 438 | sim_cancel (&lps_unit); /* deactivate unit */\r |
| 439 | lps_set_timing (NULL, lps_timing, NULL, NULL); /* init timing set */\r |
| 440 | return SCPE_OK;\r |
| 441 | }\r |
| 442 | \r |
| 443 | /* Restart I/O routine\r |
| 444 | \r |
| 445 | If I/O is started via STC, and the printer is powered off, offline,\r |
| 446 | or out of paper, the CTL and CMD flip-flops will set, a service event\r |
| 447 | will be scheduled, and the service routine will be entered. If\r |
| 448 | STOP_IOE is not set, the I/O operation will "hang" at that point\r |
| 449 | until the printer is powered on, set online, or paper is supplied\r |
| 450 | (attached).\r |
| 451 | \r |
| 452 | If a pending operation is "hung" when this routine is called, it is\r |
| 453 | restarted, which clears CTL and sets FBF and FLG, completing the\r |
| 454 | original I/O request.\r |
| 455 | */\r |
| 456 | \r |
| 457 | t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc)\r |
| 458 | {\r |
| 459 | if (lps_dib.cmd && lps_dib.ctl && !sim_is_active (uptr))\r |
| 460 | sim_activate (uptr, 0); /* reschedule I/O */\r |
| 461 | return SCPE_OK;\r |
| 462 | }\r |
| 463 | \r |
| 464 | /* Printer power off */\r |
| 465 | \r |
| 466 | t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc)\r |
| 467 | {\r |
| 468 | lps_power = LPS_OFF; /* change state */\r |
| 469 | if (DEBUG_PRS (lps_dev)) fputs (">>LPS set: Power state is OFF\n", sim_deb);\r |
| 470 | return SCPE_OK;\r |
| 471 | }\r |
| 472 | \r |
| 473 | /* Printer power on */\r |
| 474 | \r |
| 475 | t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc)\r |
| 476 | {\r |
| 477 | if (lps_unit.flags & UNIT_DIAG) { /* diag mode? */\r |
| 478 | lps_power = LPS_ON; /* no delay */\r |
| 479 | if (DEBUG_PRS (lps_dev))\r |
| 480 | fputs (">>LPS set: Power state is ON\n", sim_deb);\r |
| 481 | }\r |
| 482 | else {\r |
| 483 | lps_power = LPS_TURNING_ON; /* change state */\r |
| 484 | lps_unit.flags |= UNIT_OFFLINE; /* set offline */\r |
| 485 | sim_activate (&lps_unit, lps_rtime); /* schedule ready */\r |
| 486 | if (DEBUG_PRS (lps_dev)) fprintf (sim_deb,\r |
| 487 | ">>LPS set: Power state is TURNING ON, scheduled time = %d\n",\r |
| 488 | lps_rtime );\r |
| 489 | }\r |
| 490 | return SCPE_OK;\r |
| 491 | }\r |
| 492 | \r |
| 493 | /* Attach routine */\r |
| 494 | \r |
| 495 | t_stat lps_attach (UNIT *uptr, char *cptr)\r |
| 496 | {\r |
| 497 | lps_ccnt = lps_lcnt = 0; /* top of form */\r |
| 498 | lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */\r |
| 499 | return attach_unit (uptr, cptr);\r |
| 500 | }\r |
| 501 | \r |
| 502 | /* Set printer timing\r |
| 503 | \r |
| 504 | Realistic timing is factored, depending on CPU model, to account for the\r |
| 505 | timing method employed by the diagnostic. */\r |
| 506 | \r |
| 507 | t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 508 | {\r |
| 509 | uint32 i, factor = 1;\r |
| 510 | \r |
| 511 | lps_timing = (val != 0); /* determine choice */\r |
| 512 | if ((lps_timing == 0) && /* calc speed factor */\r |
| 513 | (UNIT_CPU_MODEL != UNIT_1000_E) &&\r |
| 514 | (UNIT_CPU_MODEL != UNIT_1000_F))\r |
| 515 | factor = 4;\r |
| 516 | for (i = 0; i < (sizeof (lps_timers) / sizeof (lps_timers[0])); i++)\r |
| 517 | *lps_timers[i] = lps_times[lps_timing][i] / factor; /* assign times */\r |
| 518 | return SCPE_OK;\r |
| 519 | }\r |
| 520 | \r |
| 521 | /* Show printer timing */\r |
| 522 | \r |
| 523 | t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 524 | {\r |
| 525 | if (lps_timing) fputs ("fast timing", st);\r |
| 526 | else fputs ("realistic timing", st);\r |
| 527 | return SCPE_OK;\r |
| 528 | }\r |