| /* | |
| Copyright (c) 2015-2016, John Forecast | |
| 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 | |
| JOHN FORECAST 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 John Forecast shall not | |
| be used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from John Forecast. | |
| */ | |
| /* cdc1700_mt.c: 1732-A/B and 1732-3 magtape device support | |
| * Simh devices: mt0, mt1, mt2, mt3 | |
| */ | |
| /* | |
| * Notes: | |
| * | |
| * 1. When writing a tape record in direct mode (programmed I/O), the | |
| * documentation states "Whenever the computer breaks the continuity of the | |
| * computer word outputs, the controller initiates an End of Record sequence." | |
| * | |
| * Since there is no explicit command sequence to initiate an End of Record | |
| * operation, we need to estimate how many instructions to delay after a | |
| * data OUT operation before terminating the current record. The tape drives | |
| * operate at a maximum of 37.5 inches per second, so given the recording | |
| * density we can compute the maximum output data rate and hence the time | |
| * delay between 2 characters written to the tape. In addition, since we are | |
| * emulating a 1732 controller, we need to take into account the character | |
| * assembly operating mode where each data OUT instruction writes 2 chatacters | |
| * to the tape. If we assume an average execution time of 1 microsecond per | |
| * instruction (to keep the arithmetic simple), we get the following table: | |
| * | |
| * Density (bpi) Data Rate Instr. Delay Char Assembly Delay | |
| * | |
| * 200 7.50K char/sec 134 Instrs. 268 Instrs. | |
| * 556 20.85K char/sec 48 Instrs. 96 Instrs. | |
| * 800 30.00K char/sec 33 Instrs. 66 Instrs. | |
| * 1600 60.00K char/sec 16 Instrs. 33 Instrs. | |
| * | |
| * The emulation does not need to be very strict with regard to timing: | |
| * | |
| * - Using instruction counts is not a very accurate representation of | |
| * real time. 1784-2 instruction execution times range from 0.600 to | |
| * 12.8 uSec so timing can easily be off by almost a factor of 2. | |
| * See definition of LOOSETIMING below. | |
| * | |
| * - This does mean that SMM17 timing diagnostics may fail since SMM | |
| * uses a timing loop of it's own. | |
| * | |
| * The PET diagnostic implies that the interrupt rate is too high for 1600 BPI | |
| * access to be supported in direct mode. | |
| * | |
| * 2. This driver supports 3 modes of access: | |
| * | |
| * 1 - Direct mode (programmed I/O) | |
| * 2 - Buffered mode (DSA or DMA mode) | |
| * 3 - 1706 buffered data channel access | |
| * | |
| * The buffered data channel access is only supported if the controller is | |
| * configured as a 1732-A (set mt type=1732-A). | |
| * | |
| */ | |
| #include <string.h> | |
| #include "cdc1700_defs.h" | |
| #include "sim_tape.h" | |
| #define LOOSETIMING(t) (((t) * 3)/ 2) | |
| #define DEVTYPE_1732_A IOtype_dev1 /* Controller is 1732-A */ | |
| #define DEVTYPE_1732_3 IOtype_dev2 /* Controller is 1732-3 */ | |
| #define STATUS2 iod_readR[2] | |
| #define BUFFEREDIO iod_writeR[3] | |
| #define CURADDRESS iod_readR[3] | |
| extern char INTprefix[]; | |
| extern uint16 LoadFromMem(uint16); | |
| extern t_bool IOStoreToMem(uint16, uint16, t_bool); | |
| extern t_bool doDirectorFunc(DEVICE *, t_bool); | |
| extern void fw_IOcompleteEOP(t_bool, DEVICE *, IO_DEVICE *, uint16, const char *); | |
| extern void fw_IOunderwayEOP(IO_DEVICE *, uint16); | |
| extern void fw_IOintr(t_bool, DEVICE *, IO_DEVICE *, uint16, uint16, uint16, const char *); | |
| extern t_bool fw_reject(IO_DEVICE *, t_bool, uint8); | |
| extern void fw_setForced(IO_DEVICE *, uint16); | |
| extern void fw_clearForced(IO_DEVICE *, uint16); | |
| extern void loadBootstrap(uint16 *, int, uint16, uint16); | |
| extern t_stat checkReset(DEVICE *, uint8); | |
| extern t_stat show_addr(FILE *, UNIT *, int32, CONST void *); | |
| extern t_stat set_protected(UNIT *, int32, CONST char *, void *); | |
| extern t_stat clear_protected(UNIT *, int32, CONST char *, void *); | |
| extern t_stat set_stoponrej(UNIT *, int32, CONST char *, void *); | |
| extern t_stat clr_stoponrej(UNIT *, int32, CONST char *, void *); | |
| extern t_stat set_equipment(UNIT *, int32, CONST char *, void *); | |
| extern void buildDCtables(void); | |
| extern uint16 M[], Areg, IOAreg; | |
| extern t_uint64 Instructions; | |
| extern t_bool IOFWinitialized; | |
| extern UNIT cpu_unit; | |
| t_stat mt_show_transport(FILE *, UNIT *, int32, CONST void *); | |
| t_stat mt_show_type(FILE *, UNIT *, int32, CONST void *); | |
| t_stat mt_set_type(UNIT *, int32, CONST char *, void *); | |
| #define DENS u3 | |
| /* | |
| * Nine-track magnetic tape bootstrap | |
| */ | |
| static uint16 mtbootstrap9[] = { | |
| 0x6819, /* 00: STA* $19 */ | |
| 0x6819, /* 01: STA* $19 */ | |
| 0xE000, /* 02: LDQ+ $382 */ | |
| 0x0382, /* 0x1382 for 1706 No. 1 */ | |
| 0xC813, /* 04: LDA* $13 */ | |
| 0x03FE, /* 05: OUT $-1 */ | |
| 0x0DFE, /* 06: INQ $-1 */ | |
| 0xC811, /* 07: LDA* $11 */ | |
| 0x03FE, /* 08: OUT $-1 */ | |
| 0x0DFE, /* 09: INQ $-1 */ | |
| 0x0203, /* 0A: INP $03 */ | |
| 0x6C0F, /* 0B: STA* ($0F) */ | |
| 0xD80E, /* 0C: RAO* $0E */ | |
| 0x18FC, /* 0D: JMP* $FC */ | |
| 0x0D01, /* 0E: INQ $1 */ | |
| 0x0B00, /* 0F: NOP */ | |
| 0x02FE, /* 10: INP $-1 */ | |
| 0x0FCB, /* 11: ALS $0B */ | |
| 0x0131, /* 12: SAM $1 */ | |
| 0x18F5, /* 13: JMP* $F5 */ | |
| 0xC804, /* 14: LDA* $04 */ | |
| 0x03FE, /* 15: OUT $-1 */ | |
| 0x1C03, /* 16: JMP* ($03) */ | |
| 0x044C, /* 17: DATA */ | |
| 0x0100, /* 18: */ | |
| 0x0000, /* 19: */ | |
| 0x0000 /* 1A: */ | |
| }; | |
| #define MTBOOTLEN9 (sizeof(mtbootstrap9) / sizeof(uint16)) | |
| /* | |
| * Seven-track magnetic tape bootstrap | |
| */ | |
| static uint16 mtbootstrap7[] = { | |
| 0x0500, /* 00: IIN */ | |
| 0x6824, /* 01: STA* $24 */ | |
| 0x6824, /* 02: STA* $24 */ | |
| 0xE000, /* 03: LDQ+ $0382 */ | |
| 0x0382, /* 0x1382 for 1706 No. 1 */ | |
| 0xC81E, /* 05: LDA* $1E */ | |
| 0x03FE, /* 06: OUT $-1 */ | |
| 0x0DFE, /* 07: INQ $-1 */ | |
| 0xC81C, /* 08: LDA* $1C */ | |
| 0x03FE, /* 09: OUT $-1 */ | |
| 0x0DFE, /* 0A: INQ $-1 */ | |
| 0x0A00, /* 0B: ENA $00 */ | |
| 0x020D, /* 0C: INP $0D */ | |
| 0x0FCA, /* 0D: ALS $0A */ | |
| 0x0821, /* 0E: */ | |
| 0x0A00, /* 0F: ENA $00 */ | |
| 0x02FE, /* 10: INP $-1 */ | |
| 0x0FC4, /* 11: ALS $04 */ | |
| 0x0869, /* 12: EAM M */ | |
| 0x0A00, /* 13: ENA $00 */ | |
| 0x02FE, /* 14: INP $-1 */ | |
| 0x0F42, /* 15: ARS $02 */ | |
| 0x086C, /* 16: EAM A */ | |
| 0x6C0F, /* 17: STA* ($0F) */ | |
| 0xD80E, /* 18: RAO* $0E */ | |
| 0x18F1, /* 19: JMP* $F1 */ | |
| 0x0D01, /* 1A: */ | |
| 0x0B00, /* 1B: */ | |
| 0x02FE, /* 1C: */ | |
| 0x0FCB, /* 1D: */ | |
| 0x0131, /* 1E: */ | |
| 0x18EA, /* 1F: */ | |
| 0xC804, /* 20: */ | |
| 0x03FE, /* 21: */ | |
| 0x1C03, /* 22: */ | |
| 0x0414, /* 23: */ | |
| 0x0100, /* 24: */ | |
| 0x0000, /* 25: */ | |
| 0x0000 /* 26: */ | |
| }; | |
| #define MTBOOTLEN7 (sizeof(mtbootstrap7) / sizeof(uint16)) | |
| /* | |
| * SMM17 bootstraps | |
| */ | |
| static uint16 smm17boot9[] = { | |
| 0x68FE, /* xFE0: MTBOOT STA* *-1 */ | |
| 0xE000, /* xFE1: LDQ =N$WESD */ | |
| 0x0382, /* xFE2: EQUIP $382 */ | |
| 0xC000, /* xFE3: LDA =N$44C */ | |
| 0x044C, | |
| 0x03FE, /* xFE5: OUT -1 */ | |
| 0x09B3, /* xFE6: INA -$400-$44C */ | |
| 0x0DFE, /* xFE7: INQ -1 */ | |
| 0x03FE, /* xFE8: OUT -1 */ | |
| 0x0F42, /* xFE9: ARS 2 */ | |
| 0x03FE, /* xFEA: OUT -1 */ | |
| 0x0DFE, /* xFEB: INQ -1 */ | |
| 0x02FE, /* xFEC: MT1 INP -1 */ | |
| 0x6CF1, /* xFED: STA* (MTBOOT-1) */ | |
| 0x0102, /* xFEE: SAZ ENDBT-*-1 */ | |
| 0xD8EF, /* xFEF: RAO* MTBOOT-1 */ | |
| 0x18FB, /* xFF0: JMP* MT1 */ | |
| 0x1007 /* xFF1: ENDBT JMP- QL ENTRY */ | |
| }; | |
| #define SMM17BOOTLEN9 (sizeof(smm17boot9) / sizeof(uint16)) | |
| static uint16 smm17boot7[] = { | |
| 0x68FE, /* xFE0: MTBOOT STA* *-1 */ | |
| 0xE000, /* xFE1: LDQ =N$WESD */ | |
| 0x0382, /* xFE2: EQUIP $382 */ | |
| 0xC000, /* xFE3: LDA =N$405 */ | |
| 0x0405, | |
| 0x03FE, /* xFE5: OUT -1 */ | |
| 0x09FB, /* xFE6: INA -4 */ | |
| 0x0DFE, /* xFE7: INQ -1 */ | |
| 0x03FE, /* xFE8: OUT -1 */ | |
| 0x0F42, /* xFE9: ARS 2 */ | |
| 0x03FE, /* xFEA: OUT -1 */ | |
| 0x0DFE, /* xFEB: INQ -1 */ | |
| 0x0A00, /* xFEC: ENA 0 */ | |
| 0x1807, /* xFED: JMP- MT2 */ | |
| 0x02FE, /* xFEE: MT1 INP -1 */ | |
| 0x0F42, /* xFEF: ARS 2 */ | |
| 0xBCEE, /* xFF0: EOR* (MTBOOT-1) */ | |
| 0x010A, /* xFF1: SAZ ENDBT-*-1 */ | |
| 0x7CEC, /* xFF2: SPA* (MTBOOT-1) */ | |
| 0xD8EB, /* xFF3: RAO* MTBOOT-1 */ | |
| 0x02FE, /* xFF4: MT2 INP -1 */ | |
| 0x0FCA, /* xFF5: ALS 10 */ | |
| 0x7CE8, /* xFF6: SPA* (MTBOOT-1) */ | |
| 0x02FE, /* xFF7: INP -1 */ | |
| 0x0FC4, /* xFF8: ALS 4 */ | |
| 0xBCE5, /* xFF9: EOR* (MTBOOT-1) */ | |
| 0x7CE4, /* xFFA: SPA* (MTBOOT-1) */ | |
| 0x18F2, /* xFFB: JMP* MT1 */ | |
| 0x1007 /* xFFC: JMP- QL ENTRY */ | |
| }; | |
| #define SMM17BOOTLEN7 (sizeof(smm17boot7) / sizeof(uint16)) | |
| /* | |
| * Shared I/O buffer. Note that this is larger than the max possible memory | |
| * so the only way to handle such large records is to use non-DMA with | |
| * dynamic processing of the data. | |
| */ | |
| #define MTSIZ 131072 | |
| uint8 MTbuf[MTSIZ]; | |
| t_mtrlnt MToffset, MTremain; | |
| static enum { MT_IDLE, MT_READING, MT_WRITING, MT_READTMO, MT_WRITETMO, MT_DSADONE } MTmode; | |
| t_stat mt_svc(UNIT *); | |
| t_stat mt_reset(DEVICE *); | |
| t_stat mt_boot(int32, DEVICE *); | |
| t_stat mt_attach(UNIT *, CONST char *); | |
| t_stat mt_detach(UNIT *); | |
| t_stat mt_vlock(UNIT *, int32 val, CONST char *cptr, void *desc); | |
| void MTstate(const char *, DEVICE *, IO_DEVICE *); | |
| void MTclear(DEVICE *); | |
| t_bool MTreject(IO_DEVICE *, t_bool, uint8); | |
| enum IOstatus MTin(IO_DEVICE *, uint8); | |
| enum IOstatus MTout(IO_DEVICE *, uint8); | |
| enum IOstatus MTBDCin(IO_DEVICE *, uint16 *, uint8); | |
| enum IOstatus MTBDCout(IO_DEVICE *, uint16 *, uint8); | |
| t_stat mt_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
| /* | |
| 1732-3 Magnetic Tape Controller | |
| Addresses | |
| Computer Instruction | |
| Q Register Output From A Input to A | |
| 00 Write Read | |
| 01 Control Function Director Status 1 | |
| 10 Unit Select Director Status 2 | |
| 11 Buffered I/O Current Address | |
| Operations: | |
| Control Function | |
| 15 11 10 9 8 7 6 5 4 3 2 1 0 | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | X | X | X | X | X | | | | | X | X | | | | | | | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
| +-----------+ | | | | Clr Controller | |
| | | | | Clr Interrupts | |
| | | | Data Interrupt Req. | |
| | | Interrupt on EOP | |
| | Interrupt on Alarm | |
| | | |
| Motion Control: | |
| 0001 Write Motion | |
| 0010 Read Motion | |
| 0011 Backspace | |
| 0101 Write File Mark/Tape Mark | |
| 0110 Search File Mark/Tape Mark Forward | |
| 0111 Search File Mark/Tape Mark Backward | |
| 1000 Rewind Load | |
| 1100 Rewind Unload (1732-A only) | |
| Unit Select | |
| 15 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | X | X | X | | | | | | | | | | | | | | | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | | | | | | | | | | | | | | |
| | | | | +---+ | | | | | | Character Mode | |
| | | | | | | | | | | BCD mode | |
| | | | | | | | | | Binary mode | |
| | | | | | | | | Select 800 BPI | |
| | | | | | | | Select 556 BPI | |
| | | | | | | Select 1600 BPI (1732-A 200 BPI) | |
| | | | | | Assembly/Disassembly | |
| | | | | Tape Unit (0-3) | |
| | | | (1732-A only, additional unit select bit) | |
| | | Select Tape Unit | |
| | Deselect Tape Unit | |
| Select Low Read Threshold (1733-3 only) | |
| Status Response: | |
| Director Status 1 | |
| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | | | | | | | | | | | | | | | | | | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | Ready | |
| | | | | | | | | | | | | | | Busy | |
| | | | | | | | | | | | | | Interrupt | |
| | | | | | | | | | | | | Data | |
| | | | | | | | | | | | End of Operation | |
| | | | | | | | | | | Alarm | |
| | | | | | | | | | Lost Data | |
| | | | | | | | | Protected | |
| | | | | | | | Parity Error | |
| | | | | | | End of Tape | |
| | | | | | Beginning of Tape | |
| | | | | File Mark | |
| | | | Controller Active | |
| | | Fill | |
| | Storage Parity Error (1733-3 only) | |
| Protect Fault (1733-3 only) | |
| Director Status 2 | |
| 15 9 8 7 6 5 4 3 2 1 0 | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | X | X | X | X | X | X | | | | | | | | | | | | |
| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| | | | | | | | | | | | |
| | | | | | | | | | 556 BPI | |
| | | | | | | | | 800 BPI | |
| | | | | | | | 1600 BPI (1733-3 only) | |
| | | | | | | Seven Track | |
| | | | | | Write Enable | |
| | | | | PE - Warning | |
| | | | PE - Lost Data | |
| | | PE - transport | |
| | ID - Abort | |
| Low Read Threshold | |
| */ | |
| IO_DEVICE MTdev = IODEV(NULL, "Magtape", 1732, 7, 0xFF, 0, | |
| MTreject, MTin, MTout, MTBDCin, MTBDCout, | |
| MTstate, NULL, NULL, MTclear, | |
| 0x7F, 4, | |
| MASK_REGISTER0 | MASK_REGISTER1 | MASK_REGISTER2 | \ | |
| MASK_REGISTER3, | |
| MASK_REGISTER1 | MASK_REGISTER2 | MASK_REGISTER3, | |
| 0, 0, 0, 1, NULL); | |
| /* | |
| * Define usage for "private" IO_DEVICE data areas. | |
| */ | |
| #define iod_mode iod_private /* operating mode */ | |
| #define iod_delay iod_private3 /* current delay reason */ | |
| #define iod_wasWriting iod_private4 /* writing was in progress */ | |
| #define iod_reason iod_private5 /* reason for EOP */ | |
| #define iod_CWA iod_readR[3] /* current DSA address */ | |
| #define iod_LWA iod_private6 /* last word address */ | |
| #define iod_DSApending iod_private10 /* DSA request pending */ | |
| /* | |
| * Define delay functions other than the standard motion commands. The low | |
| * 7 bits are available, zero is reserved to mean no pending delay. | |
| */ | |
| #define IO_DELAY_RDATA 0x01 /* Delay IO_ST_DATA for read*/ | |
| #define IO_DELAY_WDATA 0x02 /* and write */ | |
| #define IO_DELAY_RTMO 0x03 /* Read record timeout */ | |
| #define IO_DELAY_WTMO 0x04 /* Write record timeout */ | |
| #define IO_DELAY_EOP 0x05 /* EOP delay */ | |
| #define IO_DSA_READ 0x06 /* DSA Read operation */ | |
| #define IO_DSA_WRITE 0x07 /* DSA Write operation */ | |
| #define IO_LOCAL_MASK 0x7F | |
| /* MT data structures | |
| mt_dev MT device descriptor | |
| mt_unit MT units | |
| mt_reg MT register list | |
| mt_mod MT modifier list | |
| */ | |
| #define MT_NUMDR 4 /* # drives */ | |
| UNIT mt_unit[] = { | |
| { UDATA(&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA(&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA(&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA(&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+UNIT_7TRACK, 0) }, | |
| }; | |
| REG mt_reg_1732_A[] = { | |
| { HRDATAD(FUNCTION, MTdev.FUNCTION, 16, "Last director status issued") }, | |
| { HRDATAD(STATUS, MTdev.STATUS, 16, "Director status register") }, | |
| { HRDATAD(UNITSEL, MTdev.iod_writeR[2], 16, "Last Unit Select issued") }, | |
| { HRDATAD(STATUS2, MTdev.STATUS2, 16, "Transport status register") }, | |
| { HRDATAD(IENABLE, MTdev.IENABLE, 16, "Interrupts enabled") }, | |
| { NULL } | |
| }; | |
| REG mt_reg_1732_3[] = { | |
| { HRDATAD(FUNCTION, MTdev.FUNCTION, 16, "Last director status issued") }, | |
| { HRDATAD(STATUS, MTdev.STATUS, 16, "Director status register") }, | |
| { HRDATAD(UNITSEL, MTdev.iod_writeR[2], 16, "Last Unit Select issued") }, | |
| { HRDATAD(STATUS2, MTdev.STATUS2, 16, "Transport status register") }, | |
| { HRDATAD(IENABLE, MTdev.IENABLE, 16, "Interrupts enabled") }, | |
| { HRDATAD(BUFFEREDIO, MTdev.BUFFEREDIO, 16, "Last Buffered I/O issued") }, | |
| { HRDATAD(CURADDRESS, MTdev.CURADDRESS, 16, "Current DSA address") }, | |
| { HRDATAD(LASTADDRESS, MTdev.iod_LWA, 16, "Last DSA address") }, | |
| { NULL } | |
| }; | |
| MTAB mt_mod[] = { | |
| { MTAB_XTD|MTAB_VDV, 0, "TYPE", "TYPE={1732-A|1732-3}", | |
| &mt_set_type, &mt_show_type, NULL, "Set/Display magtape controller type" }, | |
| { MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", "EQUIPMENT=hexAddress", | |
| &set_equipment, &show_addr, NULL, "Set/Display equipment address" }, | |
| { MTUF_WLK, 0, "write enabled", "WRITEENABLED", | |
| &mt_vlock, NULL, NULL, "Mark transport as write enabled" }, | |
| { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", | |
| &mt_vlock, NULL, NULL, "Mark transport as writed locked" }, | |
| { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| &sim_tape_set_fmt, &sim_tape_show_fmt, NULL, "Define tape format" }, | |
| { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", | |
| &sim_tape_set_capac, &sim_tape_show_capac, NULL, "Specify tape capacity" }, | |
| { MTAB_XTD|MTAB_VUN, 0, "TRANSPORT", NULL, | |
| NULL, &mt_show_transport, NULL, "Display type of tape transport" }, | |
| { MTAB_XTD|MTAB_VDV, 0, NULL, "STOPONREJECT", | |
| &set_stoponrej, NULL, NULL, "Stop simulation if I/O is rejected" }, | |
| { MTAB_XTD|MTAB_VDV, 0, NULL, "NOSTOPONREJECT", | |
| &clr_stoponrej, NULL, NULL, "Don't stop simulation if I/O is rejected" }, | |
| { MTAB_XTD|MTAB_VDV, 0, NULL, "PROTECT", | |
| &set_protected, NULL, NULL, "Device is protected (unimplemented)" }, | |
| { MTAB_XTD|MTAB_VDV, 0, NULL, "NOPROTECT", | |
| &clear_protected, NULL, NULL, "Device is unprotected (unimplemented)" }, | |
| { 0 } | |
| }; | |
| /* | |
| * MT debug flags | |
| */ | |
| #define DBG_V_OPS (DBG_SPECIFIC+0)/* Trace operations */ | |
| #define DBG_V_READ (DBG_SPECIFIC+1)/* Dump read records */ | |
| #define DBG_V_RDATA (DBG_SPECIFIC+2)/* Read through reg. 0 */ | |
| #define DBG_V_WDATA (DBG_SPECIFIC+3)/* Write through reg. 0 */ | |
| #define DBG_V_MTIO (DBG_SPECIFIC+4)/* Trace library routine calls */ | |
| #define DBG_V_DENS (DBG_SPECIFIC+5)/* Trace density select changes */ | |
| #define DBG_V_SELECT (DBG_SPECIFIC+6)/* Trace drive select/de-select */ | |
| #define DBG_OPS (1 << DBG_V_OPS) | |
| #define DBG_READ (1 << DBG_V_READ) | |
| #define DBG_RDATA (1 << DBG_V_RDATA) | |
| #define DBG_WDATA (1 << DBG_V_WDATA) | |
| #define DBG_MTIO (1 << DBG_V_MTIO) | |
| #define DBG_DENS (1 << DBG_V_DENS) | |
| #define DBG_SELECT (1 << DBG_V_SELECT) | |
| DEBTAB mt_deb[] = { | |
| { "TRACE", DBG_DTRACE, "Trace device I/O requests" }, | |
| { "STATE", DBG_DSTATE, "Display device state changes" }, | |
| { "INTR", DBG_DINTR, "Display device interrupt requests" }, | |
| { "LOCATION", DBG_DLOC, "Display address of I/O instructions" }, | |
| { "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejects" }, | |
| { "OPS", DBG_OPS, "Trace tape transport operations" }, | |
| { "READ", DBG_READ, "Dump read records" }, | |
| { "RDATA", DBG_RDATA, "Dump programmed I/O read data" }, | |
| { "WDATA", DBG_WDATA, "Dump programmed I/O write data" }, | |
| { "MTIO", DBG_MTIO, "Trace tape library routine calls" }, | |
| { "DENS", DBG_DENS, "Trace denisty select changes" }, | |
| { "SELECT", DBG_SELECT, "Trace transport select/de-select" }, | |
| { NULL } | |
| }; | |
| DEVICE mt_dev = { | |
| "MT", mt_unit, NULL, mt_mod, | |
| MT_NUMDR, 10, 31, 1, 8, 8, | |
| NULL, NULL, &mt_reset, | |
| &mt_boot, &mt_attach, &mt_detach, | |
| &MTdev, | |
| DEV_DEBUG | DEV_TAPE | DEV_DISABLE | DEV_INDEV | DEV_OUTDEV | DEV_PROTECT, | |
| 0, mt_deb, | |
| NULL, NULL, &mt_help, NULL, NULL, NULL | |
| }; | |
| /* MT trace routine */ | |
| void mt_trace(UNIT *uptr, const char *what, t_stat st, t_bool xfer) | |
| { | |
| int32 u = uptr - mt_dev.units; | |
| const char *status = NULL; | |
| switch (st) { | |
| case MTSE_OK: | |
| status = "OK"; | |
| break; | |
| case MTSE_TMK: | |
| status = "Tape Mark"; | |
| break; | |
| case MTSE_UNATT: | |
| status = "Unattached"; | |
| break; | |
| case MTSE_IOERR: | |
| status = "IO Error"; | |
| break; | |
| case MTSE_INVRL: | |
| status = "Invalid Record Length"; | |
| break; | |
| case MTSE_FMT: | |
| status = "Invalid Format"; | |
| break; | |
| case MTSE_BOT: | |
| status = "Beginning Of Tape"; | |
| break; | |
| case MTSE_EOM: | |
| status = "End Of Medium"; | |
| break; | |
| case MTSE_RECE: | |
| status = "Error In Record"; | |
| break; | |
| case MTSE_WRP: | |
| status = "Write Protected"; | |
| break; | |
| case MTSE_LEOT: | |
| status = "Logical end of tape"; | |
| break; | |
| case MTSE_RUNAWAY: | |
| status = "Tape runaway"; | |
| break; | |
| } | |
| if (status != NULL) { | |
| if (xfer) | |
| fprintf(DBGOUT, "MT%d: %s, bytes %d - %s\r\n", u, what, MTremain, status)\ | |
| ; | |
| else fprintf(DBGOUT, "MT%d: %s - %s\r\n", u, what, status); | |
| } else fprintf(DBGOUT, "MT%d: %s\r\n", u, what); | |
| if ((mt_dev.dctrl & DBG_DLOC) != 0) | |
| fprintf(DBGOUT, "MT%d: Inst: %llu\r\n", u, Instructions); | |
| } | |
| /* MT trace routine (DSA mode) */ | |
| void mt_DSAtrace(UNIT *uptr, const char *what) | |
| { | |
| int32 u = uptr - mt_dev.units; | |
| fprintf(DBGOUT, "MT%d: DSA %s - CWA: 0x%04X, LWA: 0x%04X\r\n", | |
| u, what, MTdev.iod_CWA, MTdev.iod_LWA); | |
| } | |
| /* Tape library routine trace */ | |
| void mtio_trace(UNIT *uptr, const char *what, t_stat st, t_bool lvalid, t_mtrlnt len) | |
| { | |
| int32 u = uptr - mt_dev.units; | |
| t_bool bot = FALSE, eot = FALSE; | |
| const char *status = "Unknown"; | |
| if (st != MTSE_UNATT) { | |
| bot = sim_tape_bot(uptr); | |
| eot = sim_tape_eot(uptr); | |
| } | |
| switch (st) { | |
| case MTSE_OK: | |
| status = "OK"; | |
| break; | |
| case MTSE_TMK: | |
| status = "Tape mark"; | |
| break; | |
| case MTSE_UNATT: | |
| status = "Unattached"; | |
| break; | |
| case MTSE_IOERR: | |
| status = "IO error"; | |
| break; | |
| case MTSE_INVRL: | |
| status = "Invalid record length"; | |
| break; | |
| case MTSE_FMT: | |
| status = "Invalid format"; | |
| break; | |
| case MTSE_BOT: | |
| status = "Beginning of tape"; | |
| break; | |
| case MTSE_EOM: | |
| status = "End of medium"; | |
| break; | |
| case MTSE_RECE: | |
| status = "Error in record"; | |
| break; | |
| case MTSE_WRP: | |
| status = "Write protected"; | |
| break; | |
| case MTSE_LEOT: | |
| status = "Logical end of tape"; | |
| break; | |
| case MTSE_RUNAWAY: | |
| status = "Tape runaway"; | |
| break; | |
| } | |
| fprintf(DBGOUT, "MT%d: MTIO [%s %s] %s - %s\r\n", | |
| u, bot ? "BOT" : "", eot ? "EOT" : "", what, status); | |
| if (lvalid) | |
| fprintf(DBGOUT, | |
| "MT%d: MTIO Record len: %u, Mode: 0x%04X\r\n", | |
| u, len, MTdev.iod_mode); | |
| } | |
| /* Dump MT buffer */ | |
| char chars[128] = { | |
| ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', | |
| ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', | |
| ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', | |
| ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', | |
| ' ', '!', '"', '#', '$', '%', '&', '\'', | |
| '(', ')', '*', '+', ',', '-', '.', '/', | |
| '0', '1', '2', '3', '4', '5', '6', '7', | |
| '8', '9', ':', ';', '<', '=', '>', '?', | |
| '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', | |
| 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | |
| 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | |
| 'X', 'Y', 'Z', '[', '\\', '|', '^', '_', | |
| ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | |
| 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | |
| 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | |
| 'x', 'y', 'z', '{', ' ', '}', '~', ' ' | |
| }; | |
| void mt_dump(void) | |
| { | |
| t_mtrlnt offset = 0, count = MTremain; | |
| char msg[80], text[16]; | |
| if (MTremain > 0) { | |
| printf("Dump of MTbuf:\r\n"); | |
| while (count > 0) { | |
| t_mtrlnt remain = count >= 10 ? 10 : count; | |
| t_mtrlnt i; | |
| msg[0] = '\0'; | |
| for (i = 0; i < remain; i++) { | |
| char temp[8]; | |
| text[i] = chars[MTbuf[offset] & 0x7F]; | |
| sprintf(temp, "0x%02x", MTbuf[offset++]); | |
| if (msg[0] != '\0') | |
| strcat(msg, " "); | |
| strcat(msg, temp); | |
| } | |
| text[remain] = '\0'; | |
| printf("%-55s%s\r\n", msg, text); | |
| count -= remain; | |
| } | |
| } | |
| } | |
| /* | |
| * Dump the current internal state of the MT device. | |
| */ | |
| const char *MTstateStr[] = { | |
| "Idle", "Reading", "Writing", "Read Timeout", "Write Timeout", "DSA Done" | |
| }; | |
| void MTstate(const char *where, DEVICE *dev, IO_DEVICE *iod) | |
| { | |
| char device[16]; | |
| strcpy(device, "None"); | |
| if (iod->iod_unit != NULL) { | |
| int32 u = iod->iod_unit - dev->units; | |
| sprintf(device, "MT%u", u); | |
| } | |
| fprintf(DBGOUT, | |
| "%s[%s %s: Func: %04X, Sta: %04X, Sta2: %04X, Ena: %04X]\r\n", | |
| INTprefix, dev->name, where, | |
| iod->FUNCTION, iod->STATUS, iod->STATUS2, iod->IENABLE); | |
| fprintf(DBGOUT, | |
| "%s[%s %s: Sel: %s, %s%s]\r\n", | |
| INTprefix, dev->name, where, device, MTstateStr[MTmode], | |
| iod->iod_wasWriting ? ", Was writing" : ""); | |
| } | |
| void mt_data(UNIT *uptr, t_bool output, uint16 data) | |
| { | |
| int32 u = uptr - mt_dev.units; | |
| fprintf(DBGOUT, "MT%d: %s - 0x%04x\r\n", u, output ? "wrote" : "read", data); | |
| } | |
| t_stat mt_show_type(FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
| { | |
| switch (MTdev.iod_type) { | |
| case DEVTYPE_1732_A: | |
| fprintf(st, "1732-A Magnetic Tape Controller"); | |
| break; | |
| case DEVTYPE_1732_3: | |
| fprintf(st, "1733-3 Magnetic Tape Controller"); | |
| break; | |
| default: | |
| return SCPE_IERR; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat mt_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| if (!cptr) | |
| return SCPE_IERR; | |
| if ((uptr->flags & UNIT_ATT) != 0) | |
| return SCPE_ALATT; | |
| if (!strcmp(cptr, "1732-A")) { | |
| MTdev.iod_type = DEVTYPE_1732_A; | |
| MTdev.iod_model = "1732-A"; | |
| MTdev.iod_flags &= ~AQ_ONLY; | |
| mt_dev.registers = mt_reg_1732_A; | |
| buildDCtables(); | |
| } else { | |
| if (!strcmp(cptr, "1732-3")) { | |
| MTdev.iod_type = DEVTYPE_1732_3; | |
| MTdev.iod_model = "1732-3"; | |
| MTdev.iod_flags |= AQ_ONLY; | |
| mt_dev.registers = mt_reg_1732_3; | |
| buildDCtables(); | |
| } else return SCPE_ARG; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* | |
| * Display magtape transport | |
| */ | |
| t_stat mt_show_transport(FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
| { | |
| if (uptr == NULL) | |
| return SCPE_IERR; | |
| if (MTdev.iod_type == DEVTYPE_1732_A) { | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| fprintf(st, "7-track 608 transport"); | |
| else fprintf(st, "9-track 609 transport"); | |
| } else { | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| fprintf(st, "7-track 6173 transport"); | |
| else fprintf(st, "9-track 6193 transport"); | |
| } | |
| return SCPE_OK; | |
| } | |
| /* | |
| * Compute the delay time between new data being available from tape. This | |
| * will be dependent on the density of the tape and the speed of the drive | |
| * (in this case we assume 37.5 inches per sec). | |
| */ | |
| int32 mt_densityTimeout(t_bool loose) | |
| { | |
| int32 result; | |
| switch (MTdev.STATUS2 & (IO_ST2_556 | IO_ST2_800)) { | |
| case 0: | |
| result = MT_200_WAIT; | |
| break; | |
| case IO_ST2_556: | |
| result = MT_556_WAIT; | |
| break; | |
| case IO_ST2_800: | |
| result = MT_800_WAIT; | |
| break; | |
| } | |
| if (MTdev.iod_type == DEVTYPE_1732_3) | |
| if ((MTdev.STATUS2 & IO_ST2_1600) != 0) | |
| result = MT_1600_WAIT; | |
| if ((MTdev.iod_mode & IO_1732_ASSEM) != 0) | |
| result *= 2; | |
| return loose ? LOOSETIMING(result) : result; | |
| } | |
| /* Unit service */ | |
| t_stat mt_svc(UNIT *uptr) | |
| { | |
| uint16 mask = IO_1732_STMSK; | |
| uint16 delay = MTdev.iod_delay; | |
| uint16 result; | |
| t_stat status; | |
| t_mtrlnt temp; | |
| int32 tmo; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "mt_svc", (t_stat)-1, FALSE); | |
| MTdev.iod_delay = 0; | |
| /* | |
| * Check for local (internal) delays. | |
| */ | |
| if ((delay & IO_LOCAL_MASK) != 0) { | |
| switch (delay) { | |
| case IO_DELAY_RDATA: | |
| fw_IOintr(FALSE, &mt_dev, &MTdev, IO_ST_DATA, 0, 0xFFFF, "Read Ready"); | |
| tmo = mt_densityTimeout(TRUE); | |
| MTdev.iod_event = Instructions + tmo; | |
| MTdev.iod_delay = IO_DELAY_RTMO; | |
| sim_activate(uptr, tmo); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "Delayed IO_ST_DATA for read", (t_stat)-1, FALSE); | |
| break; | |
| case IO_DELAY_WDATA: | |
| fw_IOintr(FALSE, &mt_dev, &MTdev, IO_ST_DATA, 0, 0xFFFF, "Write Ready"); | |
| tmo = mt_densityTimeout(TRUE); | |
| MTdev.iod_event = Instructions + tmo; | |
| MTdev.iod_delay = IO_DELAY_WTMO; | |
| sim_activate(uptr, tmo); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "Delayed IO_ST_DATA for write", (t_stat)-1, FALSE); | |
| break; | |
| case IO_DELAY_RTMO: | |
| MTmode = MT_READTMO; | |
| /* | |
| * Drop DATA and schedule EOP completion | |
| */ | |
| MTdev.STATUS &= ~IO_ST_DATA; | |
| MTdev.iod_delay = IO_DELAY_EOP; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "Read buffer timed out", (t_stat)-1, FALSE); | |
| if (MTremain != 0) { | |
| MTdev.STATUS |= IO_ST_ALARM | IO_ST_LOST; | |
| MTdev.iod_reason = "Read timed out - data lost"; | |
| MTdev.iod_oldienable = MTdev.iod_ienable; | |
| MTdev.iod_ienable &= ~IO_DIR_DATA; | |
| } else MTdev.iod_reason = "Read timed out"; | |
| MTremain = 0; | |
| sim_activate(uptr, MT_EOP_WAIT); | |
| break; | |
| case IO_DELAY_WTMO: | |
| MTmode = MT_WRITETMO; | |
| status = sim_tape_wrrecf(uptr, MTbuf, MToffset); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "wrrecf", status, TRUE, MToffset); | |
| /* | |
| * Drop DATA and schedule EOP completion | |
| */ | |
| MTdev.STATUS &= ~IO_ST_DATA; | |
| MTdev.iod_delay = IO_DELAY_EOP; | |
| MTdev.iod_reason = "Write timed out"; | |
| sim_activate(uptr, MT_EOP_WAIT); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) { | |
| MTremain = MToffset; | |
| mt_trace(uptr, "Write buffer timed out", status, TRUE); | |
| } | |
| break; | |
| case IO_DELAY_EOP: | |
| MTmode = MT_IDLE; | |
| fw_IOcompleteEOP(FALSE, &mt_dev, &MTdev, ~IO_1732_ACTIVE, MTdev.iod_reason); | |
| MTdev.iod_reason = NULL; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "Delayed EOP", (t_stat)-1, FALSE); | |
| break; | |
| case IO_DSA_READ: | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_DSAtrace(uptr, "read"); | |
| if (MTdev.iod_CWA == MTdev.iod_LWA) { | |
| /* | |
| * DSA read transfer complete. | |
| */ | |
| MTmode = MT_DSADONE; | |
| MTdev.iod_delay = IO_DELAY_EOP; | |
| MTdev.iod_reason = "DSA read complete"; | |
| sim_activate(uptr, MT_EOP_WAIT); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "DSA read complete", (t_stat)-1, FALSE); | |
| break; | |
| } | |
| /* | |
| * If there is no data available, terminate the transfer | |
| */ | |
| if (MTremain == 0) { | |
| /* | |
| * DSA read complete - no more data. | |
| */ | |
| MTmode = MT_DSADONE; | |
| MTdev.iod_delay = IO_DELAY_EOP; | |
| MTdev.iod_reason = "DSA read complete - no data"; | |
| sim_activate(uptr, MT_EOP_WAIT); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "DSA read complete - no data", (t_stat)-1, FALSE); | |
| break; | |
| } | |
| if ((MTdev.iod_mode & IO_1732_ASSEM) != 0) { | |
| if (MTremain >= 2) { | |
| result = (MTbuf[MToffset] << 8) | MTbuf[MToffset + 1]; | |
| MToffset += 2; | |
| MTremain -= 2; | |
| } else { | |
| MTdev.STATUS |= IO_1732_FILL; | |
| result = MTbuf[MToffset] << 8; | |
| MToffset++; | |
| MTremain--; | |
| } | |
| } else { | |
| result = MTbuf[MToffset]; | |
| MToffset++; | |
| MTremain--; | |
| } | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| result &= 0x3F3F; | |
| if (!IOStoreToMem(MTdev.iod_CWA, result, TRUE)) { | |
| /*** TODO: generate device protect error ***/ | |
| } | |
| MTdev.iod_CWA++; | |
| MTdev.iod_delay = IO_DSA_READ; | |
| sim_activate(uptr, mt_densityTimeout(FALSE)); | |
| break; | |
| case IO_DSA_WRITE: | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_DSAtrace(uptr, "write"); | |
| if (MTdev.iod_CWA == MTdev.iod_LWA) { | |
| /* | |
| * DSA write transfer complete. | |
| */ | |
| status = sim_tape_wrrecf(uptr, MTbuf, MToffset); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "wrrecf", status, TRUE, MToffset); | |
| MTmode = MT_DSADONE; | |
| MTdev.iod_delay = IO_DELAY_EOP; | |
| MTdev.iod_reason = "DSA write complete"; | |
| sim_activate(uptr, MT_EOP_WAIT); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "DSA write complete", (t_stat)-1, FALSE); | |
| break; | |
| } | |
| result = LoadFromMem(MTdev.iod_CWA); | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| result &= 0x3F3F; | |
| MTdev.iod_CWA++; | |
| if ((MTdev.iod_mode & IO_1732_ASSEM) != 0) { | |
| MTbuf[MToffset] = (result >> 8) & 0xFF; | |
| MTbuf[MToffset + 1] = result & 0xFF; | |
| MToffset += 2; | |
| } else { | |
| MTbuf[MToffset] = result & 0xFF; | |
| MToffset += 1; | |
| } | |
| MTdev.iod_delay = IO_DSA_WRITE; | |
| sim_activate(uptr, mt_densityTimeout(FALSE)); | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* | |
| * Check if we need to write a tape mark before processing the request. | |
| */ | |
| if (MTdev.iod_wasWriting) | |
| switch (delay) { | |
| case IO_1732_BACKSP: | |
| case IO_1732_REWL: | |
| case IO_1732A_REWU: | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "Forced TM (BACKSP, REWL, REWU)", (t_stat)-1, FALSE); | |
| status = sim_tape_wrtmk(uptr); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "wrtmk", status, FALSE, 0); | |
| break; | |
| } | |
| /* | |
| * Command specific processing | |
| */ | |
| switch (delay) { | |
| /* | |
| * The following commands normally do not set "end of operation". "read | |
| * motion" does set "end of operation" if a tape mark or end of tape | |
| * is detected. | |
| */ | |
| case IO_1732_READ: | |
| MTremain = 0; | |
| status = sim_tape_rdrecf(uptr, MTbuf, &MTremain, MTSIZ); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "rdrecf", status, TRUE, MTremain); | |
| switch (status) { | |
| case MTSE_OK: | |
| break; | |
| case MTSE_TMK: | |
| MTdev.STATUS |= IO_ST_ALARM | IO_1732_FMARK; | |
| break; | |
| case MTSE_EOM: | |
| MTdev.STATUS |= IO_ST_ALARM | IO_1732_EOT; | |
| break; | |
| case MTSE_RECE: | |
| MTdev.STATUS |= IO_ST_ALARM | IO_ST_PARITY; | |
| MTremain = 0; | |
| break; | |
| } | |
| MToffset = 0; | |
| if ((MTdev.STATUS & (IO_1732_FMARK | IO_1732_EOT | IO_ST_PARITY)) == 0) | |
| mask &= ~IO_ST_EOP; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "READ", status, TRUE); | |
| if ((mt_dev.dctrl & DBG_READ) != 0) | |
| mt_dump(); | |
| if (MTremain > 0) { | |
| if (MTdev.iod_DSApending) { | |
| MTdev.iod_DSApending = FALSE; | |
| MTdev.iod_delay = IO_DSA_READ; | |
| sim_activate(uptr, mt_densityTimeout(FALSE)); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) { | |
| int32 u = uptr - mt_dev.units; | |
| fprintf(DBGOUT, | |
| "[MT%d: DSA Read started, CWA: 0x%04X, LWA: 0x%04X, Mode: 0x%X\r\n", | |
| u, MTdev.iod_CWA, MTdev.iod_LWA, MTdev.iod_mode); | |
| } | |
| return SCPE_OK; | |
| } | |
| MTdev.iod_delay = IO_DELAY_RDATA; | |
| sim_activate(uptr, MT_MIN_WAIT); | |
| return SCPE_OK; | |
| } | |
| MTmode = MT_IDLE; | |
| break; | |
| case IO_1732_WRITE: | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "WRITE", (t_stat)-1, FALSE); | |
| if (MTdev.iod_DSApending) { | |
| MTdev.iod_DSApending = FALSE; | |
| MTdev.iod_delay = IO_DSA_WRITE; | |
| sim_activate(uptr, mt_densityTimeout(FALSE)); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) { | |
| int32 u = uptr - mt_dev.units; | |
| fprintf(DBGOUT, | |
| "[MT%d: DSA Write started, CWA: 0x%04X, LWA: 0x%04X, Mode: 0x%X\r\n", | |
| u, MTdev.iod_CWA, MTdev.iod_LWA, MTdev.iod_mode); | |
| } | |
| return SCPE_OK; | |
| } | |
| MTdev.iod_delay = IO_DELAY_WDATA; | |
| sim_activate(uptr, MT_MIN_WAIT); | |
| return SCPE_OK; | |
| case IO_1732A_REWU: | |
| status = sim_tape_rewind(uptr); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "rewind & unload", status, FALSE, 0); | |
| MTdev.STATUS |= IO_1732_BOT; | |
| mt_detach(uptr); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "REWU", status, FALSE); | |
| mask &= ~IO_ST_EOP; | |
| break; | |
| /* | |
| * The following commands set "end of operation" when the command | |
| * completes. | |
| */ | |
| case IO_1732_BACKSP: | |
| status = sim_tape_sprecr(uptr, &temp); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "sprecr", status, FALSE, 0); | |
| if (status == MTSE_TMK) | |
| MTdev.STATUS |= IO_1732_FMARK; | |
| if (sim_tape_bot(uptr)) | |
| MTdev.STATUS |= IO_1732_BOT; | |
| if (sim_tape_eot(uptr)) | |
| MTdev.STATUS |= IO_1732_EOT; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "BACKSP", status, FALSE); | |
| break; | |
| case IO_1732_WFM: | |
| status = sim_tape_wrtmk(uptr); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "wrtmk", status, FALSE, 0); | |
| #if 0 | |
| MTdev.STATUS |= IO_ST_ALARM | IO_1732_FMARK; | |
| #endif | |
| if (sim_tape_eot(uptr)) | |
| MTdev.STATUS |= IO_1732_EOT; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "WFM", status, FALSE); | |
| break; | |
| case IO_1732_SFWD: | |
| while (!sim_tape_eot(uptr)) { | |
| status = sim_tape_sprecf(uptr, &temp); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "sprecf", status, FALSE, 0); | |
| if (status == MTSE_TMK) { | |
| MTdev.STATUS |= IO_1732_FMARK; | |
| break; | |
| } | |
| } | |
| if (sim_tape_bot(uptr)) | |
| MTdev.STATUS |= IO_1732_BOT; | |
| if (sim_tape_eot(uptr)) | |
| MTdev.STATUS |= IO_1732_EOT; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "SFWD", status, FALSE); | |
| break; | |
| case IO_1732_SBACK: | |
| while (!sim_tape_bot(uptr)) { | |
| status = sim_tape_sprecr(uptr, &temp); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "sprecr", status, FALSE, 0); | |
| if (status == MTSE_TMK) { | |
| MTdev.STATUS |= IO_1732_FMARK; | |
| break; | |
| } | |
| } | |
| if (sim_tape_bot(uptr)) | |
| MTdev.STATUS |= IO_1732_BOT; | |
| if (sim_tape_eot(uptr)) | |
| MTdev.STATUS |= IO_1732_EOT; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "SBACK", status, FALSE); | |
| break; | |
| case IO_1732_REWL: | |
| status = sim_tape_rewind(uptr); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "rewind", status, FALSE, 0); | |
| MTdev.STATUS |= IO_1732_BOT; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "REWL", status, FALSE); | |
| break; | |
| } | |
| /* | |
| * If we are at a tape mark or end of tape, no data is available. | |
| */ | |
| if ((MTdev.STATUS & (IO_1732_FMARK | IO_1732_EOT)) != 0) | |
| mask &= ~IO_ST_DATA; | |
| /* | |
| * Controller is no longer active. | |
| */ | |
| mask &= ~IO_1732_ACTIVE; | |
| /* | |
| * I/O is now complete. | |
| */ | |
| fw_IOcompleteEOP(FALSE, &mt_dev, &MTdev, mask, "Operation Complete"); | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat mt_reset(DEVICE *dptr) | |
| { | |
| t_stat r; | |
| if (MTdev.iod_type == IOtype_default) { | |
| /* | |
| * Setup the default device type. | |
| */ | |
| MTdev.iod_type = DEVTYPE_1732_A; | |
| MTdev.iod_model = "1732-A"; | |
| MTdev.iod_flags &= ~AQ_ONLY; | |
| mt_dev.registers = mt_reg_1732_A; | |
| buildDCtables(); | |
| } | |
| if (IOFWinitialized) | |
| if ((dptr->flags & DEV_DIS) == 0) | |
| if ((r = checkReset(dptr, MTdev.iod_equip)) != SCPE_OK) | |
| return r; | |
| DEVRESET(&MTdev); | |
| MTdev.STATUS = 0; | |
| MTdev.STATUS2 = 0; | |
| MTdev.iod_mode = 0; | |
| MTdev.iod_unit = NULL; | |
| MTdev.iod_delay = 0; | |
| MTdev.iod_wasWriting = FALSE; | |
| MTdev.iod_CWA = MTdev.iod_LWA = 0; | |
| MTdev.iod_DSApending = FALSE; | |
| MTmode = MT_IDLE; | |
| return SCPE_OK; | |
| } | |
| /* Boot routine */ | |
| t_stat mt_boot(int32 unitno, DEVICE *dptr) | |
| { | |
| if (unitno != 0) { | |
| sim_printf("Can only boot from drive 0\n"); | |
| return SCPE_ARG; | |
| } | |
| if ((sim_switches & SWMASK('S')) != 0) { | |
| /* | |
| * Special bootstrap for System Maintenance Monitor (SMM17) | |
| */ | |
| uint16 base, equip; | |
| base = ((cpu_unit.capac - 1) & 0xF000) | 0xFE0; | |
| loadBootstrap(smm17boot9, SMM17BOOTLEN9, base, base); | |
| /* | |
| * Compute the equipment address to use and patch it into memory. | |
| */ | |
| equip = (MTdev.iod_equip << 7) | 2; | |
| if ((sim_switches & SWMASK('D')) != 0) | |
| equip |= 0x1000; | |
| M[base + 2] = equip; | |
| return SCPE_OK; | |
| } | |
| loadBootstrap(mtbootstrap9, MTBOOTLEN9, 0, 0); | |
| /* | |
| * Set A register according to the amount of memory installed. | |
| */ | |
| Areg = 0x5000; | |
| if (cpu_unit.capac < 32768) | |
| Areg = 0x4000; | |
| if (cpu_unit.capac < 24576) | |
| Areg = 0x2000; | |
| return SCPE_OK; | |
| } | |
| /* Attach routine */ | |
| t_stat mt_attach(UNIT *uptr, CONST char *cptr) | |
| { | |
| t_stat r; | |
| r = sim_tape_attach(uptr, cptr); | |
| if (r != SCPE_OK) | |
| return r; | |
| uptr->flags &= ~UNIT_WPROT; | |
| if (sim_switches & SWMASK('R')) | |
| uptr->flags |= UNIT_WPROT; | |
| uptr->DENS = IO_ST2_800; | |
| /* | |
| * If this units is currently selected, make it accessible. | |
| */ | |
| if (MTdev.iod_unit == uptr) { | |
| MTdev.STATUS2 = uptr->DENS & (IO_ST2_556 | IO_ST2_800); | |
| if ((uptr->flags & UNIT_WPROT) != 0) | |
| MTdev.STATUS2 &= ~IO_ST2_WENABLE; | |
| else MTdev.STATUS2 |= IO_ST2_WENABLE; | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| MTdev.STATUS2 |= IO_ST2_7TRACK; | |
| else MTdev.STATUS2 &= ~IO_ST2_7TRACK; | |
| fw_setForced(&MTdev, IO_ST_READY); | |
| } | |
| return r; | |
| } | |
| /* Detach routine */ | |
| t_stat mt_detach(UNIT *uptr) | |
| { | |
| t_stat st; | |
| if ((uptr->flags & UNIT_ATT) == 0) | |
| return SCPE_OK; | |
| sim_cancel(uptr); | |
| st = sim_tape_detach(uptr); | |
| if (st == MTSE_OK) { | |
| if (MTdev.iod_unit == uptr) | |
| fw_clearForced(&MTdev, IO_ST_READY); | |
| } | |
| return st; | |
| } | |
| /* Write lock/enable routine */ | |
| t_stat mt_vlock(UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
| { | |
| if (((uptr->flags & UNIT_ATT) != 0) && (val || sim_tape_wrp(uptr))) | |
| uptr->flags |= UNIT_WPROT; | |
| else uptr->flags &= ~UNIT_WPROT; | |
| return SCPE_OK; | |
| } | |
| /* | |
| * Perform a "Clear Controller" operation. Basically this is similar to a | |
| * device reset except it does not forget the currently selected transport. | |
| */ | |
| void MTclear(DEVICE *dptr) | |
| { | |
| UNIT *uptr; | |
| DEVRESET(&MTdev); | |
| MTdev.STATUS = 0; | |
| MTdev.STATUS2 = 0; | |
| MTdev.iod_mode = 0; | |
| MTdev.iod_delay = 0; | |
| MTdev.iod_wasWriting = FALSE; | |
| MTmode = MT_IDLE; | |
| if ((uptr = MTdev.iod_unit) != NULL) { | |
| fw_setForced(&MTdev, IO_ST_READY); | |
| MTdev.STATUS2 = uptr->DENS & (IO_ST2_556 | IO_ST2_800); | |
| if ((uptr->flags & UNIT_WPROT) != 0) | |
| MTdev.STATUS2 &= ~IO_ST2_WENABLE; | |
| else MTdev.STATUS2 |= IO_ST2_WENABLE; | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| MTdev.STATUS2 |= IO_ST2_7TRACK; | |
| else MTdev.STATUS2 &= ~IO_ST2_7TRACK; | |
| } | |
| } | |
| /* | |
| * Check if I/O should be rejected. I/O allowed if: | |
| * | |
| * Reg. Write (OUT) Read (INP) | |
| * | |
| * 00 Ready and writing active Ready and data available | |
| * 01 Controller dependent Always allowed | |
| * Always allow Clear Interrupts/Controller | |
| * 1732-A: Reject if controller active | |
| * 1732-3: Always allow | |
| * 10 Controller active clear Always allowed | |
| * 11 Controller busy and EOP clear Always allowed | |
| * | |
| * If a data I/O (register 0) is performed after the tape motion has timed | |
| * out, we need to generate an ALARM + LOST data status. | |
| */ | |
| t_bool MTreject(IO_DEVICE *iod, t_bool output, uint8 reg) | |
| { | |
| switch (reg) { | |
| case 0: | |
| if (output) | |
| return ((iod->STATUS & (IO_ST_DATA | IO_ST_READY)) != | |
| (IO_ST_DATA | IO_ST_READY)) || (MTmode != MT_WRITING); | |
| return ((iod->STATUS & (IO_ST_DATA | IO_ST_READY)) != | |
| (IO_ST_DATA | IO_ST_READY)) || (MTremain == 0); | |
| case 1: | |
| if (output) { | |
| if (MTdev.iod_type == DEVTYPE_1732_A) | |
| if ((IOAreg & (IO_DIR_CCONT | IO_DIR_CINT)) == 0) | |
| return ((iod->STATUS & IO_1732_ACTIVE) != 0); | |
| } | |
| break; | |
| case 2: | |
| if (output) | |
| return ((iod->STATUS & IO_1732_ACTIVE) != 0); | |
| break; | |
| case 3: | |
| if (MTdev.iod_type != DEVTYPE_1732_3) | |
| return TRUE; | |
| if (output) | |
| return ((iod->STATUS & (IO_ST_EOP | IO_ST_BUSY)) == IO_ST_BUSY); | |
| break; | |
| } | |
| return FALSE; | |
| } | |
| /* Perform an input operation on a selected drive. This can be performed | |
| by issuing a command directly to the device or via a 1706 */ | |
| enum IOstatus doMTIn(UNIT *uptr, uint16 *data, t_bool via1706) | |
| { | |
| uint16 result; | |
| /* | |
| * Reject the request if we are not reading or data is not available | |
| */ | |
| if ((MTmode != MT_READING) || ((MTdev.STATUS & IO_ST_DATA) == 0)) | |
| return IO_REJECT; | |
| sim_cancel(uptr); | |
| if ((MTdev.iod_mode & IO_1732_ASSEM) != 0) { | |
| if (MTremain >= 2) { | |
| result = (MTbuf[MToffset] << 8) | MTbuf[MToffset + 1]; | |
| MToffset += 2; | |
| MTremain -= 2; | |
| } else { | |
| MTdev.STATUS |= IO_1732_FILL; | |
| result = MTbuf[MToffset] << 8; | |
| MToffset++; | |
| MTremain--; | |
| } | |
| } else { | |
| result = MTbuf[MToffset]; | |
| MToffset++; | |
| MTremain--; | |
| } | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| result &= 0x3F3F; | |
| if ((mt_dev.dctrl & DBG_RDATA) != 0) | |
| mt_data(uptr, FALSE, result); | |
| fw_IOintr(FALSE, &mt_dev, &MTdev, 0, IO_ST_DATA, 0xFFFF, NULL); | |
| if (MTremain != 0) { | |
| MTdev.iod_delay = IO_DELAY_RDATA; | |
| sim_activate(uptr, (int32)(MTdev.iod_event - Instructions)); | |
| } else { | |
| MTmode = MT_IDLE; | |
| MTdev.STATUS |= IO_ST_EOP; | |
| MTdev.STATUS &= ~(IO_1732_ACTIVE | IO_ST_BUSY); | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "Consumed read buffer", (t_stat)-1, FALSE); | |
| } | |
| *data = result; | |
| return IO_REPLY; | |
| } | |
| /* Perform an output operation on a selected drive. This can be performed | |
| by issuing a command directly to the device or via a 1706 */ | |
| enum IOstatus doMTOut(UNIT *uptr, uint16 *data, t_bool via1706) | |
| { | |
| uint16 temp = *data; | |
| t_mtrlnt need = ((MTdev.iod_mode & IO_1732_ASSEM) != 0) ? 2 : 1; | |
| /* | |
| * Reject the request if we are not writing or data cannot be written. | |
| */ | |
| if ((MTmode != MT_WRITING) || (MTdev.STATUS & IO_ST_DATA) == 0) | |
| return IO_REJECT; | |
| sim_cancel(uptr); | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| temp &= 0x3F3F; | |
| if (MTremain < need) | |
| return IO_REJECT; | |
| if ((MTdev.iod_mode & IO_1732_ASSEM) != 0) { | |
| MTbuf[MToffset] = (temp >> 8) & 0xFF; | |
| MTbuf[MToffset + 1] = temp & 0xFF; | |
| MToffset += 2; | |
| MTremain -= 2; | |
| } else { | |
| MTbuf[MToffset] = temp & 0xFF; | |
| MToffset += 1; | |
| MTremain -= 1; | |
| } | |
| if ((mt_dev.dctrl & DBG_WDATA) != 0) | |
| mt_data(uptr, TRUE, temp); | |
| fw_IOintr(FALSE, &mt_dev, &MTdev, 0, IO_ST_DATA, 0xFFFF, NULL); | |
| MTdev.iod_delay = IO_DELAY_WDATA; | |
| sim_activate(uptr, (int32)(MTdev.iod_event - Instructions)); | |
| return IO_REPLY; | |
| } | |
| /* Perform control function */ | |
| enum IOstatus doMTFunction(DEVICE *dev) | |
| { | |
| UNIT *uptr; | |
| t_stat st; | |
| t_mtrlnt count = 1; | |
| /* | |
| * Handle commands in the following order: | |
| * | |
| * 1. Handle clears | |
| * 2. Handle interrupt selections | |
| * 3. Handle motion control | |
| */ | |
| switch (IOAreg & IO_1732_MOTION) { | |
| case 0: | |
| case IO_1732_WRITE: | |
| case IO_1732_READ: | |
| case IO_1732_BACKSP: | |
| case IO_1732_WFM: | |
| case IO_1732_SFWD: | |
| case IO_1732_SBACK: | |
| case IO_1732_REWL: | |
| break; | |
| case IO_1732A_REWU: | |
| if (MTdev.iod_type == DEVTYPE_1732_3) | |
| return IO_REJECT; | |
| break; | |
| default: | |
| return IO_REJECT; | |
| } | |
| if (doDirectorFunc(&mt_dev, TRUE)) { | |
| /* | |
| * The device interrupt mask has been explicitly changed. If the device | |
| * state is such that an interrupt can occur, generate it now. | |
| */ | |
| fw_IOintr(FALSE, &mt_dev, &MTdev, 0, 0, 0xFFFF, "Mask change interrupt"); | |
| } | |
| /* | |
| * All done if there is no motion control requested. | |
| */ | |
| if ((IOAreg & IO_1732_MOTION) == 0) | |
| return IO_REPLY; | |
| /* | |
| * Drive must be selected to perform a motion operation | |
| */ | |
| if ((uptr = MTdev.iod_unit) == NULL) | |
| return IO_REJECT; | |
| /* | |
| * We now know we have a valid motion command. | |
| */ | |
| MTdev.iod_wasWriting = MTmode == MT_WRITING; | |
| /* | |
| * If we are currently writing to the tape, terminate the current | |
| * record before initiating the new tape motion command. | |
| */ | |
| if (MTmode == MT_WRITING) { | |
| st = sim_tape_wrrecf(uptr, MTbuf, MToffset); | |
| if ((mt_dev.dctrl & DBG_MTIO) != 0) | |
| mtio_trace(uptr, "wrrecf", st, TRUE, MToffset); | |
| MTmode = MT_IDLE; | |
| MTdev.STATUS &= ~IO_1732_ACTIVE; | |
| } | |
| /* | |
| * Clear ALARM, LOST data, FILL and any position information on a motion | |
| * operation | |
| */ | |
| if ((IOAreg & IO_1732_MOTION) != 0) { | |
| MTdev.STATUS &= ~IO_ST_ALARM; | |
| MTdev.STATUS &= ~(IO_ST_LOST | IO_1732_FILL); | |
| MTdev.STATUS &= ~(IO_1732_FMARK | IO_1732_EOT | IO_1732_BOT); | |
| } | |
| switch (IOAreg & IO_1732_MOTION) { | |
| case IO_1732_READ: | |
| MTmode = MT_READING; | |
| goto active; | |
| case IO_1732_WRITE: | |
| MTmode = MT_WRITING; | |
| MToffset = 0; | |
| MTremain = MTSIZ; | |
| goto active; | |
| case IO_1732_BACKSP: | |
| case IO_1732_WFM: | |
| case IO_1732_SFWD: | |
| case IO_1732_SBACK: | |
| active: | |
| MTdev.STATUS |= IO_1732_ACTIVE; | |
| break; | |
| case IO_1732_REWL: | |
| if (!MTdev.iod_wasWriting && sim_tape_bot(uptr)) { | |
| /* | |
| * If we are currently standing at the load point, complete the | |
| * request immediately. Diagnostic 0F (BD2) relies on this | |
| * behaviour. | |
| */ | |
| MTdev.STATUS |= IO_1732_BOT; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_trace(uptr, "REWL", (t_stat)-1, FALSE); | |
| fw_IOcompleteEOP(FALSE, &mt_dev, &MTdev, 0xFFFF, "Rewind complete"); | |
| return IO_REPLY; | |
| } | |
| /* FALLTHROUGH */ | |
| case IO_1732A_REWU: | |
| break; | |
| } | |
| /* | |
| * Mark I/O underway and activate a delayed operation. | |
| */ | |
| fw_IOunderwayEOP(&MTdev, 0); | |
| sim_cancel(uptr); | |
| MTdev.iod_delay = Areg & IO_1732_MOTION; | |
| sim_activate(uptr, MT_MOTION_WAIT); | |
| return IO_REPLY; | |
| } | |
| /* Perform I/O */ | |
| enum IOstatus MTin(IO_DEVICE *iod, uint8 reg) | |
| { | |
| UNIT *uptr = MTdev.iod_unit; | |
| /* | |
| * The framework only passes INP operations for the data register (0x380). | |
| */ | |
| if (uptr != NULL) { | |
| if (((MTdev.STATUS & IO_ST_READY) != 0) && (MTremain != 0)) { | |
| return doMTIn(uptr, &Areg, FALSE); | |
| } | |
| } | |
| return IO_REJECT; | |
| } | |
| enum IOstatus MTout(IO_DEVICE *iod, uint8 reg) | |
| { | |
| UNIT *uptr = MTdev.iod_unit; | |
| uint16 unit; | |
| switch (reg) { | |
| case 0x00: | |
| if (uptr != NULL) { | |
| if ((MTdev.STATUS & IO_ST_READY) != 0) | |
| return doMTOut(uptr, &Areg, FALSE); | |
| } | |
| return IO_REJECT; | |
| case 0x01: | |
| return doMTFunction(MTdev.iod_outdev); | |
| case 0x02: | |
| /* | |
| * Check for invalid bit combinations. | |
| */ | |
| if ((Areg & IO_1732_PARITY) == IO_1732_PARITY) | |
| return IO_REJECT; | |
| if ((Areg & IO_1732_DESEL) != 0) | |
| if ((Areg & ~IO_1732_DESEL) != 0) | |
| return IO_REJECT; | |
| if ((Areg & IO_1732_SEL) != 0) { | |
| /* | |
| * Check for illegal unit select. | |
| */ | |
| unit = MTdev.iod_type == DEVTYPE_1732_3 ? IO_1732_UNIT : IO_1732A_UNIT; | |
| unit = (unit & Areg) >> 7; | |
| if (unit >= mt_dev.numunits) | |
| return IO_REJECT; | |
| } | |
| switch (Areg & (IO_1732_1600 | IO_1732_556 | IO_1732_800)) { | |
| case IO_1732_1600: /* IO_1732A_200 on 1732-A */ | |
| case IO_1732_556: | |
| case IO_1732_800: | |
| if (uptr != NULL) | |
| if ((mt_dev.dctrl & DBG_DENS) != 0) { | |
| DEVICE *dptr = find_dev_from_unit(uptr); | |
| int32 u = uptr - dptr->units; | |
| fprintf(DBGOUT, | |
| "MT%d: Density changed to %04X\r\n", | |
| u, Areg & (IO_1732_1600 | IO_1732_556 | IO_1732_800)); | |
| } | |
| /* FALLTHROUGH */ | |
| case 0: /* No change in density */ | |
| break; | |
| default: | |
| return IO_REJECT; | |
| } | |
| /* | |
| * Process the select/deselect operation. | |
| */ | |
| if ((Areg & IO_1732_DESEL) != 0) { | |
| /*** TODO: Implement protected device support ***/ | |
| if ((mt_dev.dctrl & DBG_SELECT) != 0) | |
| if (MTdev.iod_unit != NULL) { | |
| DEVICE *dptr = find_dev_from_unit(uptr); | |
| int32 u = uptr - dptr->units; | |
| fprintf(DBGOUT, "MT%d - Deselected\r\n", u); | |
| } | |
| MTdev.iod_unit = NULL; | |
| fw_clearForced(&MTdev, IO_ST_READY); | |
| MTdev.STATUS2 = 0; | |
| return IO_REPLY; | |
| } | |
| if ((Areg & IO_1732_SEL) != 0) { | |
| MTdev.iod_unit = NULL; | |
| MTdev.STATUS &= ~(IO_1732_STCINT | IO_1732_FMARK | IO_1732_EOT); | |
| fw_clearForced(&MTdev, IO_ST_READY); | |
| uptr = &mt_unit[unit]; | |
| if ((uptr->flags & UNIT_ATT) != 0) { | |
| MTdev.iod_unit = uptr; | |
| fw_setForced(&MTdev, IO_ST_READY); | |
| if (sim_tape_bot(uptr)) | |
| MTdev.STATUS |= IO_1732_BOT; | |
| if (sim_tape_eot(uptr)) | |
| MTdev.STATUS |= IO_1732_EOT; | |
| } | |
| if ((mt_dev.dctrl & DBG_SELECT) != 0) | |
| fprintf(DBGOUT, "MT%d Selected\r\n", unit); | |
| MTdev.STATUS2 = 0; | |
| } | |
| /* | |
| * Remember the current mode of operation. | |
| */ | |
| MTdev.iod_mode = Areg; | |
| if ((uptr = MTdev.iod_unit) != NULL) { | |
| /* | |
| * If this operation modifies the density, remember it for later. | |
| */ | |
| if ((Areg & (IO_1732_1600 | IO_1732_556 | IO_1732_800)) != 0) { | |
| if ((uptr->flags & UNIT_7TRACK) != 0) { | |
| uptr->DENS &= ~(IO_ST2_556 | IO_ST2_800 | IO_ST2_1600); | |
| if ((Areg & IO_1732_556) != 0) | |
| uptr->DENS |= IO_ST2_556; | |
| if ((Areg & IO_1732_800) != 0) | |
| uptr->DENS |= IO_ST2_800; | |
| if (MTdev.iod_type == DEVTYPE_1732_3) | |
| if ((Areg & IO_1732_1600) != 0) | |
| uptr->DENS |= IO_ST2_1600; | |
| } | |
| } | |
| /* | |
| * Make sure STATUS2 values are consistent with actual drive status. | |
| */ | |
| MTdev.STATUS2 = uptr->DENS & (IO_ST2_556 | IO_ST2_800); | |
| if ((uptr->flags & UNIT_WPROT) != 0) | |
| MTdev.STATUS2 &= ~IO_ST2_WENABLE; | |
| else MTdev.STATUS2 |= IO_ST2_WENABLE; | |
| if ((uptr->flags & UNIT_7TRACK) != 0) | |
| MTdev.STATUS2 |= IO_ST2_7TRACK; | |
| else MTdev.STATUS2 &= ~IO_ST2_7TRACK; | |
| } | |
| return IO_REPLY; | |
| case 0x03: | |
| if ((uptr == NULL) || (MTdev.iod_type == DEVTYPE_1732_A)) | |
| return IO_REJECT; | |
| MTdev.iod_LWA = LoadFromMem(IOAreg); | |
| MTdev.iod_CWA = ++IOAreg; | |
| MTdev.iod_DSApending = TRUE; | |
| if ((mt_dev.dctrl & DBG_OPS) != 0) | |
| mt_DSAtrace(uptr, "setup"); | |
| return IO_REPLY; | |
| } | |
| return IO_REJECT; | |
| } | |
| /* Perform I/O initiated through a 1706 buffered data channel */ | |
| enum IOstatus MTBDCin(IO_DEVICE *iod, uint16 *data, uint8 reg) | |
| { | |
| UNIT *uptr = MTdev.iod_unit; | |
| if ((mt_dev.dctrl & DBG_DTRACE) != 0) { | |
| int32 u = uptr - mt_dev.units; | |
| fprintf(DBGOUT, | |
| "%sMT%d: BDC input to register %d\r\n", | |
| INTprefix, u, reg); | |
| } | |
| /* | |
| * The framework only passes INP operations for the data register (0x380). | |
| */ | |
| if (uptr != NULL) { | |
| if ((MTdev.STATUS & IO_ST_DATA) != 0) | |
| if (((MTdev.STATUS & IO_ST_READY) != 0) && (MTremain != 0)) | |
| return doMTIn(uptr, data, TRUE); | |
| } | |
| return IO_REJECT; | |
| } | |
| enum IOstatus MTBDCout(IO_DEVICE *iod, uint16 *data, uint8 reg) | |
| { | |
| UNIT *uptr = MTdev.iod_unit; | |
| if ((mt_dev.dctrl & DBG_DTRACE) != 0) { | |
| int32 u = uptr - mt_dev.units; | |
| fprintf(DBGOUT, | |
| "%sMT%d: BDC output, %04X from register %d\r\n", | |
| INTprefix, u, IOAreg, reg); | |
| } | |
| switch (reg) { | |
| case 0x00: | |
| if (uptr != NULL) { | |
| if ((MTdev.STATUS & IO_ST_READY) != 0) | |
| return doMTOut(uptr, data, TRUE); | |
| } | |
| return IO_REJECT; | |
| case 0x01: | |
| return doMTFunction(MTdev.iod_outdev); | |
| case 0x02: | |
| break; | |
| } | |
| return IO_REJECT; | |
| } | |
| t_stat mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
| { | |
| const char helpString[] = | |
| /****************************************************************************/ | |
| " The %D device is either a 1732-A or 1732-3 magtape controller.\n" | |
| "1 Hardware Description\n" | |
| " The %D device consists of either a 1732-A or 1732-3 controller along\n" | |
| " with 4 tape transports. The type con controller present may be changed\n" | |
| " by:\n\n" | |
| "+sim> SET %D 1732-A\n" | |
| "+sim> SET %D 1732-3\n\n" | |
| " The first 3 transports (MT0, MT1, MT2) are 9-track drives and MT3 is a\n" | |
| " 7-track drive. Each drive may be individually write-locked or\n" | |
| " write-enabled with:\n\n" | |
| "+sim> SET %U LOCKED\n" | |
| "+sim> SET %U WRITEENABLED\n\n" | |
| " The 1732-A controller can only perform I/O 1 or 2 bytes at a time. In\n" | |
| " order to use DMA it must be coupled with a 1706-A. Due to the lack of\n" | |
| " DMA it can only support 200, 556 and 800 BPI on 9-track transports.\n\n" | |
| " The 1732-3 is a newer controller which has DMA capability built in. It\n" | |
| " loses the ability to handle 200 BPI tape but adds the ability to access\n" | |
| " 1600 BPI phase encoded tapes.\n" | |
| "2 Equipment Address\n" | |
| " Magtape controllers are typically set to equipment address 7. This\n" | |
| " address may be changed by:\n\n" | |
| "+sim> SET %D EQUIPMENT=hexValue\n\n" | |
| "2 $Registers\n" | |
| "\n" | |
| " These registers contain the emulated state of the device. These values\n" | |
| " don't necessarily relate to any detail of the original device being\n" | |
| " emulated but are merely internal details of the emulation. STATUS and\n" | |
| " STATUS2 always contains the current status of the device as it would be\n" | |
| " read by an application program.\n" | |
| "1 Configuration\n" | |
| " A %D device is configured with various simh SET and ATTACH commands\n" | |
| "2 $Set commands\n"; | |
| return scp_help(st, dptr, uptr, flag, helpString, cptr); | |
| } |