| 1 | /* id_lp.c: Interdata line printer\r |
| 2 | \r |
| 3 | Copyright (c) 2001-2008, 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 M46-206 line printer\r |
| 27 | \r |
| 28 | 27-May-08 RMS Fixed bug in printing test (from Davis Johnson)\r |
| 29 | 19-Jan-07 RMS Added UNIT_TEXT flag\r |
| 30 | 25-Apr-03 RMS Revised for extended file support\r |
| 31 | */\r |
| 32 | \r |
| 33 | #include "id_defs.h"\r |
| 34 | #include <ctype.h>\r |
| 35 | \r |
| 36 | /* Device definitions */\r |
| 37 | \r |
| 38 | #define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */\r |
| 39 | #define UNIT_UC (1 << UNIT_V_UC)\r |
| 40 | #define SPC_BASE 0x40 /* spacing base */\r |
| 41 | #define VFU_BASE 0x78 /* VFU base */\r |
| 42 | #define VFU_WIDTH 0x8 /* VFU width */\r |
| 43 | #define LF 0xA\r |
| 44 | #define VT 0xB\r |
| 45 | #define VT_VFU 4 /* VFU chan for VT */\r |
| 46 | #define FF 0xC\r |
| 47 | #define FF_VFU 8 /* VFU chan for FF */\r |
| 48 | #define CR 0xD\r |
| 49 | #define VFUP(ch,val) ((val) & (1 << (ch))) /* VFU chan test */\r |
| 50 | \r |
| 51 | /* Status byte, * = dynamic */\r |
| 52 | \r |
| 53 | #define STA_PAPE 0x40 /* *paper empty */\r |
| 54 | #define STA_MASK (STA_BSY) /* static status */\r |
| 55 | \r |
| 56 | uint32 lpt_sta = STA_BSY; /* status */\r |
| 57 | char lpxb[LPT_WIDTH + 1]; /* line buffer */\r |
| 58 | uint32 lpt_bptr = 0; /* buf ptr */\r |
| 59 | uint32 lpt_spnd = 0; /* space pending */\r |
| 60 | uint32 lpt_vfup = 0; /* VFU ptr */\r |
| 61 | uint32 lpt_vful = 1; /* VFU lnt */\r |
| 62 | uint8 lpt_vfut[VFU_LNT] = { 0xFF }; /* VFU tape */\r |
| 63 | uint32 lpt_arm = 0; /* int armed */\r |
| 64 | int32 lpt_ctime = 10; /* char time */\r |
| 65 | int32 lpt_stime = 1000; /* space time */\r |
| 66 | int32 lpt_stopioe = 0; /* stop on err */\r |
| 67 | \r |
| 68 | extern uint32 int_req[INTSZ], int_enb[INTSZ];\r |
| 69 | \r |
| 70 | DEVICE lpt_dev;\r |
| 71 | uint32 lpt (uint32 dev, uint32 op, uint32 dat);\r |
| 72 | t_stat lpt_svc (UNIT *uptr);\r |
| 73 | t_stat lpt_reset (DEVICE *dptr);\r |
| 74 | t_stat lpt_attach (UNIT *uptr, char *cptr);\r |
| 75 | t_stat lpt_bufout (UNIT *uptr);\r |
| 76 | t_stat lpt_vfu (UNIT *uptr, int32 ch);\r |
| 77 | t_stat lpt_spc (UNIT *uptr, int32 cnt);\r |
| 78 | \r |
| 79 | /* LPT data structures\r |
| 80 | \r |
| 81 | lpt_dev LPT device descriptor\r |
| 82 | lpt_unit LPT unit descriptors\r |
| 83 | lpt_reg LPT register list\r |
| 84 | */\r |
| 85 | \r |
| 86 | DIB lpt_dib = { d_LPT, -1, v_LPT, NULL, &lpt, NULL };\r |
| 87 | \r |
| 88 | UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_UC+UNIT_TEXT, 0) };\r |
| 89 | \r |
| 90 | REG lpt_reg[] = {\r |
| 91 | { HRDATA (STA, lpt_sta, 8) },\r |
| 92 | { HRDATA (BUF, lpt_unit.buf, 7) },\r |
| 93 | { BRDATA (DBUF, lpxb, 16, 7, LPT_WIDTH) },\r |
| 94 | { HRDATA (DBPTR, lpt_bptr, 8) },\r |
| 95 | { HRDATA (VFUP, lpt_vfup, 8) },\r |
| 96 | { HRDATA (VFUL, lpt_vful, 8) },\r |
| 97 | { BRDATA (VFUT, lpt_vfut, 16, 8, VFU_LNT) },\r |
| 98 | { FLDATA (IREQ, int_req[l_LPT], i_LPT) },\r |
| 99 | { FLDATA (IENB, int_enb[l_LPT], i_LPT) },\r |
| 100 | { FLDATA (IARM, lpt_arm, 0) },\r |
| 101 | { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT },\r |
| 102 | { DRDATA (CTIME, lpt_ctime, 24), PV_LEFT },\r |
| 103 | { DRDATA (STIME, lpt_stime, 24), PV_LEFT },\r |
| 104 | { FLDATA (STOP_IOE, lpt_stopioe, 0) },\r |
| 105 | { HRDATA (DEVNO, lpt_dib.dno, 8), REG_HRO },\r |
| 106 | { NULL }\r |
| 107 | };\r |
| 108 | \r |
| 109 | MTAB lpt_mod[] = {\r |
| 110 | { UNIT_UC, 0, "lower case", "LC", NULL },\r |
| 111 | { UNIT_UC, UNIT_UC, "upper case", "UC", NULL },\r |
| 112 | { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",\r |
| 113 | &set_dev, &show_dev, NULL },\r |
| 114 | { 0 }\r |
| 115 | };\r |
| 116 | \r |
| 117 | DEVICE lpt_dev = {\r |
| 118 | "LPT", &lpt_unit, lpt_reg, lpt_mod,\r |
| 119 | 1, 10, 31, 1, 16, 7,\r |
| 120 | NULL, NULL, &lpt_reset,\r |
| 121 | NULL, &lpt_attach, NULL,\r |
| 122 | &lpt_dib, DEV_DISABLE\r |
| 123 | };\r |
| 124 | \r |
| 125 | /* Line printer: IO routine */\r |
| 126 | \r |
| 127 | uint32 lpt (uint32 dev, uint32 op, uint32 dat)\r |
| 128 | {\r |
| 129 | int32 t;\r |
| 130 | \r |
| 131 | switch (op) { /* case IO op */\r |
| 132 | \r |
| 133 | case IO_ADR: /* select */\r |
| 134 | return BY; /* byte only */\r |
| 135 | \r |
| 136 | case IO_OC: /* command */\r |
| 137 | lpt_arm = int_chg (v_LPT, dat, lpt_arm); /* upd int ctrl */\r |
| 138 | break;\r |
| 139 | \r |
| 140 | case IO_WD: /* write */\r |
| 141 | t = lpt_unit.buf = dat & 0x7F; /* mask char */\r |
| 142 | lpt_sta = STA_BSY; /* set busy */\r |
| 143 | if (lpt_spnd || ((t >= LF) && (t <= CR))) /* space op? */\r |
| 144 | sim_activate (&lpt_unit, lpt_stime);\r |
| 145 | else sim_activate (&lpt_unit, lpt_ctime); /* normal char */\r |
| 146 | break;\r |
| 147 | \r |
| 148 | case IO_SS: /* status */\r |
| 149 | t = lpt_sta & STA_MASK; /* status byte */\r |
| 150 | if ((lpt_unit.flags & UNIT_ATT) == 0) /* test paper out */\r |
| 151 | t = t | STA_EX | STA_PAPE | STA_BSY;\r |
| 152 | return t;\r |
| 153 | }\r |
| 154 | \r |
| 155 | return 0;\r |
| 156 | }\r |
| 157 | \r |
| 158 | /* Unit service */\r |
| 159 | \r |
| 160 | t_stat lpt_svc (UNIT *uptr)\r |
| 161 | {\r |
| 162 | int32 t;\r |
| 163 | t_stat r = SCPE_OK;\r |
| 164 | \r |
| 165 | lpt_sta = 0; /* clear busy */\r |
| 166 | if (lpt_arm) SET_INT (v_LPT); /* armed? intr */\r |
| 167 | if ((uptr->flags & UNIT_ATT) == 0) /* attached? */\r |
| 168 | return IORETURN (lpt_stopioe, SCPE_UNATT);\r |
| 169 | t = uptr->buf; /* get character */\r |
| 170 | if (lpt_spnd || ((t >= LF) && (t < CR))) { /* spc pend or spc op? */\r |
| 171 | lpt_spnd = 0;\r |
| 172 | if (lpt_bufout (uptr) != SCPE_OK) /* print */\r |
| 173 | return SCPE_IOERR;\r |
| 174 | if ((t == 1) || (t == LF)) lpt_spc (uptr, 1); /* single space */\r |
| 175 | else if (t == VT) r = lpt_vfu (uptr, VT_VFU - 1); /* VT->VFU */\r |
| 176 | else if (t == 0xC) r = lpt_vfu (uptr, FF_VFU - 1); /* FF->VFU */\r |
| 177 | else if ((t >= SPC_BASE) && (t < VFU_BASE))\r |
| 178 | lpt_spc (uptr, t - SPC_BASE); /* space */\r |
| 179 | else if ((t >= VFU_BASE) && (t < VFU_BASE + VFU_WIDTH))\r |
| 180 | r = lpt_vfu (uptr, t - VFU_BASE); /* VFU */\r |
| 181 | else fputs ("\r", uptr->fileref); /* overprint */\r |
| 182 | uptr->pos = ftell (uptr->fileref); /* update position */\r |
| 183 | if (ferror (lpt_unit.fileref)) {\r |
| 184 | perror ("LPT I/O error");\r |
| 185 | clearerr (uptr->fileref);\r |
| 186 | return SCPE_IOERR;\r |
| 187 | }\r |
| 188 | }\r |
| 189 | else if (t == CR) { /* CR? */\r |
| 190 | lpt_spnd = 1; /* set spc pend */\r |
| 191 | return lpt_bufout (uptr); /* print line */\r |
| 192 | }\r |
| 193 | else if (t >= 0x20) { /* printable? */\r |
| 194 | if ((uptr->flags & UNIT_UC) && islower (t)) /* UC only? */\r |
| 195 | t = toupper (t);\r |
| 196 | if (lpt_bptr < LPT_WIDTH) lpxb[lpt_bptr++] = t;\r |
| 197 | }\r |
| 198 | return r;\r |
| 199 | }\r |
| 200 | \r |
| 201 | /* Printing and spacing routines */\r |
| 202 | \r |
| 203 | t_stat lpt_bufout (UNIT *uptr)\r |
| 204 | {\r |
| 205 | int32 i;\r |
| 206 | t_stat r = SCPE_OK;\r |
| 207 | \r |
| 208 | if (lpt_bptr == 0) return SCPE_OK; /* any char in buf? */\r |
| 209 | for (i = LPT_WIDTH - 1; (i >= 0) && (lpxb[i] == ' '); i--)\r |
| 210 | lpxb[i] = 0; /* backscan line */\r |
| 211 | if (lpxb[0]) { /* any char left? */\r |
| 212 | fputs (lpxb, uptr->fileref); /* write line */\r |
| 213 | lpt_unit.pos = ftell (uptr->fileref); /* update position */\r |
| 214 | if (ferror (uptr->fileref)) {\r |
| 215 | perror ("LPT I/O error");\r |
| 216 | clearerr (uptr->fileref);\r |
| 217 | r = SCPE_IOERR;\r |
| 218 | }\r |
| 219 | } \r |
| 220 | lpt_bptr = 0; /* reset buffer */\r |
| 221 | for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' ';\r |
| 222 | lpxb[LPT_WIDTH] = 0;\r |
| 223 | return r;\r |
| 224 | }\r |
| 225 | \r |
| 226 | t_stat lpt_vfu (UNIT *uptr, int32 ch)\r |
| 227 | {\r |
| 228 | uint32 i, j;\r |
| 229 | \r |
| 230 | if ((ch == (FF_VFU - 1)) && VFUP (ch, lpt_vfut[0])) { /* top of form? */\r |
| 231 | fputs ("\n\f", uptr->fileref); /* nl + ff */\r |
| 232 | lpt_vfup = 0; /* top of page */\r |
| 233 | return SCPE_OK;\r |
| 234 | }\r |
| 235 | for (i = 1; i < lpt_vful + 1; i++) { /* sweep thru cct */\r |
| 236 | lpt_vfup = (lpt_vfup + 1) % lpt_vful; /* adv pointer */\r |
| 237 | if (VFUP (ch, lpt_vfut[lpt_vfup])) { /* chan punched? */\r |
| 238 | for (j = 0; j < i; j++) fputc ('\n', uptr->fileref);\r |
| 239 | return SCPE_OK;\r |
| 240 | }\r |
| 241 | }\r |
| 242 | return STOP_VFU; /* runaway channel */\r |
| 243 | }\r |
| 244 | \r |
| 245 | t_stat lpt_spc (UNIT *uptr, int32 cnt)\r |
| 246 | {\r |
| 247 | int32 i;\r |
| 248 | \r |
| 249 | if (cnt == 0) fputc ('\r', uptr->fileref);\r |
| 250 | else {\r |
| 251 | for (i = 0; i < cnt; i++) fputc ('\n', uptr->fileref);\r |
| 252 | lpt_vfup = (lpt_vfup + cnt) % lpt_vful;\r |
| 253 | }\r |
| 254 | return SCPE_OK;\r |
| 255 | }\r |
| 256 | \r |
| 257 | /* Reset routine */\r |
| 258 | \r |
| 259 | t_stat lpt_reset (DEVICE *dptr)\r |
| 260 | {\r |
| 261 | int32 i;\r |
| 262 | \r |
| 263 | sim_cancel (&lpt_unit); /* deactivate */\r |
| 264 | lpt_sta = 0; /* clr busy */\r |
| 265 | lpt_bptr = 0; /* clr buf ptr */\r |
| 266 | for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' '; /* clr buf */\r |
| 267 | lpxb[LPT_WIDTH] = 0;\r |
| 268 | CLR_INT (v_LPT); /* clearr int */\r |
| 269 | CLR_ENB (v_LPT); /* disable int */\r |
| 270 | lpt_arm = 0; /* disarm int */\r |
| 271 | return SCPE_OK;\r |
| 272 | }\r |
| 273 | \r |
| 274 | /* Attach routine */\r |
| 275 | \r |
| 276 | t_stat lpt_attach (UNIT *uptr, char *cptr)\r |
| 277 | {\r |
| 278 | lpt_vfup = 0; /* top of form */\r |
| 279 | return attach_unit (uptr, cptr);\r |
| 280 | }\r |
| 281 | \r |
| 282 | /* Carriage control load routine */\r |
| 283 | \r |
| 284 | t_stat lp_load (FILE *fileref, char *cptr, char *fnam)\r |
| 285 | {\r |
| 286 | int32 col, ptr, mask, vfubuf[VFU_LNT];\r |
| 287 | uint32 rpt;\r |
| 288 | t_stat r;\r |
| 289 | char cbuf[CBUFSIZE], gbuf[CBUFSIZE];\r |
| 290 | \r |
| 291 | if (*cptr != 0) return SCPE_ARG;\r |
| 292 | ptr = 0;\r |
| 293 | for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */\r |
| 294 | mask = 0;\r |
| 295 | if (*cptr == '(') { /* repeat count? */\r |
| 296 | cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */\r |
| 297 | rpt = get_uint (gbuf, 10, VFU_LNT, &r); /* repeat count */\r |
| 298 | if (r != SCPE_OK) return SCPE_FMT;\r |
| 299 | }\r |
| 300 | else rpt = 1;\r |
| 301 | while (*cptr != 0) { /* get col no's */\r |
| 302 | cptr = get_glyph (cptr, gbuf, ','); /* get next field */\r |
| 303 | col = get_uint (gbuf, 10, 7, &r); /* column number */\r |
| 304 | if (r != SCPE_OK) return SCPE_FMT;\r |
| 305 | mask = mask | (1 << col); /* set bit */\r |
| 306 | }\r |
| 307 | for ( ; rpt > 0; rpt--) { /* store vals */\r |
| 308 | if (ptr >= VFU_LNT) return SCPE_FMT;\r |
| 309 | vfubuf[ptr++] = mask;\r |
| 310 | }\r |
| 311 | }\r |
| 312 | if (ptr == 0) return SCPE_FMT;\r |
| 313 | lpt_vful = ptr;\r |
| 314 | lpt_vfup = 0;\r |
| 315 | for (rpt = 0; rpt < lpt_vful; rpt++) lpt_vfut[rpt] = vfubuf[rpt];\r |
| 316 | return SCPE_OK;\r |
| 317 | }\r |