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