| /* s3_lp.c: IBM 1403 line printer simulator | |
| Copyright (c) 2001-2005, Charles E. Owen | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Charles E. Owen shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Charles E. Owen. | |
| lpt 1403 line printer | |
| 25-Apr-03 RMS Revised for extended file support | |
| 08-Oct-02 RMS Added impossible function catcher | |
| */ | |
| #include "s3_defs.h" | |
| extern uint8 M[]; | |
| extern char bcd_to_ascii[64]; | |
| extern int32 iochk, ind[64]; | |
| int32 cct[CCT_LNT] = { 03 }; | |
| int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; | |
| t_stat lpt_reset (DEVICE *dptr); | |
| t_stat lpt_attach (UNIT *uptr, char *cptr); | |
| t_stat write_line (int32 ilnt, int32 mod); | |
| t_stat space (int32 lines, int32 lflag); | |
| t_stat carriage_control (int32 action, int32 mod); | |
| extern unsigned char ebcdic_to_ascii[256]; | |
| #define UNIT_V_PCHAIN (UNIT_V_UF + 0) | |
| #define UNIT_M_PCHAIN 03 | |
| #define M_UCS 00 /* Universal */ | |
| #define M_PCF 00 /* full */ | |
| #define M_PCA 01 /* business */ | |
| #define M_PCH 02 /* Fortran */ | |
| #define UNIT_PCHAIN (UNIT_M_PCHAIN << UNIT_V_PCHAIN) | |
| #define UCS (M_UCS << UNIT_V_PCHAIN) | |
| #define PCF (M_PCF << UNIT_V_PCHAIN) | |
| #define PCA (M_PCA << UNIT_V_PCHAIN) | |
| #define PCH (M_PCH << UNIT_V_PCHAIN) | |
| #define GET_PCHAIN(x) (((x) >> UNIT_V_PCHAIN) & UNIT_M_PCHAIN) | |
| #define CHP(ch,val) ((val) & (1 << (ch))) | |
| int32 LPDAR; /* Data Address */ | |
| int32 LPFLR; /* Forms Length */ | |
| int32 LPIAR; /* Image address */ | |
| int32 linectr; /* current line # */ | |
| int32 lpterror = 0; | |
| int32 CC9 = 0; | |
| int32 CC12 = 0; | |
| /* LPT data structures | |
| lpt_dev LPT device descriptor | |
| lpt_unit LPT unit descriptor | |
| lpt_reg LPT register list | |
| */ | |
| UNIT lpt_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; | |
| REG lpt_reg[] = { | |
| { FLDATA (ERR, lpterror, 0) }, | |
| { HRDATA (LPDAR, LPDAR, 16) }, | |
| { HRDATA (LPFLR, LPFLR, 8) }, | |
| { HRDATA (LPIAR, LPIAR, 16) }, | |
| { DRDATA (LINECT, linectr, 8) }, | |
| { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, | |
| { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, | |
| { DRDATA (LINES, lines, 8), PV_LEFT }, | |
| { DRDATA (CCTP, cctptr, 8), PV_LEFT }, | |
| { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, | |
| { GRDATA (CHAIN, lpt_unit.flags, 10, 2, UNIT_V_PCHAIN), REG_HRO }, | |
| { NULL } | |
| }; | |
| MTAB lpt_mod[] = { | |
| { UNIT_PCHAIN, UCS, "UCS", "UCS", NULL }, | |
| { UNIT_PCHAIN, PCA, "A chain", "PCA", NULL }, | |
| { UNIT_PCHAIN, PCH, "H chain", "PCH", NULL }, | |
| { 0 } | |
| }; | |
| DEVICE lpt_dev = { | |
| "LPT", &lpt_unit, lpt_reg, lpt_mod, | |
| 1, 10, 31, 1, 8, 7, | |
| NULL, NULL, &lpt_reset, | |
| NULL, NULL, NULL | |
| }; | |
| /* -------------------------------------------------------------------- */ | |
| /* Printer: master routine */ | |
| int32 lpt (int32 op, int32 m, int32 n, int32 data) | |
| { | |
| int32 iodata; | |
| switch (op) { | |
| case 0: /* SIO 1403 */ | |
| iodata = 0; | |
| printf("\0"); | |
| switch (n) { | |
| case 0x00: /* Spacing only */ | |
| if (data > 0 && data < 4) | |
| iodata = carriage_control(2, data); | |
| break; | |
| case 0x02: /* Print & space */ | |
| iodata = write_line(0, 0); | |
| if (data > 3) data = 0; | |
| if (iodata == SCPE_OK) | |
| iodata = carriage_control(2, data); | |
| break; | |
| case 0x04: /* Skip only */ | |
| iodata = carriage_control(4, data); | |
| break; | |
| case 0x06: /* Print and skip */ | |
| iodata = write_line(0, 0); | |
| if (iodata == SCPE_OK) | |
| iodata = carriage_control(4, data); | |
| break; | |
| default: | |
| return STOP_INVDEV; | |
| } | |
| return iodata; | |
| case 1: /* LIO 1403 */ | |
| switch (n) { | |
| case 0x00: /* LPFLR */ | |
| LPFLR = (data >> 8) & 0xff; | |
| break; | |
| case 0x04: | |
| LPIAR = data & 0xffff; | |
| break; | |
| case 0x06: | |
| LPDAR = data & 0xffff; | |
| break; | |
| default: | |
| return STOP_INVDEV; | |
| } | |
| return SCPE_OK; | |
| case 2: /* TIO 1403 */ | |
| iodata = 0; | |
| switch (n) { | |
| case 0x00: /* Not ready/check */ | |
| if (lpterror) | |
| iodata = 1; | |
| if ((lpt_unit.flags & UNIT_ATT) == 0) | |
| iodata = 1; | |
| break; | |
| case 0x02: /* Buffer Busy */ | |
| iodata = 0; | |
| break; | |
| case 0x04: /* Carriage Busy */ | |
| iodata = 0; | |
| break; | |
| case 0x06: /* Printer busy */ | |
| iodata = 0; | |
| break; | |
| default: | |
| return (STOP_INVDEV << 16); | |
| } | |
| return ((SCPE_OK << 16) | iodata); | |
| case 3: /* SNS 1403 */ | |
| switch (n) { | |
| case 0x00: /* Line count */ | |
| iodata = (linectr << 8); | |
| break; | |
| case 0x02: /* Timing data */ | |
| iodata = 0; | |
| break; | |
| case 0x03: /* Check data */ | |
| iodata = 0; | |
| break; | |
| case 0x04: /* LPIAR */ | |
| iodata = LPIAR; | |
| break; | |
| case 0x06: /* LPDAR */ | |
| iodata = LPDAR; | |
| break; | |
| default: | |
| return (STOP_INVDEV << 16); | |
| } | |
| return ((SCPE_OK << 16) | iodata); | |
| case 4: /* APL 1403 */ | |
| iodata = 0; | |
| return ((SCPE_OK << 16) | iodata); | |
| default: | |
| break; | |
| } | |
| printf (">>LPT non-existent function %d\n", op); | |
| return SCPE_OK; | |
| } | |
| /* Print routine | |
| Modifiers have been checked by the caller | |
| S = suppress automatic newline | |
| */ | |
| t_stat write_line (int32 ilnt, int32 mod) | |
| { | |
| int32 i, t, lc; | |
| static char lbuf[LPT_WIDTH + 1]; /* + null */ | |
| if ((lpt_unit.flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; | |
| lpterror = 0; | |
| lc = LPDAR; /* clear error */ | |
| for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ | |
| t = M[lc]; | |
| lbuf[i] = ebcdic_to_ascii[t & 0xff]; | |
| M[lc] = 0x40; /* HJS MOD */ | |
| lc++; | |
| } | |
| for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; | |
| fputs (lbuf, lpt_unit.fileref); /* write line */ | |
| if (lines) space (lines, lflag); /* cc action? do it */ | |
| else if (mod == 0) space (1, FALSE); /* default? 1 line */ | |
| else { | |
| fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ | |
| lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ | |
| } | |
| lines = lflag = 0; /* clear cc action */ | |
| if (ferror (lpt_unit.fileref)) { /* error? */ | |
| perror ("Line printer I/O error"); | |
| clearerr (lpt_unit.fileref); | |
| lpterror = 1; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Carriage control routine | |
| Parameters: | |
| action = 00, skip to channel now | |
| = 01, space lines after | |
| = 02, space lines now | |
| = 03, skip to channel after | |
| = 04, skip to line number | |
| mod = number of lines or channel number or line number | |
| */ | |
| t_stat carriage_control (int32 action, int32 mod) | |
| { | |
| int32 i; | |
| if ((lpt_unit.flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; | |
| switch (action) { | |
| case 0: /* to channel now */ | |
| if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; | |
| for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ | |
| if (CHP (mod, cct[(cctptr + i) % cctlnt])) | |
| return space (i, TRUE); | |
| } | |
| return STOP_INVDEV; /* runaway channel */ | |
| case 1: /* space after */ | |
| if (mod <= 3) { | |
| lines = mod; /* save # lines */ | |
| lflag = FALSE; /* flag spacing */ | |
| CC9 = CC12 = 0; | |
| } | |
| return SCPE_OK; | |
| case 2: /* space now */ | |
| if (mod <= 3) return space (mod, FALSE); | |
| return SCPE_OK; | |
| case 3: /* to channel after */ | |
| if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */ | |
| CC9 = CC12 = 0; | |
| for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ | |
| if (CHP (mod, cct[(cctptr + i) % cctlnt])) { | |
| lines = i; /* save # lines */ | |
| lflag = TRUE; /* flag skipping */ | |
| return SCPE_OK; | |
| } | |
| } | |
| return STOP_INVDEV; | |
| case 4: /* To line # */ | |
| if (mod < 2) { | |
| fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ | |
| linectr = 1; | |
| } else { | |
| if (mod <= linectr) { | |
| fputs ("\n\f", lpt_unit.fileref); | |
| linectr = 1; | |
| } | |
| while (1) { | |
| if (linectr == mod) | |
| break; | |
| space(1, 0); | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Space routine - space or skip n lines | |
| Inputs: | |
| count = number of lines to space or skip | |
| sflag = skip (TRUE) or space (FALSE) | |
| */ | |
| t_stat space (int32 count, int32 sflag) | |
| { | |
| int32 i; | |
| if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; | |
| cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ | |
| if (sflag && CHP (0, cct[cctptr])) { /* skip, top of form? */ | |
| fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ | |
| linectr = 1; | |
| } else { | |
| for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); | |
| } | |
| lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ | |
| CC9 = CHP (9, cct[cctptr]) != 0; /* set indicators */ | |
| CC12 = CHP (12, cct[cctptr]) != 0; | |
| linectr += count; | |
| if (linectr > LPFLR) | |
| linectr -= LPFLR; | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat lpt_reset (DEVICE *dptr) | |
| { | |
| cctptr = 0; /* clear cct ptr */ | |
| lines = linectr = lflag = 0; /* no cc action */ | |
| lpterror = 0; | |
| return SCPE_OK; | |
| } | |
| /* Attach routine */ | |
| t_stat lpt_attach (UNIT *uptr, char *cptr) | |
| { | |
| cctptr = 0; /* clear cct ptr */ | |
| lines = 0; /* no cc action */ | |
| lpterror = 0; | |
| linectr = 0; | |
| return attach_unit (uptr, cptr); | |
| } |