| /* i7090_mt.c: IBM 7090 Magnetic tape controller | |
| Copyright (c) 2005-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. | |
| Magnetic tapes are represented as a series of variable records | |
| of the form: | |
| 32b byte count | |
| byte 0 | |
| byte 1 | |
| : | |
| byte n-2 | |
| byte n-1 | |
| 32b byte count | |
| If the byte count is odd, the record is padded with an extra byte | |
| of junk. File marks are represented by a byte count of 0. | |
| */ | |
| #include "i7000_defs.h" | |
| #include "sim_tape.h" | |
| #ifndef NUM_DEVS_MT | |
| #define NUM_DEVS_MT 0 | |
| #endif | |
| #if (NUM_DEVS_MT > 0) || defined(MT_CHANNEL_ZERO) | |
| #define BUFFSIZE (MAXMEMSIZE * CHARSPERWORD) | |
| #define UNIT_MT(x) UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | \ | |
| UNIT_S_CHAN(x) | |
| #define MTUF_LDN (1 << MTUF_V_UF) | |
| #define MTUF_ONLINE (1 << UNIT_V_UF_31) | |
| /* in u3 is current frame of tape */ | |
| /* in u5 holds the commands */ | |
| #define MT_RDS 1 | |
| #define MT_RDSB 2 | |
| #define MT_WRS 3 | |
| #define MT_WRSB 4 | |
| #define MT_WEF 5 | |
| #define MT_BSR 6 | |
| #define MT_BSF 7 | |
| #define MT_REW 8 | |
| #define MT_SDN 9 | |
| #define MT_RUN 10 | |
| #define MT_SKIP 11 /* Do skip to end of record */ | |
| #define MT_WRITE 12 /* Actual transfer operation */ | |
| #define MT_SKR 13 | |
| #define MT_ERG 14 | |
| #define MT_RDB 15 | |
| #define MT_LREW 16 /* Low speed rewind */ | |
| #define MT_HREW 17 /* High speed rewind */ | |
| #define MT_CMDMSK 000037 /* Command being run */ | |
| #define MT_RDY 000040 /* Device is ready for command */ | |
| #define MT_IDLE 000100 /* Tape still in motion */ | |
| #define MT_MARK 000200 /* Hit tape mark */ | |
| #define MT_EOT 000400 /* At End Of Tape */ | |
| #define MT_RM 001000 /* Hit a record mark character */ | |
| #define MT_EOR 002000 /* Set EOR on next record */ | |
| #define MT_UNLOAD 004000 /* Unload when rewind done */ | |
| #define MT_EGAP 010000 /* Write extended gap on next write */ | |
| /* u6 holds the current buffer position */ | |
| /* Flags for mt_chan */ | |
| #define MTC_SEL 0020 /* Controller executing read/write */ | |
| #define MTC_BSY 0040 /* Controller is busy - executing cmd */ | |
| #define MTC_UNIT 0017 /* device Channel is on */ | |
| /* Timing for tape */ | |
| #define IPS 75 /* Inches per second 75 or 112 */ | |
| #define HS_IPS 500 /* High speed rewind Inches per second */ | |
| #define LD 200 | |
| #define HD 555 | |
| #define LT_GAP_LEN ((3 * LD)/ 4) /* Gap length for low density */ | |
| #define HT_GAP_LEN ((3 * HD)/ 4) /* Gap length for high density */ | |
| #define LT (1000000/(LD * IPS)) /* Time per char low density */ | |
| #define HT (1000000/(HD * IPS)) /* Time per char high density */ | |
| #define LT_GAP_TIM (LT_GAP_LEN * LT) /* Time per char low density */ | |
| #define HT_GAP_TIM (HT_GAP_LEN * HT) /* Time per char high density */ | |
| /* Normal frame time */ | |
| #define T1 ((uptr->flags & MTUF_LDN) ?LT:HT) | |
| #define T1_us us_to_ticks(T1) | |
| /* Gap time */ | |
| #define T2 ((uptr->flags & MTUF_LDN) ?LT_GAP_TIM:HT_GAP_TIM) | |
| #define T2_us us_to_ticks(T2) | |
| /* Start time */ | |
| #define T3 (((uptr->flags & MTUF_LDN) ?LT_GAP_TIM:HT_GAP_TIM) + 500) | |
| #define T3_us us_to_ticks(T3) | |
| #define GAP_LEN ((uptr->flags & MTUF_LDN) ?LT_GAP_LEN:HT_GAP_LEN) | |
| /* Definitions */ | |
| uint32 mt_cmd(UNIT *, uint16, uint16); | |
| t_stat mt_srv(UNIT *); | |
| t_stat mt_boot(int32, DEVICE *); | |
| void mt_ini(UNIT *, t_bool); | |
| t_stat mt_reset(DEVICE *); | |
| t_stat mt_attach(UNIT *, CONST char *); | |
| t_stat mt_detach(UNIT *); | |
| t_stat mt_rew(UNIT * uptr, int32 val, CONST char *cptr, | |
| void *desc); | |
| t_stat mt_tape_density(UNIT * uptr, int32 val, CONST char *cptr, | |
| void *desc); | |
| t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, | |
| const char *cptr); | |
| const char *mt_description (DEVICE *dptr); | |
| extern t_stat chan_boot(int32, DEVICE *); | |
| #ifdef I7010 | |
| extern uint8 chan_io_status[NUM_CHAN]; /* Channel status */ | |
| #endif | |
| #ifdef MT_CHANNEL_ZERO | |
| #define NUM_DEVS (NUM_DEVS_MT + 1) | |
| #else | |
| #define NUM_DEVS (NUM_DEVS_MT) | |
| #endif | |
| /* Channel level activity */ | |
| uint8 mt_chan[NUM_DEVS]; | |
| /* One buffer per channel */ | |
| uint8 mt_buffer[NUM_DEVS][BUFFSIZE]; | |
| UNIT mta_unit[] = { | |
| /* Controller 1 */ | |
| #if (NUM_DEVS_MT > 0) | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 9 */ | |
| #if (NUM_DEVS_MT > 1) | |
| /* Controller 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 9 */ | |
| #if (NUM_DEVS_MT > 2) | |
| /* Controller 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 9 */ | |
| #if (NUM_DEVS_MT > 3) | |
| /* Controller 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 9 */ | |
| #if (NUM_DEVS_MT > 4) | |
| /* Controller 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 9 */ | |
| #if (NUM_DEVS_MT > 5) | |
| /* Controller 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 9 */ | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| #ifdef MT_CHANNEL_ZERO | |
| /* Controller 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 0 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 1 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 2 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 3 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 4 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 5 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 6 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 7 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 8 */ | |
| {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 9 */ | |
| #endif | |
| }; | |
| MTAB mt_mod[] = { | |
| {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL, | |
| "Write ring in place"}, | |
| {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, NULL, NULL, | |
| "No write ring in place"}, | |
| {MTUF_LDN, 0, "high density", "HIGH", &mt_tape_density, NULL, NULL, | |
| "556 BPI"}, | |
| {MTUF_LDN, MTUF_LDN, "low density", "LOW", &mt_tape_density, NULL, NULL, | |
| "200 BPI"}, | |
| #ifdef I7090 | |
| {MTUF_ONLINE, 0, "offline", "OFFLINE", NULL, NULL, NULL, | |
| "Tape offline"}, | |
| {MTUF_ONLINE, MTUF_ONLINE, "online", "ONLINE", NULL, NULL, NULL, | |
| "Tape Online"}, | |
| #endif | |
| {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| &sim_tape_set_fmt, &sim_tape_show_fmt, NULL, | |
| "Set/Display tape format (SIMH, E11, TPC, P7B)"}, | |
| {MTAB_XTD | MTAB_VUN, 0, "LENGTH", "LENGTH", | |
| &sim_tape_set_capac, &sim_tape_show_capac, NULL, | |
| "Set unit n capacity to arg MB (0 = unlimited)" }, | |
| {MTAB_XTD | MTAB_VUN, 0, NULL, "REWIND", | |
| &mt_rew, NULL, NULL, "Rewind tape" | |
| }, | |
| #ifdef I7090 | |
| {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan, | |
| NULL, "Device Channel"}, | |
| #endif | |
| {0} | |
| }; | |
| #ifdef MT_CHANNEL_ZERO | |
| DEVICE mtz_dev = { | |
| "MT", &mta_unit[NUM_DEVS_MT * 10], NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 8, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(NUM_DEVS_MT) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #endif | |
| #if (NUM_DEVS_MT > 0) | |
| DEVICE mta_dev = { | |
| "MTA", mta_unit, NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 8, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #if (NUM_DEVS_MT > 1) | |
| DEVICE mtb_dev = { | |
| "MTB", &mta_unit[10], NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 8, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #if (NUM_DEVS_MT > 2) | |
| DEVICE mtc_dev = { | |
| "MTC", &mta_unit[20], NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 8, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(2) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #if (NUM_DEVS_MT > 3) | |
| DEVICE mtd_dev = { | |
| "MTD", &mta_unit[30], NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 36, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(3) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #if (NUM_DEVS_MT > 4) | |
| DEVICE mte_dev = { | |
| "MTE", &mta_unit[40], NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 8, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(4) | DEV_DIS | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #if (NUM_DEVS_MT > 5) | |
| DEVICE mtf_dev = { | |
| "MTF", &mta_unit[50], NULL, mt_mod, | |
| NUM_UNITS_MT, 8, 15, 1, 8, 8, | |
| NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, | |
| &mt_dib, DEV_BUF_NUM(5) | DEV_DIS | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
| NULL, NULL, &mt_help, NULL, NULL, &mt_description | |
| }; | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| uint8 parity_table[64] = { | |
| /* 0 1 2 3 4 5 6 7 */ | |
| 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, | |
| 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, | |
| 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, | |
| 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, | |
| 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, | |
| 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, | |
| 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, | |
| 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000 | |
| }; | |
| /* Rewind tape drive */ | |
| t_stat | |
| mt_rew(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| /* If drive is offline or not attached return not ready */ | |
| if ((uptr->flags & (UNIT_ATT | MTUF_ONLINE)) == 0) | |
| return SCPE_NOATT; | |
| /* Check if drive is ready to recieve a command */ | |
| if ((uptr->u5 & MT_RDY) == 0) | |
| return STOP_IOCHECK; | |
| return sim_tape_rewind(uptr); | |
| } | |
| /* Start off a mag tape command */ | |
| uint32 mt_cmd(UNIT * uptr, uint16 cmd, uint16 dev) | |
| { | |
| int chan = UNIT_G_CHAN(uptr->flags); | |
| DEVICE *dptr = find_dev_from_unit(uptr); | |
| int time = us_to_ticks(100); | |
| int unit = dev & 017; | |
| unit -= mt_dib.addr & 017; /* Adjust to origin zero */ | |
| if (unit == 10) | |
| unit = 0; | |
| /* Make sure valid drive number */ | |
| if (unit > NUM_UNITS_MT || unit < 0) | |
| return SCPE_NODEV; | |
| uptr += unit; | |
| /* If unit disabled return error */ | |
| if (uptr->flags & UNIT_DIS) { | |
| /* | |
| fprintf(stderr, "Attempt to access disconnected unit %s%d\n", | |
| dptr->name, unit); */ | |
| return SCPE_NODEV; | |
| } | |
| /* Check status of the drive */ | |
| /* Can't do nothing if controller is busy */ | |
| if (mt_chan[chan] & MTC_BSY) { | |
| return SCPE_BUSY; | |
| } | |
| /* If drive is offline or not attached return not ready */ | |
| if ((uptr->flags & (UNIT_ATT | MTUF_ONLINE)) != | |
| (UNIT_ATT | MTUF_ONLINE)) { | |
| fprintf(stderr, "Attempt to access offline unit %s%d\n\r", | |
| dptr->name, unit); | |
| return SCPE_IOERR; | |
| } | |
| /* Check if drive is ready to recieve a command */ | |
| if ((uptr->u5 & MT_RDY) == 0) { | |
| /* Return indication if not ready and doing TRS */ | |
| if (cmd == IO_TRS) { | |
| return SCPE_IOERR; | |
| } else { | |
| return SCPE_BUSY; | |
| } | |
| } | |
| uptr->u5 &= ~(MT_CMDMSK | MT_RDY); | |
| time = us_to_ticks(12000); | |
| if ((uptr->u5 & MT_IDLE) == 0) | |
| time = us_to_ticks(15000); | |
| switch (cmd) { | |
| case IO_RDS: | |
| if (sim_tape_bot(uptr)) | |
| time = us_to_ticks(21000); | |
| if (mt_chan[chan] & MTC_SEL) { | |
| uptr->u5 |= MT_RDY; | |
| return SCPE_BUSY; | |
| } | |
| #ifdef I701 | |
| uptr->u5 |= MT_RDSB; | |
| #else | |
| if (dev & 020) | |
| uptr->u5 |= MT_RDSB; | |
| else | |
| uptr->u5 |= MT_RDS; | |
| #endif | |
| chan_set_sel(chan, 0); | |
| chan_clear_status(chan); | |
| mt_chan[chan] = MTC_BSY | MTC_SEL | unit; | |
| uptr->u5 &= ~(MT_RM|MT_EOR|MT_EGAP); | |
| uptr->u6 = -1; | |
| uptr->hwmark = -1; | |
| #if I7010 | I7080 | |
| chan_set(chan, STA_TWAIT); | |
| #endif | |
| sim_debug(DEBUG_CMD, dptr, "RDS %s unit=%d %d\n", | |
| ((uptr->u5 & MT_CMDMSK) == MT_RDS) ? "BCD" : "Binary", | |
| unit, dev); | |
| break; | |
| case IO_WRS: | |
| if (sim_tape_bot(uptr)) | |
| time = us_to_ticks(40000); | |
| if (mt_chan[chan] & MTC_SEL) { | |
| uptr->u5 |= MT_RDY; | |
| return SCPE_BUSY; | |
| } | |
| if (sim_tape_wrp(uptr)) { | |
| sim_debug(DEBUG_EXP, dptr, | |
| "WRS %d attempted on locked tape\n", unit); | |
| uptr->u5 |= MT_RDY; | |
| return SCPE_IOERR; | |
| } | |
| #ifdef I701 | |
| uptr->u5 |= MT_WRSB; | |
| #else | |
| if (dev & 020) | |
| uptr->u5 |= MT_WRSB; | |
| else | |
| uptr->u5 |= MT_WRS; | |
| #endif | |
| time += T2_us; | |
| uptr->u6 = 0; | |
| uptr->hwmark = 0; | |
| chan_set_sel(chan, 1); | |
| chan_clear_status(chan); | |
| mt_chan[chan] = MTC_BSY | MTC_SEL | unit; | |
| uptr->u5 &= ~(MT_MARK | MT_EOT); | |
| #if I7010 | I7080 | |
| chan_set(chan, STA_TWAIT); | |
| #endif | |
| sim_debug(DEBUG_CMD, dptr, "WRS %s unit=%d %d\n", | |
| ((uptr->u5 & MT_CMDMSK) == MT_WRS) ? "BCD" : "Binary", | |
| unit, dev); | |
| break; | |
| case IO_RDB: | |
| if (mt_chan[chan] & MTC_SEL) { | |
| uptr->u5 |= MT_RDY; | |
| return SCPE_BUSY; | |
| } | |
| uptr->u5 |= MT_RDB; | |
| chan_set_sel(chan, 0); | |
| chan_clear_status(chan); | |
| mt_chan[chan] = MTC_BSY | MTC_SEL | unit; | |
| uptr->u5 &= ~(MT_RM|MT_EOR|MT_EGAP); | |
| uptr->u6 = -1; | |
| uptr->hwmark = -1; | |
| #if I7010 | I7080 | |
| chan_set(chan, STA_TWAIT); | |
| #endif | |
| sim_debug(DEBUG_CMD, dptr, "RDB unit=%d %d\n", unit, dev); | |
| break; | |
| case IO_WEF: | |
| if (sim_tape_bot(uptr)) | |
| time = us_to_ticks(40000); | |
| uptr->u5 &= ~(MT_EOT|MT_MARK); | |
| if (sim_tape_wrp(uptr)) { | |
| sim_debug(DEBUG_EXP, dptr, | |
| "WRS %d attempted on locked tape\n", unit); | |
| uptr->u5 |= MT_RDY; | |
| return SCPE_IOERR; | |
| } | |
| uptr->u5 |= MT_WEF; | |
| #if I7010 | |
| chan_set_sel(chan, 1); | |
| chan_clear_status(chan); | |
| mt_chan[chan] = MTC_BSY | MTC_SEL | unit; | |
| chan_set(chan, STA_TWAIT); | |
| #else | |
| mt_chan[chan] = MTC_BSY; | |
| #endif | |
| sim_debug(DEBUG_CMD, dptr, "WEF unit=%d\n", unit); | |
| break; | |
| case IO_BSR: | |
| uptr->u5 &= ~(MT_MARK); | |
| /* Check if at load point, quick return if so */ | |
| if (sim_tape_bot(uptr)) { | |
| sim_debug(DEBUG_CMD, dptr, "BSR unit=%d at BOT\n", unit); | |
| uptr->u5 |= MT_RDY; | |
| uptr->u3 = 0; | |
| chan_set(chan, CHS_BOT); | |
| return SCPE_OK; | |
| } | |
| uptr->u5 |= MT_BSR; | |
| mt_chan[chan] = MTC_BSY; | |
| sim_debug(DEBUG_CMD, dptr, "BSR unit=%d\n", unit); | |
| break; | |
| case IO_BSF: | |
| uptr->u5 &= ~(MT_MARK); | |
| /* Check if at load point, quick return if so */ | |
| if (sim_tape_bot(uptr)) { | |
| sim_debug(DEBUG_CMD, dptr, "BSF unit=%d at BOT\n", unit); | |
| uptr->u5 |= MT_RDY; | |
| uptr->u3 = 0; | |
| chan_set(chan, CHS_BOT); | |
| return SCPE_OK; | |
| } | |
| uptr->u5 |= MT_BSF; | |
| mt_chan[chan] = MTC_BSY; | |
| sim_debug(DEBUG_CMD, dptr, "BSF unit=%d\n", unit); | |
| break; | |
| case IO_SKR: | |
| if (sim_tape_bot(uptr)) | |
| time = us_to_ticks(21000); | |
| uptr->u5 &= ~(MT_MARK|MT_EGAP); | |
| uptr->u5 |= MT_SKR; | |
| mt_chan[chan] = MTC_BSY; | |
| sim_debug(DEBUG_CMD, dptr, "SKR unit=%d\n", unit); | |
| break; | |
| case IO_ERG: | |
| sim_debug(DEBUG_CMD, dptr, "ERG unit=%d\n", unit); | |
| #ifdef I7080 | |
| uptr->u5 &= ~(MT_MARK); | |
| uptr->u5 |= MT_ERG; | |
| mt_chan[chan] = MTC_BSY; | |
| chan_set(chan, STA_TWAIT); | |
| break; | |
| #else | |
| uptr->u5 |= MT_EGAP|MT_RDY; /* Command is quick */ | |
| return SCPE_OK; | |
| #endif | |
| case IO_REW: | |
| uptr->u5 &= ~(MT_EOT|MT_MARK|MT_EGAP); | |
| /* Check if at load point, quick return if so */ | |
| if (sim_tape_bot(uptr)) { | |
| sim_debug(DEBUG_CMD, dptr, "REW unit=%d at BOT\n", unit); | |
| uptr->u5 |= MT_RDY; | |
| uptr->u3 = 0; | |
| return SCPE_OK; | |
| } | |
| time = 1000; | |
| uptr->u5 |= MT_REW; | |
| mt_chan[chan] = MTC_BSY; | |
| sim_debug(DEBUG_CMD, dptr, "REW unit=%d\n", unit); | |
| sim_cancel(uptr); | |
| sim_activate(uptr, time); | |
| return SCPE_OK; | |
| case IO_RUN: | |
| uptr->u5 &= ~(MT_EOT|MT_MARK|MT_EGAP); | |
| chan_clear_status(chan); | |
| uptr->u5 |= MT_RUN; | |
| mt_chan[chan] = MTC_BSY; | |
| time = 1000; | |
| sim_debug(DEBUG_CMD, dptr, "RUN unit=%d\n", unit); | |
| sim_cancel(uptr); | |
| sim_activate(uptr, time); | |
| return SCPE_OK; | |
| case IO_SDL: | |
| uptr->u5 |= MT_RDY; /* Command is quick */ | |
| uptr->flags |= MTUF_LDN; | |
| sim_debug(DEBUG_CMD, dptr, "SDN unit=%d low\n", unit); | |
| return SCPE_OK; | |
| case IO_SDH: | |
| uptr->u5 |= MT_RDY; /* Command is quick */ | |
| uptr->flags &= ~MTUF_LDN; | |
| sim_debug(DEBUG_CMD, dptr, "SDN unit=%d high\n", unit); | |
| return SCPE_OK; | |
| case IO_DRS: | |
| uptr->flags &= ~MTUF_ONLINE; | |
| uptr->u5 |= MT_RDY; /* Command is quick */ | |
| sim_debug(DEBUG_CMD, dptr, "DRS unit=%d\n", unit); | |
| return SCPE_OK; | |
| case IO_TRS: | |
| uptr->u5 |= MT_RDY; /* Get here we are ready */ | |
| sim_debug(DEBUG_CMD, dptr, "TRS unit=%d\n", unit); | |
| return SCPE_OK; | |
| } | |
| sim_cancel(uptr); | |
| sim_activate(uptr, time); | |
| return SCPE_OK; | |
| } | |
| #if I7090 | I704 | I701 | |
| /* Read a word from tape, used during boot read */ | |
| int | |
| mt_read_buff(UNIT * uptr, int cmd, DEVICE * dptr, t_uint64 *word) | |
| { | |
| int chan = UNIT_G_CHAN(uptr->flags); | |
| int bufnum = GET_DEV_BUF(dptr->flags); | |
| int i; | |
| uint8 ch; | |
| int mode = 0; | |
| int mark = 1; | |
| int parity = 0; | |
| uptr->u5 &= ~MT_MARK; | |
| if (cmd == MT_RDS) | |
| mode = 0100; | |
| *word = 0; | |
| for(i = CHARSPERWORD-1; i >= 0 && uptr->u6 < (int32)uptr->hwmark; i--) { | |
| ch = mt_buffer[bufnum][uptr->u6++]; | |
| /* Do BCD translation */ | |
| if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { | |
| parity = 1; | |
| } | |
| ch &= 077; | |
| /* Not needed on decimal machines */ | |
| if (mode) { | |
| /* Map BCD to internal format */ | |
| ch ^= (ch & 020) << 1; | |
| if (ch == 012) | |
| ch = 0; | |
| if (ch == 017 && mark) { | |
| chan_set_error(chan); /* Force CRC error. */ | |
| ch = 0; | |
| mark = 0; | |
| uptr->u6++; /* Skip next character */ | |
| i--; | |
| } | |
| } | |
| if (i >= 0) | |
| *word |= ((t_uint64) ch) << (6 * i); | |
| } | |
| if (parity) { | |
| chan_set_error(chan); /* Force redundency error */ | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| #endif | |
| /* Map simH errors into machine errors */ | |
| t_stat mt_error(UNIT * uptr, int chan, t_stat r, DEVICE * dptr) | |
| { | |
| switch (r) { | |
| case MTSE_OK: /* no error */ | |
| break; | |
| case MTSE_TMK: /* tape mark */ | |
| sim_debug(DEBUG_EXP, dptr, "MARK "); | |
| chan_set_eof(chan); | |
| break; | |
| case MTSE_WRP: /* write protected */ | |
| case MTSE_UNATT: /* unattached */ | |
| sim_debug(DEBUG_EXP, dptr, "ATTENTION %d ", r); | |
| chan_set_attn(chan); | |
| break; | |
| case MTSE_IOERR: /* IO error */ | |
| case MTSE_FMT: /* invalid format */ | |
| case MTSE_RECE: /* error in record */ | |
| chan_set_error(chan); /* Force redundency error */ | |
| chan_set_attn(chan); /* Set error */ | |
| sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r); | |
| break; | |
| case MTSE_BOT: /* beginning of tape */ | |
| chan_set(chan, CHS_BOT); /* Set flag */ | |
| sim_debug(DEBUG_EXP, dptr, "BOT "); | |
| break; | |
| case MTSE_INVRL: /* invalid rec lnt */ | |
| case MTSE_EOM: /* end of medium */ | |
| uptr->u5 |= MT_EOT; | |
| sim_debug(DEBUG_EXP, dptr, "EOT "); | |
| #ifdef I7010 | |
| chan_set_attn(chan); /* Set error */ | |
| #endif | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Handle processing of tape requests. */ | |
| t_stat mt_srv(UNIT * uptr) | |
| { | |
| int chan = UNIT_G_CHAN(uptr->flags); | |
| DEVICE *dptr = find_dev_from_unit(uptr); | |
| int unit = (uptr - dptr->units) & MTC_UNIT; | |
| int cmd = uptr->u5 & MT_CMDMSK; | |
| int bufnum = GET_DEV_BUF(dptr->flags); | |
| t_mtrlnt reclen; | |
| t_stat r = SCPE_ARG; /* Force error if not set */ | |
| uint8 ch; | |
| int mode = 0; | |
| #ifdef I7010 | |
| extern uint8 astmode; | |
| #endif | |
| /* Call channel proccess to make sure data is ready */ | |
| chan_proc(); | |
| /* Channel has disconnected, abort current read. */ | |
| if ((mt_chan[chan] & 037) == (MTC_SEL | unit) && | |
| chan_stat(chan, DEV_DISCO)) { | |
| uptr->u5 &= ~MT_CMDMSK; | |
| reclen = uptr->hwmark; | |
| if (cmd == MT_WRS || cmd == MT_WRSB) { | |
| if (uptr->u6 > 0) { | |
| uptr->u3 += GAP_LEN; | |
| sim_debug(DEBUG_DETAIL, dptr, | |
| "Write flush unit=%d %s Block %d chars\n", | |
| unit, (cmd == MT_WRS) ? "BCD" : "Binary", reclen); | |
| r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen); | |
| mt_error(uptr, chan, r, dptr); /* Record errors */ | |
| } | |
| } else if (cmd == MT_RDS || cmd == MT_RDSB) { | |
| sim_debug(DEBUG_DETAIL, dptr, | |
| "Read flush unit=%d %s Block %d chars\n", | |
| unit, (cmd == MT_RDS) ? "BCD" : "Binary", reclen); | |
| /* Keep moving until end of block */ | |
| if (uptr->u6 < (int32)uptr->hwmark ) { | |
| reclen -= uptr->u6; | |
| uptr->u3 += reclen; | |
| uptr->u5 |= MT_SKIP|MT_IDLE; | |
| uptr->u6 = 0; | |
| uptr->hwmark = 0; | |
| chan_clear(chan, DEV_DISCO | DEV_WEOR); | |
| sim_activate(uptr, reclen * T1_us); | |
| return SCPE_OK; | |
| } else { | |
| #ifndef I7010 | |
| if (uptr->u5 & MT_MARK) { | |
| /* We hit tapemark, Back up so next read hits it */ | |
| /* Or write starts just before it */ | |
| /* This is due to SIMH returning mark after read */ | |
| (void) sim_tape_sprecr(uptr, &reclen); | |
| uptr->u5 &= ~MT_MARK; | |
| uptr->u3 -= GAP_LEN + reclen; | |
| } | |
| #endif | |
| } | |
| } | |
| /* Allow time for tape to be restarted, before stop */ | |
| sim_activate(uptr, us_to_ticks(500)); | |
| uptr->u6 = 0; | |
| uptr->hwmark = 0; | |
| sim_debug(DEBUG_CHAN, dptr, "Disconnect unit=%d\n", unit); | |
| uptr->u5 |= MT_IDLE|MT_RDY; | |
| mt_chan[chan] = 0; | |
| chan_clear(chan, DEV_DISCO | DEV_WEOR | DEV_SEL); | |
| #if I7010 | I7080 | |
| chan_clear(chan, STA_TWAIT); | |
| #endif | |
| return SCPE_OK; | |
| } | |
| uptr->u5 &= ~MT_IDLE; | |
| switch (cmd) { | |
| case 0: /* No command, stop tape */ | |
| uptr->u5 |= MT_RDY; /* Ready since command is done */ | |
| sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit); | |
| return SCPE_OK; | |
| case MT_SKIP: /* Record skip done, enable tape drive */ | |
| uptr->u5 &= ~MT_CMDMSK; | |
| uptr->u5 |= MT_RDY | MT_IDLE; | |
| #if I7090 | I704 | I701 | |
| chan_clear(chan, DEV_SEL); | |
| #else | |
| chan_clear(chan, DEV_SEL|STA_TWAIT); | |
| #endif | |
| mt_chan[chan] = 0; | |
| sim_debug(DEBUG_DETAIL, dptr, "Skip unit=%d\n", unit); | |
| /* Allow time for tape to be restarted, before stop */ | |
| sim_activate(uptr, us_to_ticks(500)); | |
| return SCPE_OK; | |
| case MT_RDS: | |
| mode = 0100; | |
| /* Fall through */ | |
| case MT_RDSB: | |
| /* Post EOR */ | |
| if (uptr->u5 & MT_EOR) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post EOR\n", unit); | |
| chan_set(chan, DEV_REOR); | |
| uptr->u5 &= ~ MT_EOR; | |
| sim_activate(uptr, T1_us); | |
| return SCPE_OK; | |
| } | |
| /* If tape mark pending, return it */ | |
| if (chan_test(chan, DEV_FULL) == 0 && uptr->u5 & MT_MARK) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post ", unit); | |
| uptr->u5 &= ~(MT_CMDMSK|MT_MARK); | |
| #ifdef I7010 | |
| if (astmode) { | |
| ch = mode?017:054; | |
| chan_write_char(chan, &ch, DEV_REOR); | |
| if (mode) { | |
| chan_clear(chan, STA_TWAIT); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return SCPE_OK; | |
| } | |
| } | |
| #endif | |
| chan_set_attn(chan); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return mt_error(uptr, chan, MTSE_TMK, dptr); | |
| } | |
| /* If at end of record, fill buffer */ | |
| if (uptr->u6 == uptr->hwmark) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit); | |
| uptr->u3 += GAP_LEN; | |
| if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen, | |
| BUFFSIZE)) != MTSE_OK) { | |
| if (r == MTSE_TMK && uptr->u6 != -1) { | |
| sim_debug(DEBUG_DETAIL, dptr, "pend TM\n"); | |
| uptr->u5 |= MT_MARK; | |
| r = MTSE_OK; | |
| } else { | |
| sim_debug(DEBUG_DETAIL, dptr, "error=%d\n", r); | |
| uptr->u5 &= ~MT_CMDMSK; | |
| #ifdef I7010 | |
| /* Translate TM characters for 7010 */ | |
| if (r == MTSE_TMK && astmode) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Read TM "); | |
| ch = mode?017:054; | |
| chan_write_char(chan, &ch, 0); | |
| chan_set_attn(chan); | |
| chan_set(chan, DEV_REOR); | |
| chan_clear(chan, STA_TWAIT); | |
| if (mode) { | |
| sim_activate(uptr, T1_us); | |
| return SCPE_OK; | |
| } | |
| chan_set_error(chan); | |
| } | |
| #else | |
| chan_set_attn(chan); | |
| #endif | |
| } | |
| sim_activate(uptr, T1_us); | |
| return mt_error(uptr, chan, r, dptr); | |
| } | |
| uptr->u6 = 0; | |
| uptr->hwmark = reclen; | |
| chan_clear(chan, CHS_EOF|CHS_ERR); | |
| sim_debug(DEBUG_DETAIL, dptr, "%s Block %d chars\n", | |
| (cmd == MT_RDS) ? "BCD" : "Binary", reclen); | |
| #ifdef I7010 | |
| if (mode && mt_buffer[bufnum][0] == 017) | |
| chan_set_eof(chan); | |
| #endif | |
| } | |
| ch = mt_buffer[bufnum][uptr->u6++]; | |
| uptr->u3++; | |
| /* Do BCD translation */ | |
| if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { | |
| #ifdef I7010 | |
| if (astmode) | |
| ch = 054; | |
| #else | |
| chan_set_attn(chan); | |
| #endif | |
| chan_set_error(chan); | |
| } | |
| #if I7090 | I704 | I701 | |
| /* Not needed on decimal machines */ | |
| if (mode) { | |
| /* Map BCD to internal format */ | |
| ch ^= (ch & 020) << 1; | |
| if (ch == 012) | |
| ch = 0; | |
| if (ch == 017) { | |
| chan_set_error(chan); /* Force CRC error. */ | |
| if ((uptr->u5 & MT_RM) == 0) { | |
| ch = 0; | |
| uptr->u5 |= MT_RM; | |
| mt_buffer[bufnum][uptr->u6] = 0; | |
| } | |
| } | |
| } | |
| #endif | |
| #ifdef I7010 | |
| if (mode) { | |
| if (ch == 0120) | |
| ch = 0; | |
| } | |
| #endif | |
| ch &= 077; | |
| /* Convert one word. */ | |
| switch (chan_write_char(chan, &ch, 0)) { | |
| case END_RECORD: | |
| sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit); | |
| /* If not read whole record, skip till end */ | |
| uptr->u5 |= MT_EOR; | |
| if (uptr->u6 < (int32)uptr->hwmark) { | |
| sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us); | |
| uptr->u3 += (uptr->hwmark - uptr->u6); | |
| uptr->u6 = uptr->hwmark; /* Force read next record */ | |
| } | |
| sim_activate(uptr, T1_us); | |
| break; | |
| case DATA_OK: | |
| sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n", | |
| unit, uptr->u6, ch); | |
| if (uptr->u6 >= (int32)uptr->hwmark) /* In IRG */ | |
| uptr->u5 |= MT_EOR; | |
| sim_activate(uptr, T1_us); | |
| break; | |
| case TIME_ERROR: | |
| sim_debug(DEBUG_DATA, dptr, "Read unit=%d timeout\n", unit); | |
| uptr->u3 += (uptr->hwmark - uptr->u6); | |
| uptr->u5 &= ~MT_CMDMSK; | |
| uptr->u5 |= MT_SKIP; | |
| sim_activate(uptr, ((uptr->hwmark - uptr->u6) * T1_us) + T2_us); | |
| uptr->u6 = uptr->hwmark; /* Force read next record */ | |
| break; | |
| } | |
| return SCPE_OK; | |
| /* Check mode */ | |
| case MT_WRS: | |
| mode = 0100; | |
| /* fall through */ | |
| case MT_WRSB: | |
| if (uptr->u5 & MT_EGAP) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d\n", unit); | |
| uptr->u5 &= ~MT_EGAP; | |
| r = sim_tape_wrgap(uptr, 35); | |
| sim_activate(uptr, 10*T3_us); | |
| return SCPE_OK; | |
| } | |
| switch (chan_read_char(chan, &ch, | |
| (uptr->u6 > BUFFSIZE) ? DEV_WEOR : 0)) { | |
| case TIME_ERROR: | |
| #if I7090 | I701 | I704 | |
| /* If no data was written, simulate a write gap */ | |
| if (uptr->u6 == 0) { | |
| r = sim_tape_wrgap(uptr, 35); | |
| if (r != MTSE_OK) { | |
| mt_error(uptr, chan, r, dptr); /* Record errors */ | |
| return SCPE_OK; | |
| } | |
| } | |
| #endif | |
| chan_set_attn(chan); | |
| /* fall through */ | |
| case END_RECORD: | |
| if (uptr->u6 > 0) { /* Only if data in record */ | |
| reclen = uptr->hwmark; | |
| sim_debug(DEBUG_DETAIL, dptr, | |
| "Write unit=%d %s Block %d chars\n", | |
| unit, (cmd == MT_WRS) ? "BCD" : "Binary", reclen); | |
| r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen); | |
| uptr->u3 += GAP_LEN; | |
| uptr->u6 = 0; | |
| uptr->hwmark = 0; | |
| mt_error(uptr, chan, r, dptr); /* Record errors */ | |
| } | |
| sim_activate(uptr, T2_us); | |
| return SCPE_OK; | |
| case DATA_OK: | |
| /* Copy data to buffer */ | |
| ch &= 077; | |
| #if I7090 | I701 | I704 | |
| /* Not needed on decimal machines */ | |
| if (mode) { | |
| /* Do BCD translation */ | |
| ch ^= (ch & 020) << 1; | |
| if (ch == 0) | |
| ch = 012; | |
| } | |
| #endif | |
| ch |= mode ^ parity_table[ch] ^ 0100; | |
| mt_buffer[bufnum][uptr->u6++] = ch; | |
| uptr->u3++; | |
| sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %02o\n", | |
| unit, uptr->u6, ch); | |
| uptr->hwmark = uptr->u6; | |
| break; | |
| } | |
| sim_activate(uptr, T1_us); | |
| return SCPE_OK; | |
| case MT_RDB: | |
| /* If tape mark pending, return it */ | |
| if (chan_test(chan, DEV_FULL) == 0 && uptr->u5 & MT_MARK) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post ", unit); | |
| uptr->u5 &= ~(MT_CMDMSK|MT_MARK); | |
| mt_chan[chan] &= MTC_BSY; | |
| chan_clear(chan, DEV_SEL); | |
| sim_activate(uptr, us_to_ticks(100)); | |
| return mt_error(uptr, chan, MTSE_TMK, dptr); | |
| } | |
| /* If at end of record, fill buffer */ | |
| if (uptr->u6 == uptr->hwmark) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit); | |
| if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen, | |
| BUFFSIZE)) != MTSE_OK) { | |
| uptr->u3 -= GAP_LEN; | |
| sim_activate(uptr, T2_us); | |
| if (r == MTSE_TMK && uptr->u6 != -1) { | |
| sim_debug(DEBUG_DETAIL, dptr, "pend TM\n"); | |
| uptr->u5 |= MT_MARK; | |
| r = MTSE_OK; | |
| } else { | |
| sim_debug(DEBUG_DETAIL, dptr, "error=%d\n", r); | |
| uptr->u5 &= ~MT_CMDMSK; | |
| chan_set_attn(chan); | |
| chan_clear(chan, DEV_SEL); | |
| mt_chan[chan] &= MTC_BSY; | |
| } | |
| return mt_error(uptr, chan, r, dptr); | |
| } | |
| uptr->u6 = 0; | |
| uptr->hwmark = reclen; | |
| chan_clear(chan, CHS_EOF|CHS_ERR); | |
| sim_debug(DEBUG_DETAIL, dptr, "Binary Block %d chars\n", reclen); | |
| } | |
| ch = mt_buffer[bufnum][uptr->u6++]; | |
| uptr->u3--; | |
| /* Do BCD translation */ | |
| if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { | |
| chan_set_error(chan); | |
| chan_set_attn(chan); | |
| } | |
| ch &= 077; | |
| /* Convert one word. */ | |
| switch (chan_write_char(chan, &ch, | |
| (uptr->u6 >= (int32)uptr->hwmark) ? DEV_REOR : 0)) { | |
| case END_RECORD: | |
| sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit); | |
| if (uptr->u6 >= (int32)uptr->hwmark) { | |
| uptr->u5 &= ~MT_CMDMSK; | |
| uptr->u5 |= MT_SKIP; | |
| uptr->u3 -= (uptr->hwmark-uptr->u6); | |
| sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us); | |
| chan_set(chan, DEV_REOR); | |
| uptr->u6 = uptr->hwmark; /* Force read next record */ | |
| break; | |
| } | |
| /* fall through */ | |
| case DATA_OK: | |
| sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n", | |
| unit, uptr->u6, ch); | |
| if (uptr->u6 >= (int32)uptr->hwmark) { /* In IRG */ | |
| uptr->u3 -= (uptr->hwmark-uptr->u6); | |
| sim_activate(uptr, T2_us); | |
| } else | |
| sim_activate(uptr, T1_us); | |
| break; | |
| case TIME_ERROR: | |
| uptr->u5 &= ~MT_CMDMSK; | |
| uptr->u5 |= MT_SKIP; | |
| uptr->u3 -= (uptr->hwmark-uptr->u6); | |
| sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us); | |
| uptr->u6 = uptr->hwmark; /* Force read next record */ | |
| break; | |
| } | |
| return SCPE_OK; | |
| case MT_WEF: | |
| if (uptr->u5 & MT_EGAP) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d\n", unit); | |
| uptr->u5 &= ~MT_EGAP; | |
| r = sim_tape_wrgap(uptr, 35); | |
| sim_activate(uptr, 10*T3_us); | |
| return SCPE_OK; | |
| } | |
| sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit); | |
| uptr->u5 &= ~(MT_CMDMSK|MT_MARK); | |
| uptr->u5 |= (MT_RDY); | |
| r = sim_tape_wrtmk(uptr); | |
| uptr->u3 += GAP_LEN; | |
| mt_chan[chan] &= ~MTC_BSY; | |
| sim_activate(uptr, T2_us); | |
| #if I7010 | I7080 | |
| chan_set(chan, DEV_REOR); | |
| #endif | |
| break; | |
| case MT_BSR: | |
| sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d ", unit); | |
| /* Clear tape mark, command, idle since we will need to change dir */ | |
| uptr->u5 &= ~(MT_CMDMSK | MT_EOT | MT_RDY); | |
| r = sim_tape_sprecr(uptr, &reclen); | |
| if (r != MTSE_BOT) | |
| uptr->u3 -= GAP_LEN; | |
| mt_chan[chan] &= ~MTC_BSY; | |
| if (r == MTSE_TMK) { | |
| #ifdef I7080 | |
| chan_set_eof(chan); | |
| #else | |
| /* We don't set EOF on BSR */ | |
| #endif | |
| sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); | |
| sim_activate(uptr, T2_us); | |
| return SCPE_OK; | |
| } | |
| sim_debug(DEBUG_DETAIL, dptr, "%d \n", reclen); | |
| uptr->u3 -= reclen; | |
| sim_activate(uptr, T2_us + (reclen * T1_us)); | |
| return SCPE_OK; | |
| case MT_BSF: | |
| uptr->u5 &= ~(MT_IDLE | MT_RDY | MT_EOT); | |
| r = sim_tape_sprecr(uptr, &reclen); | |
| if (r != MTSE_BOT) | |
| uptr->u3 -= GAP_LEN; | |
| /* If we hit mark or end of tape */ | |
| if (r == MTSE_TMK || r == MTSE_BOT) { | |
| sim_debug(DEBUG_DETAIL, dptr, "Backspace file unit=%d\n", unit); | |
| uptr->u5 &= ~MT_CMDMSK; | |
| mt_chan[chan] &= ~MTC_BSY; | |
| sim_activate(uptr, T2_us); | |
| } else { | |
| uptr->u3 -= reclen; | |
| sim_activate(uptr, T2_us + (reclen * T1_us)); | |
| } | |
| return SCPE_OK; | |
| case MT_SKR: | |
| sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit); | |
| /* Clear tape mark, command, idle since we will need to change dir */ | |
| uptr->u5 &= ~(MT_CMDMSK | MT_EOT); | |
| uptr->u5 |= (MT_RDY | MT_IDLE); | |
| r = sim_tape_sprecf(uptr, &reclen); | |
| uptr->u3 += GAP_LEN; | |
| mt_chan[chan] &= ~MTC_BSY; | |
| #if I7010 | I7080 | |
| chan_clear(chan, STA_TWAIT); | |
| #endif | |
| #ifdef I7010 | |
| chan_set(chan, STA_PEND); | |
| #else | |
| /* We are like read that transfers nothing */ | |
| chan_set(chan, DEV_REOR); | |
| #endif | |
| /* We don't set EOF on SKR */ | |
| if (r == MTSE_TMK) { | |
| sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); | |
| sim_activate(uptr, T1_us); | |
| return SCPE_OK; | |
| #ifdef I7010 | |
| } else if (r == MTSE_EOM) { | |
| chan_set(chan, STA_PEND); | |
| #endif | |
| } | |
| sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen); | |
| uptr->u3 += reclen; | |
| sim_activate(uptr, (reclen * T1_us)); | |
| break; | |
| case MT_ERG: | |
| sim_debug(DEBUG_DETAIL, dptr, "Erase unit=%d\n", unit); | |
| uptr->u5 &= ~(MT_CMDMSK|MT_MARK); | |
| uptr->u5 |= (MT_RDY | MT_IDLE); | |
| #if I7010 | I7080 | |
| chan_clear(chan, STA_TWAIT); | |
| #endif | |
| r = sim_tape_wrgap(uptr, 35); | |
| uptr->u3 += GAP_LEN; | |
| mt_chan[chan] &= ~MTC_BSY; | |
| sim_activate(uptr, 10*T3_us); | |
| break; | |
| case MT_REW: | |
| sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d %d %d\n", unit, uptr->u3, | |
| uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200); | |
| uptr->u5 &= ~(MT_CMDMSK | MT_IDLE | MT_RDY); | |
| if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) { | |
| uptr->u5 |= MT_HREW; | |
| sim_activate(uptr, us_to_ticks(5000000)); | |
| } else { | |
| uptr->u5 |= MT_LREW; | |
| sim_activate(uptr, 300); | |
| } | |
| mt_chan[chan] &= ~MTC_BSY; | |
| break; | |
| case MT_RUN: | |
| sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit); | |
| uptr->u5 &= ~(MT_CMDMSK | MT_IDLE | MT_RDY); | |
| uptr->u5 |= MT_UNLOAD; | |
| if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) { | |
| uptr->u5 |= MT_HREW; | |
| sim_activate(uptr, us_to_ticks(5000000)); | |
| } else { | |
| uptr->u5 |= MT_LREW; | |
| sim_activate(uptr, 300); | |
| } | |
| mt_chan[chan] &= ~MTC_BSY; | |
| return SCPE_OK; | |
| case MT_HREW: | |
| sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d HS\n", unit); | |
| if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) { | |
| uptr->u3 -= (uptr->flags & MTUF_LDN) ? 1666 :4625; | |
| sim_activate(uptr, us_to_ticks(16000)); | |
| } else { | |
| uptr->u5 &= ~(MT_CMDMSK); | |
| uptr->u5 |= MT_LREW; | |
| sim_activate(uptr, us_to_ticks(5000000)); | |
| } | |
| return SCPE_OK; | |
| case MT_LREW: | |
| sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d LS\n", unit); | |
| if (uptr->u3 > 0) { | |
| uptr->u3 -= (uptr->flags & MTUF_LDN) ? 373 :1036; | |
| sim_activate(uptr, us_to_ticks(16000)); | |
| return SCPE_OK; | |
| } else { | |
| if(uptr->u5 & MT_UNLOAD) | |
| r = sim_tape_detach(uptr); | |
| else | |
| r = sim_tape_rewind(uptr); | |
| uptr->u5 &= ~(MT_CMDMSK|MT_UNLOAD); | |
| uptr->u5 |= MT_RDY; | |
| uptr->u3 = 0; | |
| } | |
| break; | |
| } | |
| return mt_error(uptr, chan, r, dptr); | |
| } | |
| /* Boot from given device */ | |
| t_stat | |
| mt_boot(int32 unit_num, DEVICE * dptr) | |
| { | |
| UNIT *uptr = &dptr->units[unit_num]; | |
| uint16 dev = unit_num + 020 + mt_dib.addr; | |
| #if I7090 | I704 | I701 | |
| t_mtrlnt reclen; | |
| t_stat r; | |
| #endif | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_UNATT; /* attached? */ | |
| /* Start a read. */ | |
| if (mt_cmd(dptr->units, IO_RDS, dev) != SCPE_OK) | |
| return STOP_IONRDY; | |
| #if I7090 | I704 | I701 | |
| r = sim_tape_rdrecf(uptr, &mt_buffer[GET_DEV_BUF(dptr->flags)][0], &reclen, | |
| BUFFSIZE); | |
| if (r != SCPE_OK) | |
| return r; | |
| uptr->u6 = 0; | |
| uptr->hwmark = reclen; | |
| /* Copy first three records. */ | |
| mt_read_buff(uptr, MT_RDSB, dptr, &M[0]); | |
| mt_read_buff(uptr, MT_RDSB, dptr, &M[1]); | |
| if (UNIT_G_CHAN(uptr->flags) != 0) | |
| mt_read_buff(uptr, MT_RDSB, dptr, &M[2]); | |
| /* Make sure channel is set to start reading rest. */ | |
| #endif | |
| return chan_boot(unit_num, dptr); | |
| } | |
| void | |
| mt_ini(UNIT * uptr, t_bool f) | |
| { | |
| int chan = UNIT_G_CHAN(uptr->flags); | |
| if (uptr->flags & UNIT_ATT) | |
| uptr->u5 = MT_RDY; | |
| else | |
| uptr->u5 = 0; | |
| uptr->u3 = 0; | |
| mt_chan[chan] = 0; | |
| } | |
| t_stat | |
| mt_reset(DEVICE * dptr) | |
| { | |
| UNIT *uptr = dptr->units; | |
| uint32 i; | |
| for (i = 0; i < dptr->numunits; i++) { | |
| sim_tape_set_dens (uptr, | |
| ((uptr->flags & MTUF_LDN) ? MT_DENS_200 : MT_DENS_556), | |
| NULL, NULL); | |
| uptr++; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| mt_tape_density(UNIT * uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| mt_attach(UNIT * uptr, CONST char *file) | |
| { | |
| t_stat r; | |
| if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) | |
| return r; | |
| uptr->u3 = 0; | |
| uptr->u5 |= MT_RDY; | |
| uptr->flags |= MTUF_ONLINE; | |
| uptr->dynflags = MT_200_VALID | MT_556_VALID | | |
| (((uptr->flags & MTUF_LDN) ? MT_556_VALID : MT_200_VALID) < UNIT_V_DF_TAPE); | |
| return SCPE_OK; | |
| } | |
| t_stat | |
| mt_detach(UNIT * uptr) | |
| { | |
| uptr->u3 = 0; | |
| uptr->u5 = 0; | |
| uptr->flags &= ~MTUF_ONLINE; | |
| return sim_tape_detach(uptr); | |
| } | |
| t_stat | |
| mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| fprintf (st, "%s\n\n", mt_description(dptr)); | |
| fprintf (st, "The magnetic tape controller assumes that all tapes are 7 track\n"); | |
| fprintf (st, "with valid parity. Tapes are assumed to be 555.5 characters per\n"); | |
| fprintf (st, "inch. To simulate a standard 2400foot tape, do:\n\n"); | |
| fprintf (st, " sim> SET %s LENGTH 15\n\n", dptr->name); | |
| fprintf (st, "The mag tape drives support the BOOT command\n\n"); | |
| help_set_chan_type(st, dptr, "Mag tape"); | |
| sim_tape_attach_help (st, dptr, uptr, flag, cptr); | |
| fprint_set_help(st, dptr); | |
| fprint_show_help(st, dptr); | |
| return SCPE_OK; | |
| } | |
| const char * | |
| mt_description(DEVICE *dptr) | |
| { | |
| return "IBM 729 Magnetic tape unit"; | |
| } | |
| #endif |