| /* b5500_urec.c: Burrioughs 5500 Unit record devices. | |
| Copyright (c) 2016, Richard Cornwell | |
| 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 | |
| RICHARD CORNWELL 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. | |
| This is the standard card reader. 10,14 | |
| This is the standard card punch. 10 | |
| This is the standard line printer. 22,26 | |
| This is the standard operators console. 30 | |
| */ | |
| #include "b5500_defs.h" | |
| #include "sim_card.h" | |
| #include "sim_defs.h" | |
| #include "sim_console.h" | |
| #define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | UNIT_ROABLE | \ | |
| MODE_029 | |
| #define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_029 | |
| #define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE | |
| /* For Card reader, when set returns end of file at end of deck. */ | |
| /* Reset after sent to system */ | |
| #define MODE_EOF (0x40 << UNIT_V_MODE) | |
| /* std devices. data structures | |
| cdr_dev Card Reader device descriptor | |
| cdr_unit Card Reader unit descriptor | |
| cdr_reg Card Reader register list | |
| cdr_mod Card Reader modifiers list | |
| */ | |
| /* Device status information stored in u5 */ | |
| #define URCSTA_CHMASK 0003 /* Mask of I/O channel to send data on */ | |
| #define URCSTA_CARD 0004 /* Unit has card in buffer */ | |
| #define URCSTA_FULL 0004 /* Unit has full buffer */ | |
| #define URCSTA_BUSY 0010 /* Device is busy */ | |
| #define URCSTA_BIN 0020 /* Card reader in binary mode */ | |
| #define URCSTA_ACTIVE 0040 /* Unit is active */ | |
| #define URCSTA_EOF 0100 /* Flag the end of file */ | |
| #define URCSTA_INPUT 0200 /* Console fill buffer from keyboard */ | |
| #define URCSTA_FILL 010000 /* Fill unit buffer */ | |
| #define URCSTA_CMD_V 16 | |
| #define URCSTA_SKIP 000017 /* Skip mask */ | |
| #define URCSTA_DOUBLE 000020 /* Double space skip */ | |
| #define URCSTA_SINGLE 000040 /* Single space skip. */ | |
| #define URCSTA_READ 000400 /* Read flag */ | |
| #define URCSTA_WC 001000 /* Use word count */ | |
| #define URCSTA_DIRECT 002000 /* Direction, Long line */ | |
| #define URCSTA_BINARY 004000 /* Binary transfer */ | |
| #define URCSTA_INHIBIT 040000 /* Inhibit transfer to memory */ | |
| /* Simulator debug controls */ | |
| DEBTAB cdr_debug[] = { | |
| {"CMD", DEBUG_CMD, "Show command execution to devices"}, | |
| {"DATA", DEBUG_DATA, "Show data transfers"}, | |
| {"DETAIL", DEBUG_DETAIL, "Show details about device"}, | |
| {"EXP", DEBUG_EXP, "Show console data"}, | |
| {"CARD", DEBUG_CARD, "Show Card read/punches"}, | |
| {0, 0} | |
| }; | |
| #if NUM_DEVS_CDR > 0 | |
| t_stat cdr_boot(int32, DEVICE *); | |
| t_stat cdr_srv(UNIT *); | |
| t_stat cdr_attach(UNIT *, char *); | |
| t_stat cdr_detach(UNIT *); | |
| t_stat cdr_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
| const char *cdr_description(DEVICE *dptr); | |
| #endif | |
| #if NUM_DEVS_CDP > 0 | |
| t_stat cdp_srv(UNIT *); | |
| t_stat cdp_attach(UNIT *, char *); | |
| t_stat cdp_detach(UNIT *); | |
| t_stat cdp_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
| const char *cdp_description(DEVICE *dptr); | |
| #endif | |
| #if NUM_DEVS_LPR > 0 | |
| struct _lpr_data | |
| { | |
| uint8 lbuff[145]; /* Output line buffer */ | |
| } | |
| lpr_data[NUM_DEVS_LPR]; | |
| t_stat lpr_srv(UNIT *); | |
| t_stat lpr_attach(UNIT *, char *); | |
| t_stat lpr_detach(UNIT *); | |
| t_stat lpr_setlpp(UNIT *, int32, char *, void *); | |
| t_stat lpr_getlpp(FILE *, UNIT *, int32, void *); | |
| t_stat lpr_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
| const char *lpr_description(DEVICE *dptr); | |
| #endif | |
| #if NUM_DEVS_CON > 0 | |
| struct _con_data | |
| { | |
| uint8 ibuff[145]; /* Input line buffer */ | |
| uint8 inptr; | |
| uint8 outptr; | |
| } | |
| con_data[NUM_DEVS_CON]; | |
| t_stat con_ini(DEVICE *); | |
| t_stat con_srv(UNIT *); | |
| t_stat con_attach(UNIT *, char *); | |
| t_stat con_detach(UNIT *); | |
| t_stat con_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
| const char *con_description(DEVICE *dptr); | |
| #endif | |
| #if NUM_DEVS_CDR > 0 | |
| UNIT cdr_unit[] = { | |
| {UDATA(cdr_srv, UNIT_CDR, 0)}, /* A */ | |
| #if NUM_DEVS_CDR > 1 | |
| {UDATA(cdr_srv, UNIT_CDR|UNIT_DIS, 0)}, /* B */ | |
| #endif | |
| }; | |
| MTAB cdr_mod[] = { | |
| {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| &sim_card_set_fmt, &sim_card_show_fmt, NULL, | |
| "Sets card format"}, | |
| {MODE_EOF, MODE_EOF, "EOF", "EOF", NULL, NULL, | |
| "Causes EOF to be set when reader empty"}, | |
| {0} | |
| }; | |
| DEVICE cdr_dev = { | |
| "CR", cdr_unit, NULL, cdr_mod, | |
| NUM_DEVS_CDR, 8, 15, 1, 8, 8, | |
| NULL, NULL, NULL, &cdr_boot, &cdr_attach, &cdr_detach, | |
| NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, | |
| NULL, NULL, &cdr_help, NULL, NULL, | |
| &cdr_description | |
| }; | |
| #endif | |
| #if NUM_DEVS_CDP > 0 | |
| UNIT cdp_unit[] = { | |
| {UDATA(cdp_srv, UNIT_CDP, 0)}, /* A */ | |
| }; | |
| MTAB cdp_mod[] = { | |
| {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| &sim_card_set_fmt, &sim_card_show_fmt, NULL, | |
| "Sets card format"}, | |
| {0} | |
| }; | |
| DEVICE cdp_dev = { | |
| "CP", cdp_unit, NULL, cdp_mod, | |
| NUM_DEVS_CDP, 8, 15, 1, 8, 8, | |
| NULL, NULL, NULL, NULL, &cdp_attach, &cdp_detach, | |
| NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, | |
| NULL, NULL, &cdp_help, NULL, NULL, | |
| &cdp_description | |
| }; | |
| #endif | |
| #if NUM_DEVS_LPR > 0 | |
| UNIT lpr_unit[] = { | |
| {UDATA(lpr_srv, UNIT_LPR, 59)}, /* A */ | |
| #if NUM_DEVS_LPR > 1 | |
| {UDATA(lpr_srv, UNIT_LPR|UNIT_DIS, 59)}, /* B */ | |
| #endif | |
| }; | |
| MTAB lpr_mod[] = { | |
| {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", | |
| &lpr_setlpp, &lpr_getlpp, NULL, | |
| "Sets number of lines on a printed page"}, | |
| {0} | |
| }; | |
| DEVICE lpr_dev = { | |
| "LP", lpr_unit, NULL, lpr_mod, | |
| NUM_DEVS_LPR, 8, 15, 1, 8, 8, | |
| NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, | |
| NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &lpr_help, NULL, NULL, | |
| &lpr_description | |
| }; | |
| #endif | |
| #if NUM_DEVS_CON > 0 | |
| UNIT con_unit[] = { | |
| {UDATA(con_srv, 0, 0), 0}, /* A */ | |
| }; | |
| DEVICE con_dev = { | |
| "CON", con_unit, NULL, NULL, | |
| NUM_DEVS_CON, 8, 15, 1, 8, 8, | |
| NULL, NULL, con_ini, NULL, NULL, NULL, | |
| NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &con_help, NULL, NULL, | |
| &con_description | |
| }; | |
| #endif | |
| #if ((NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0)) | |
| /* | |
| * Device entry points for card reader. | |
| * And Card punch. | |
| */ | |
| t_stat card_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) | |
| { | |
| UNIT *uptr; | |
| int u; | |
| if (dev == CARD1_DEV) | |
| u = 0; | |
| else if (dev == CARD2_DEV) | |
| u = 1; | |
| else | |
| return SCPE_NXDEV; | |
| /* Check if card reader or card punch */ | |
| if (cmd & URCSTA_READ) { | |
| uptr = &cdr_unit[u]; | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; | |
| /* Are we currently tranfering? */ | |
| if (uptr->u5 & URCSTA_ACTIVE) | |
| return SCPE_BUSY; | |
| /* Check if we ran out of cards */ | |
| if (uptr->u5 & URCSTA_EOF) { | |
| /* If end of file, return to system */ | |
| if (uptr->flags & MODE_EOF) { | |
| sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d report eof\n", u, | |
| chan); | |
| chan_set_eof(chan); | |
| uptr->flags &= ~MODE_EOF; | |
| } | |
| /* Clear unit ready */ | |
| iostatus &= ~(CARD1_FLAG << u); | |
| return SCPE_UNATT; | |
| } | |
| if (cmd & URCSTA_BINARY) { | |
| uptr->u5 |= URCSTA_BIN; | |
| *wc = 20; | |
| } else { | |
| uptr->u5 &= ~URCSTA_BIN; | |
| *wc = 10; | |
| } | |
| uptr->u5 &= ~URCSTA_CHMASK; | |
| uptr->u5 |= URCSTA_ACTIVE|chan; | |
| uptr->u4 = 0; | |
| sim_activate(uptr, 500000); | |
| return SCPE_OK; | |
| } else { | |
| /* Talking to punch */ | |
| if (u != 0) | |
| return SCPE_NXDEV; | |
| sim_debug(DEBUG_DETAIL, &cdr_dev, "cdp %d %d start\n", u, chan); | |
| uptr = &cdp_unit[0]; | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; | |
| if (uptr->u5 & URCSTA_ACTIVE) | |
| return SCPE_BUSY; | |
| uptr->u5 &= ~URCSTA_CHMASK; | |
| uptr->u5 |= URCSTA_ACTIVE|chan; | |
| uptr->u4 = 0; | |
| *wc = 10; | |
| sim_activate(uptr, 500000); | |
| sim_debug(DEBUG_DETAIL, &cdr_dev, "cdp %d %d go\n", u, chan); | |
| return SCPE_OK; | |
| } | |
| return SCPE_IOERR; | |
| } | |
| /* Handle transfer of data for card reader */ | |
| t_stat | |
| cdr_srv(UNIT *uptr) { | |
| int chan = URCSTA_CHMASK & uptr->u5; | |
| int u = (uptr - cdr_unit); | |
| if (uptr->u5 & URCSTA_EOF) { | |
| sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d unready\n", u, chan); | |
| iostatus &= ~(CARD1_FLAG << u); | |
| uptr->u5 &= ~ URCSTA_EOF; | |
| return SCPE_OK; | |
| } | |
| /* Check if new card requested. */ | |
| if (uptr->u4 == 0 && uptr->u5 & URCSTA_ACTIVE && | |
| (uptr->u5 & URCSTA_CARD) == 0) { | |
| switch(sim_read_card(uptr)) { | |
| case SCPE_UNATT: | |
| iostatus &= ~(CARD1_FLAG << u); | |
| uptr->u5 &= ~(URCSTA_ACTIVE); | |
| iostatus &= ~(CARD1_FLAG << u); | |
| chan_set_notrdy(chan); | |
| break; | |
| case SCPE_EOF: | |
| /* If end of file, return to system */ | |
| if (uptr->flags & MODE_EOF) { | |
| sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d set eof\n", u, chan); | |
| chan_set_eof(chan); | |
| uptr->flags &= ~MODE_EOF; | |
| } | |
| uptr->u5 &= ~(URCSTA_ACTIVE); | |
| uptr->u5 |= URCSTA_EOF; | |
| chan_set_notrdy(chan); | |
| sim_activate(uptr, 500); | |
| break; | |
| case SCPE_IOERR: | |
| chan_set_error(chan); | |
| uptr->u5 &= ~(URCSTA_ACTIVE); | |
| uptr->u5 |= URCSTA_EOF; | |
| chan_set_end(chan); | |
| break; | |
| case SCPE_OK: | |
| uptr->u5 |= URCSTA_CARD; | |
| sim_activate(uptr, 500); | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Copy next column over */ | |
| if (uptr->u5 & URCSTA_CARD && | |
| uptr->u4 <= ((uptr->u5 & URCSTA_BIN) ? 160 : 80)) { | |
| struct _card_data *data; | |
| uint8 ch = 0; | |
| int u = (uptr - cdr_unit); | |
| data = (struct _card_data *)uptr->up7; | |
| if (uptr->u5 & URCSTA_BIN) { | |
| ch = (data->image[uptr->u4 >> 1] >> | |
| ((uptr->u4 & 1)? 0 : 6)) & 077; | |
| } else { | |
| ch = sim_hol_to_bcd(data->image[uptr->u4]); | |
| /* Remap some characters from 029 to BCL */ | |
| switch(ch) { | |
| case 0: ch = 020; break; /* Translate blanks */ | |
| case 10: /* Check if 0 punch of 82 punch */ | |
| if (data->image[uptr->u4] != 0x200) { | |
| ch = 0; | |
| if (uptr->u4 == 0) | |
| chan_set_parity(chan); | |
| } | |
| break; | |
| case 0111: | |
| ch = 0; | |
| /* Handle invalid punch */ | |
| chan_set_parity(chan); | |
| break; /* Translate ? to error*/ | |
| } | |
| } | |
| if(chan_write_char(chan, &ch, 0)) { | |
| uptr->u5 &= ~(URCSTA_ACTIVE|URCSTA_CARD); | |
| chan_set_end(chan); | |
| /* Drop ready a bit after the last card is read */ | |
| if (sim_card_eof(uptr)) { | |
| uptr->u5 |= URCSTA_EOF; | |
| sim_activate(uptr, 100); | |
| } | |
| } else { | |
| uptr->u4++; | |
| sim_activate(uptr, 100); | |
| } | |
| sim_debug(DEBUG_DATA, &cdr_dev, "cdr %d: Char > %03o '%c' %d\n", u, ch, | |
| sim_six_to_ascii[ch & 077], uptr->u4); | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Boot from given device */ | |
| t_stat | |
| cdr_boot(int32 unit_num, DEVICE * dptr) | |
| { | |
| UNIT *uptr = &dptr->units[unit_num]; | |
| uint8 dev; | |
| t_uint64 desc; | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; /* attached? */ | |
| dev = (uptr == &cdr_unit[0]) ? CARD1_DEV : CARD2_DEV; | |
| uptr->u5 &= ~URCSTA_ACTIVE; | |
| desc = ((t_uint64)dev) << DEV_V | DEV_IORD| DEV_BIN | 020LL; | |
| /* Read in one record */ | |
| return chan_boot(desc); | |
| } | |
| t_stat | |
| cdr_attach(UNIT * uptr, char *file) | |
| { | |
| t_stat r; | |
| int u = uptr-cdr_unit; | |
| if ((r = sim_card_attach(uptr, file)) != SCPE_OK) | |
| return r; | |
| uptr->u5 &= URCSTA_BUSY; | |
| uptr->u4 = 0; | |
| uptr->u6 = 0; | |
| iostatus |= (CARD1_FLAG << u); | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| cdr_detach(UNIT * uptr) | |
| { | |
| int u = uptr-cdr_unit; | |
| iostatus &= ~(CARD1_FLAG << u); | |
| return sim_card_detach(uptr); | |
| } | |
| t_stat | |
| cdr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| fprintf (st, "B124 Card Reader\n\n"); | |
| fprintf (st, "The system supports up to two card readers, the second one is disabled\n"); | |
| fprintf (st, "by default. To have the card reader return the EOF flag when the deck\n"); | |
| fprintf (st, "has finished reading do:\n"); | |
| fprintf (st, " sim> SET CRn EOF\n"); | |
| fprintf (st, "This flag is cleared each time a deck has been read, so it must be set\n"); | |
| fprintf (st, "again after each deck. MCP does not require this to be set as long as\n"); | |
| fprintf (st, "the deck includes a ?END card\n"); | |
| fprint_set_help(st, dptr); | |
| fprint_show_help(st, dptr); | |
| return SCPE_OK; | |
| } | |
| const char * | |
| cdr_description(DEVICE *dptr) | |
| { | |
| return "B124 Card Reader"; | |
| } | |
| #endif | |
| #if NUM_DEVS_CDR > 0 | NUM_DEVS_CDP > 0 | |
| /* Handle transfer of data for card punch */ | |
| t_stat | |
| cdp_srv(UNIT *uptr) { | |
| int chan = URCSTA_CHMASK & uptr->u5; | |
| int u = (uptr - cdp_unit); | |
| if (uptr->u5 & URCSTA_BUSY) { | |
| /* Done waiting, punch card */ | |
| if (uptr->u5 & URCSTA_FULL) { | |
| sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d punch\n", u, chan); | |
| switch(sim_punch_card(uptr, NULL)) { | |
| case SCPE_EOF: | |
| case SCPE_UNATT: | |
| sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d set eof\n", u, | |
| chan); | |
| chan_set_eof(chan); | |
| break; | |
| /* If we get here, something is wrong */ | |
| case SCPE_IOERR: | |
| chan_set_error(chan); | |
| break; | |
| case SCPE_OK: | |
| break; | |
| } | |
| uptr->u5 &= ~URCSTA_FULL; | |
| chan_set_end(chan); | |
| } | |
| uptr->u5 &= ~URCSTA_BUSY; | |
| } | |
| /* Copy next column over */ | |
| if (uptr->u5 & URCSTA_ACTIVE && uptr->u4 <= 80) { | |
| struct _card_data *data; | |
| uint8 ch = 0; | |
| data = (struct _card_data *)uptr->up7; | |
| if(chan_read_char(chan, &ch, 0)) { | |
| uptr->u5 |= URCSTA_BUSY|URCSTA_FULL; | |
| uptr->u5 &= ~URCSTA_ACTIVE; | |
| } else { | |
| sim_debug(DEBUG_DATA, &cdp_dev, "cdp %d: Char %d < %02o\n", u, | |
| uptr->u4, ch); | |
| data->image[uptr->u4++] = sim_bcd_to_hol(ch & 077); | |
| } | |
| sim_activate(uptr, 10); | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| cdp_attach(UNIT * uptr, char *file) | |
| { | |
| t_stat r; | |
| if ((r = sim_card_attach(uptr, file)) != SCPE_OK) | |
| return r; | |
| uptr->u5 = 0; | |
| iostatus |= PUNCH_FLAG; | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| cdp_detach(UNIT * uptr) | |
| { | |
| if (uptr->u5 & URCSTA_FULL) | |
| sim_punch_card(uptr, NULL); | |
| iostatus &= ~PUNCH_FLAG; | |
| return sim_card_detach(uptr); | |
| } | |
| t_stat | |
| cdp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| fprintf (st, "B303 Card Punch\n\n"); | |
| fprintf (st, "The B303 Card Punch is only capable of punching text decks, binary decks\n"); | |
| fprintf (st, "where not supported.\n"); | |
| fprint_set_help(st, dptr); | |
| fprint_show_help(st, dptr); | |
| return SCPE_OK; | |
| } | |
| const char * | |
| cdp_description(DEVICE *dptr) | |
| { | |
| return "B303 Card Punch"; | |
| } | |
| #endif | |
| /* Line printer routines | |
| */ | |
| #if NUM_DEVS_LPR > 0 | |
| t_stat | |
| lpr_setlpp(UNIT *uptr, int32 val, char *cptr, void *desc) | |
| { | |
| int i; | |
| if (cptr == NULL) | |
| return SCPE_ARG; | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| i = 0; | |
| while(*cptr != '\0') { | |
| if (*cptr < '0' || *cptr > '9') | |
| return SCPE_ARG; | |
| i = (i * 10) + (*cptr++) - '0'; | |
| } | |
| if (i < 20 || i > 100) | |
| return SCPE_ARG; | |
| uptr->capac = i; | |
| uptr->u4 = 0; | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| lpr_getlpp(FILE *st, UNIT *uptr, int32 v, void *desc) | |
| { | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| fprintf(st, "linesperpage=%d", uptr->capac); | |
| return SCPE_OK; | |
| } | |
| void | |
| print_line(UNIT * uptr, int unit) | |
| { | |
| /* Convert word record into column image */ | |
| /* Check output type, if auto or text, try and convert record to bcd first */ | |
| /* If failed and text report error and dump what we have */ | |
| /* Else if binary or not convertable, dump as image */ | |
| char out[150]; /* Temp conversion buffer */ | |
| int i; | |
| int chan = uptr->u5 & URCSTA_CHMASK; | |
| if ((uptr->flags & (UNIT_ATT)) == 0) | |
| return; /* attached? */ | |
| if (uptr->u3 > 0) { | |
| /* Try to convert to text */ | |
| memset(out, 0, sizeof(out)); | |
| /* Scan each column */ | |
| for (i = 0; i < uptr->u3; i++) { | |
| int bcd = lpr_data[unit].lbuff[i] & 077; | |
| out[i] = con_to_ascii[bcd]; | |
| } | |
| /* Trim trailing spaces */ | |
| for (--i; i > 0 && out[i] == ' '; i--) ; | |
| out[i+1] = '\0'; | |
| sim_debug(DEBUG_DETAIL, &lpr_dev, "lpr print %s\n", out); | |
| if (uptr->u5 & (URCSTA_DOUBLE << URCSTA_CMD_V)) { | |
| out[++i] = '\r'; | |
| out[++i] = '\n'; | |
| uptr->u4 ++; | |
| } | |
| out[++i] = '\r'; | |
| out[++i] = '\n'; | |
| uptr->u4++; | |
| out[++i] = '\0'; | |
| /* Print out buffer */ | |
| sim_fwrite(&out, 1, i, uptr->fileref); | |
| uptr->u5 &= ~URCSTA_EOF; | |
| } | |
| switch ((uptr->u5 >> URCSTA_CMD_V) & URCSTA_SKIP) { | |
| case 0: /* No special skip */ | |
| break; | |
| case 1: | |
| case 2: /* Skip to top of form */ | |
| case 12: | |
| uptr->u4 = uptr->capac+1; | |
| break; | |
| case 3: /* Even lines */ | |
| if ((uptr->u4 & 1) == 1) { | |
| sim_fwrite("\r", 1, 1, uptr->fileref); | |
| sim_fwrite("\n", 1, 1, uptr->fileref); | |
| uptr->u4++; | |
| uptr->u5 &= ~URCSTA_EOF; | |
| } | |
| break; | |
| case 4: /* Odd lines */ | |
| if ((uptr->u4 & 1) == 0) { | |
| sim_fwrite("\r", 1, 1, uptr->fileref); | |
| sim_fwrite("\n", 1, 1, uptr->fileref); | |
| uptr->u4++; | |
| uptr->u5 &= ~URCSTA_EOF; | |
| } | |
| break; | |
| case 5: /* Half page */ | |
| while((uptr->u4 != (uptr->capac/2)) || | |
| (uptr->u4 != (uptr->capac))) { | |
| sim_fwrite("\r", 1, 1, uptr->fileref); | |
| sim_fwrite("\n", 1, 1, uptr->fileref); | |
| uptr->u4++; | |
| if (((uint32)uptr->u4) > uptr->capac) { | |
| uptr->u4 = 1; | |
| break; | |
| } | |
| uptr->u5 &= ~URCSTA_EOF; | |
| } | |
| break; | |
| case 6: /* 1/4 Page */ | |
| while((uptr->u4 != (uptr->capac/4)) || | |
| (uptr->u4 != (uptr->capac/2)) || | |
| (uptr->u4 != (uptr->capac/2+uptr->capac/4)) || | |
| (uptr->u4 != (uptr->capac))) { | |
| sim_fwrite("\r", 1, 1, uptr->fileref); | |
| sim_fwrite("\n", 1, 1, uptr->fileref); | |
| uptr->u4++; | |
| if (((uint32)uptr->u4) > uptr->capac) { | |
| uptr->u4 = 1; | |
| break; | |
| } | |
| uptr->u5 &= ~URCSTA_EOF; | |
| } | |
| break; | |
| case 7: /* User defined, now 1 line */ | |
| case 8: | |
| case 9: | |
| case 10: | |
| case 11: | |
| sim_fwrite("\r", 1, 1, uptr->fileref); | |
| sim_fwrite("\n", 1, 1, uptr->fileref); | |
| uptr->u4++; | |
| break; | |
| } | |
| if (((uint32)uptr->u4) > uptr->capac) { | |
| uptr->u4 = 1; | |
| uptr->u5 |= URCSTA_EOF; | |
| sim_fwrite("\f", 1, 1, uptr->fileref); | |
| sim_fseek(uptr->fileref, 0, SEEK_CUR); | |
| sim_debug(DEBUG_DETAIL, &lpr_dev, "lpr %d page\n", unit); | |
| } | |
| } | |
| t_stat lpr_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) | |
| { | |
| UNIT *uptr; | |
| int u; | |
| if (dev == PRT1_DEV) | |
| u = 0; | |
| else if (dev == PRT2_DEV) | |
| u = 1; | |
| else | |
| return SCPE_NXDEV; | |
| uptr = &lpr_unit[u]; | |
| /* Are we currently tranfering? */ | |
| if (uptr->u5 & URCSTA_BUSY) | |
| return SCPE_BUSY; | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; | |
| if (*wc == 0 && (cmd & URCSTA_INHIBIT) == 0) | |
| *wc = (cmd & URCSTA_DIRECT) ? 17 : 15; | |
| /* Remember not to drop the FULL */ | |
| uptr->u5 &= ~((077 << URCSTA_CMD_V) | URCSTA_CHMASK); | |
| uptr->u5 |= URCSTA_BUSY|chan; | |
| uptr->u5 |= (cmd & (URCSTA_SKIP|URCSTA_SINGLE|URCSTA_DOUBLE)) | |
| << URCSTA_CMD_V; | |
| uptr->u3 = 0; | |
| sim_debug(DEBUG_CMD, &lpr_dev, "%d: Cmd WRS %d %02o %o\n", u, chan, | |
| cmd & (URCSTA_SKIP|URCSTA_SINGLE|URCSTA_DOUBLE),uptr->u5); | |
| sim_activate(uptr, 100); | |
| return SCPE_OK; | |
| } | |
| /* Handle transfer of data for printer */ | |
| t_stat | |
| lpr_srv(UNIT *uptr) { | |
| int chan = URCSTA_CHMASK & uptr->u5; | |
| int u = (uptr - lpr_unit); | |
| if (uptr->u5 & URCSTA_FULL) { | |
| sim_debug(DEBUG_CMD, &lpr_dev, "lpr %d: done\n", u); | |
| uptr->u5 &= ~URCSTA_FULL; | |
| IAR |= (IRQ_3 << u); | |
| } | |
| /* Copy next column over */ | |
| if ((uptr->u5 & URCSTA_BUSY) != 0) { | |
| if(chan_read_char(chan, &lpr_data[u].lbuff[uptr->u3], 0)) { | |
| /* Done waiting, print line */ | |
| print_line(uptr, u); | |
| memset(&lpr_data[u].lbuff[0], 0, 144); | |
| uptr->u5 |= URCSTA_FULL; | |
| uptr->u5 &= ~URCSTA_BUSY; | |
| chan_set_wc(chan, (uptr->u3/8)); | |
| chan_set_end(chan); | |
| sim_activate(uptr, 20000); | |
| return SCPE_OK; | |
| } else { | |
| sim_debug(DEBUG_DATA, &lpr_dev, "lpr %d: Char < %02o\n", u, | |
| lpr_data[u].lbuff[uptr->u3]); | |
| uptr->u3++; | |
| } | |
| sim_activate(uptr, 50); | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| lpr_attach(UNIT * uptr, char *file) | |
| { | |
| t_stat r; | |
| int u = (uptr - lpr_unit); | |
| if ((r = attach_unit(uptr, file)) != SCPE_OK) | |
| return r; | |
| uptr->u5 = 0; | |
| uptr->u4 = 0; | |
| uptr->u3 = 0; | |
| iostatus |= PRT1_FLAG << u; | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| lpr_detach(UNIT * uptr) | |
| { | |
| int u = (uptr - lpr_unit); | |
| if (uptr->u5 & URCSTA_FULL) | |
| print_line(uptr, u); | |
| iostatus &= ~(PRT1_FLAG << u); | |
| return detach_unit(uptr); | |
| } | |
| t_stat | |
| lpr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| fprintf (st, "B320 Line Printer\n\n"); | |
| fprintf (st, "The system supports up to two line printers, the second one is disabled\n"); | |
| fprintf (st, "by default. The B320 Line printer can be configured to any number of\n"); | |
| fprintf (st, "lines per page with the:\n"); | |
| fprintf (st, " sim> SET LPn LINESPERPAGE=n\n\n"); | |
| fprintf (st, "The default is 59 lines per page. The Line Printer has the following\n"); | |
| fprintf (st, "control tape attached.\n"); | |
| fprintf (st, " Channel 1: Skip to top of page\n"); | |
| fprintf (st, " Channel 2: Skip to top of page\n"); | |
| fprintf (st, " Channel 3: Skip to next even line\n"); | |
| fprintf (st, " Channel 4: Skip to next odd line\n"); | |
| fprintf (st, " Channel 5: Skip to middle or top of page\n"); | |
| fprintf (st, " Channel 6: Skip 1/4 of page\n"); | |
| fprintf (st, " Channel 7: Skip one linee\n"); | |
| fprintf (st, " Channel 8: Skip one linetop of page\n"); | |
| fprintf (st, " Channel 9: Skip one linetop of page\n"); | |
| fprintf (st, " Channel 10: Skip one linetop of page\n"); | |
| fprintf (st, " Channel 11: Skip one linetop of page\n"); | |
| fprintf (st, " Channel 12: Skip to top of page\n"); | |
| fprint_set_help(st, dptr); | |
| fprint_show_help(st, dptr); | |
| return SCPE_OK; | |
| } | |
| const char * | |
| lpr_description(DEVICE *dptr) | |
| { | |
| return "B320 Line Printer"; | |
| } | |
| #endif | |
| #if NUM_DEVS_CON > 0 | |
| /* | |
| * Console printer routines. | |
| */ | |
| t_stat | |
| con_ini(DEVICE *dptr) { | |
| UNIT *uptr = &con_unit[0]; | |
| uptr->u5 = 0; | |
| iostatus |= SPO_FLAG; | |
| if (!sim_is_active(uptr)) | |
| sim_activate(uptr, 1000); | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| con_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) | |
| { | |
| UNIT *uptr = &con_unit[0]; | |
| /* Are we currently tranfering? */ | |
| if (uptr->u5 & (URCSTA_READ|URCSTA_FILL|URCSTA_BUSY|URCSTA_INPUT)) | |
| return SCPE_BUSY; | |
| if (cmd & URCSTA_READ) { | |
| if (uptr->u5 & (URCSTA_INPUT|URCSTA_FILL)) | |
| return SCPE_BUSY; | |
| /* Activate input so we can get response */ | |
| uptr->u5 = 0; | |
| uptr->u5 |= URCSTA_INPUT|chan; | |
| sim_putchar('I'); | |
| sim_putchar(' '); | |
| sim_debug(DEBUG_CMD, &con_dev, ": Cmd RDS\n"); | |
| uptr->u3 = 0; | |
| } else { | |
| if (uptr->u5 & (URCSTA_INPUT|URCSTA_FILL)) | |
| return SCPE_BUSY; | |
| sim_putchar('R'); | |
| sim_putchar(' '); | |
| sim_debug(DEBUG_CMD, &con_dev, ": Cmd WRS\n"); | |
| uptr->u5 = 0; | |
| uptr->u5 |= URCSTA_FILL|chan; | |
| uptr->u3 = 0; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Handle transfer of data for printer */ | |
| t_stat | |
| con_srv(UNIT *uptr) { | |
| t_stat r; | |
| uint8 ch; | |
| int chan = uptr->u5 & URCSTA_CHMASK; | |
| uptr->u5 &= ~URCSTA_BUSY; /* Clear busy */ | |
| /* Copy next column over */ | |
| if (uptr->u5 & URCSTA_FILL) { | |
| if(chan_read_char(chan, &ch, 0)) { | |
| sim_putchar('\r'); | |
| sim_putchar('\n'); | |
| sim_debug(DEBUG_EXP, &con_dev, "\n\r"); | |
| uptr->u5 &= ~URCSTA_FILL; | |
| chan_set_end(chan); | |
| } else { | |
| ch &= 077; | |
| sim_debug(DEBUG_EXP, &con_dev, "%c", con_to_ascii[ch]); | |
| sim_putchar((int32)con_to_ascii[ch]); | |
| } | |
| } | |
| if (uptr->u5 & URCSTA_READ) { | |
| ch = con_data[0].ibuff[con_data[0].outptr++]; | |
| if(chan_write_char(chan, &ch, | |
| (con_data[0].inptr == con_data[0].outptr))) { | |
| sim_putchar('\r'); | |
| sim_putchar('\n'); | |
| sim_debug(DEBUG_EXP, &con_dev, "\n\r"); | |
| uptr->u5 &= ~URCSTA_READ; | |
| chan_set_end(chan); | |
| } | |
| } | |
| r = sim_poll_kbd(); | |
| if (r & SCPE_KFLAG) { | |
| ch = r & 0377; | |
| if (uptr->u5 & URCSTA_INPUT) { | |
| /* Handle end of buffer */ | |
| switch (ch) { | |
| case 033: | |
| con_data[0].inptr = 0; | |
| case '\r': | |
| case '\n': | |
| uptr->u5 &= ~URCSTA_INPUT; | |
| uptr->u5 |= URCSTA_READ; | |
| break; | |
| case '\b': | |
| case 0x7f: | |
| if (con_data[0].inptr != 0) { | |
| con_data[0].inptr--; | |
| sim_putchar('\b'); | |
| sim_putchar(' '); | |
| sim_putchar('\b'); | |
| } | |
| break; | |
| default: | |
| if (con_data[0].inptr < sizeof(con_data[0].ibuff)) { | |
| ch = ascii_to_con[0177&ch]; | |
| if (ch == 0xff) { | |
| sim_putchar('\007'); | |
| break; | |
| } | |
| sim_putchar((int32)con_to_ascii[ch]); | |
| con_data[0].ibuff[con_data[0].inptr++] = ch; | |
| } | |
| break; | |
| } | |
| } else { | |
| if (ch == 033) { | |
| IAR |= IRQ_2; | |
| con_data[0].inptr = 0; | |
| con_data[0].outptr = 0; | |
| } | |
| } | |
| } | |
| sim_activate(uptr, 1000); | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| con_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| fprintf (st, "Supervisory Printer\n\n"); | |
| fprintf (st, "This is the interface from the operator to the system. The printer\n"); | |
| fprintf (st, "operated in a half duplex mode. To request the system to accept input\n"); | |
| fprintf (st, "press the <esc> key and wait until the system responds with a line with\n"); | |
| fprintf (st, "I as the first character. When you have finished typing your line, press\n"); | |
| fprintf (st, "return or enter key. Backspace will delete the last character.\n"); | |
| fprintf (st, "All responses from the system are prefixed with a R and blank as the\n"); | |
| fprintf (st, "first character\n"); | |
| return SCPE_OK; | |
| } | |
| const char * | |
| con_description(DEVICE *dptr) | |
| { | |
| return "Supervisory Printer"; | |
| } | |
| #endif | |