| /* pdp10_tu.c - PDP-10 RH11/TM03/TU45 magnetic tape simulator | |
| Copyright (c) 1993-2008, Robert M Supnik | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| tu RH11/TM03/TU45 magtape | |
| 29-Apr-07 RMS Fixed bug in setting FCE on TMK (found by Naoki Hamada) | |
| 16-Feb-06 RMS Added tape capacity checking | |
| 16-Aug-05 RMS Fixed C++ declaration and cast problems | |
| 07-Jul-05 RMS Removed extraneous externs | |
| 31-Mar-05 RMS Fixed bug, ERASE/WREOF incorrectly clear CS1<done> | |
| Fixed inaccuracies in error reporting | |
| 18-Mar-05 RMS Added attached test to detach routine | |
| 23-Oct-04 RMS Fixed setting done on non data transfers | |
| 01-Oct-04 RMS Modified to set FCE on read short record, eof | |
| Implemented write check | |
| TM03 uses only den<2> for validity test | |
| TMK is cleared by new motion command, not DCLR | |
| 14-Sep-04 RMS Fixed RIP value | |
| 25-Apr-03 RMS Revised for extended file support | |
| 28-Mar-03 RMS Added multiformat support | |
| 28-Feb-03 RMS Revised for magtape library | |
| 27-Jan-03 RMS Changed to dynamically allocate buffer | |
| 21-Nov-02 RMS Fixed bug in bootstrap (reported by Michael Thompson) | |
| Fixed bug in read (reported by Harris Newman) | |
| 29-Sep-02 RMS Added variable vector support | |
| New data structures | |
| 28-Aug-02 RMS Added end of medium support | |
| 30-May-02 RMS Widened POS to 32b | |
| 22-Apr-02 RMS Changed record length error code | |
| 06-Jan-02 RMS Revised enable/disable support | |
| 30-Nov-01 RMS Added read only unit, extended SET/SHOW support | |
| 24-Nov-01 RMS Changed POS, FLG, UST to arrays | |
| 23-Oct-01 RMS Fixed bug in error interrupts | |
| New IO page address constants | |
| 05-Oct-01 RMS Rewrote interrupt handling from schematics | |
| 30-Sep-01 RMS Fixed handling of non-existent formatters | |
| 28-Sep-01 RMS Fixed interrupt handling for SC/ATA | |
| 4-May-01 RMS Fixed bug in odd address test | |
| 3-May-01 RMS Fixed drive reset to clear SSC | |
| Magnetic tapes are represented as a series of variable 8b records | |
| of the form: | |
| 32b record length in bytes - exact number, sign = error | |
| byte 0 | |
| byte 1 | |
| : | |
| byte n-2 | |
| byte n-1 | |
| 32b record length in bytes - exact number, sign = error | |
| If the byte count is odd, the record is padded with an extra byte | |
| of junk. File marks are represented by a single record length of 0. | |
| End of tape is two consecutive end of file marks. | |
| WARNING: The interupt logic of the RH11/RH70 is unusual and must be | |
| simulated with great precision. The RH11 has an internal interrupt | |
| request flop, CSTB INTR, which is controlled as follows: | |
| - Writing IE and DONE simultaneously sets CSTB INTR | |
| - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR | |
| (and also clear IE) | |
| - A transition of DONE from 0 to 1 sets CSTB from INTR | |
| The output of INTR is OR'd with the AND of RPCS1<SC,DONE,IE> to | |
| create the interrupt request signal. Thus, | |
| - The DONE interrupt is edge sensitive, but the SC interrupt is | |
| level sensitive. | |
| - The DONE interrupt, once set, is not disabled if IE is cleared, | |
| but the SC interrupt is. | |
| */ | |
| #include "pdp10_defs.h" | |
| #include "sim_tape.h" | |
| #define TU_NUMFM 1 /* #formatters */ | |
| #define TU_NUMDR 8 /* #drives */ | |
| #define USTAT u3 /* unit status */ | |
| #define UDENS u4 /* unit density */ | |
| #define UD_UNK 0 /* unknown */ | |
| #define MT_MAXFR (1 << 16) /* max data buf */ | |
| /* MTCS1 - 172440 - control/status 1 */ | |
| #define CS1_GO CSR_GO /* go */ | |
| #define CS1_V_FNC 1 /* function pos */ | |
| #define CS1_M_FNC 037 /* function mask */ | |
| #define CS1_N_FNC (CS1_M_FNC + 1) | |
| #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) | |
| #define FNC_NOP 000 /* no operation */ | |
| #define FNC_UNLOAD 001 /* unload */ | |
| #define FNC_REWIND 003 /* rewind */ | |
| #define FNC_FCLR 004 /* formatter clear */ | |
| #define FNC_RIP 010 /* read in preset */ | |
| #define FNC_ERASE 012 /* erase tape */ | |
| #define FNC_WREOF 013 /* write tape mark */ | |
| #define FNC_SPACEF 014 /* space forward */ | |
| #define FNC_SPACER 015 /* space reverse */ | |
| #define FNC_XFER 024 /* >=? data xfr */ | |
| #define FNC_WCHKF 024 /* write check */ | |
| #define FNC_WCHKR 027 /* write check rev */ | |
| #define FNC_WRITE 030 /* write */ | |
| #define FNC_READF 034 /* read forward */ | |
| #define FNC_READR 037 /* read reverse */ | |
| #define CS1_IE CSR_IE /* int enable */ | |
| #define CS1_DONE CSR_DONE /* ready */ | |
| #define CS1_V_UAE 8 /* Unibus addr ext */ | |
| #define CS1_M_UAE 03 | |
| #define CS1_UAE (CS1_M_UAE << CS1_V_UAE) | |
| #define CS1_DVA 0004000 /* drive avail NI */ | |
| #define CS1_MCPE 0020000 /* Mbus par err NI */ | |
| #define CS1_TRE 0040000 /* transfer err */ | |
| #define CS1_SC 0100000 /* special cond */ | |
| #define CS1_MBZ 0012000 | |
| #define CS1_DRV (CS1_FNC | CS1_GO) | |
| #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) | |
| #define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) | |
| /* MTWC - 172442 - word count */ | |
| /* MTBA - 172444 - base address */ | |
| #define BA_MBZ 0000001 /* must be zero */ | |
| /* MTFC - 172446 - frame count */ | |
| /* MTCS2 - 172450 - control/status 2 */ | |
| #define CS2_V_FMTR 0 /* formatter select */ | |
| #define CS2_M_FMTR 07 | |
| #define CS2_FMTR (CS2_M_FMTR << CS2_V_FMTR) | |
| #define CS2_UAI 0000010 /* addr inhibit NI */ | |
| #define CS2_PAT 0000020 /* parity test NI */ | |
| #define CS2_CLR 0000040 /* controller clear */ | |
| #define CS2_IR 0000100 /* input ready */ | |
| #define CS2_OR 0000200 /* output ready */ | |
| #define CS2_MDPE 0000400 /* Mbus par err NI */ | |
| #define CS2_MXF 0001000 /* missed xfer NI */ | |
| #define CS2_PGE 0002000 /* program err */ | |
| #define CS2_NEM 0004000 /* nx mem err */ | |
| #define CS2_NEF 0010000 /* nx fmter err */ | |
| #define CS2_PE 0020000 /* parity err NI */ | |
| #define CS2_WCE 0040000 /* write chk err */ | |
| #define CS2_DLT 0100000 /* data late NI */ | |
| #define CS2_MBZ (CS2_CLR | CS2_WCE) | |
| #define CS2_RW (CS2_FMTR | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) | |
| #define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ | |
| CS2_NEF | CS2_PE | CS2_DLT ) | |
| #define GET_FMTR(x) (((x) >> CS2_V_FMTR) & CS2_M_FMTR) | |
| /* MTFS - 172452 - formatter status | |
| + indicates kept in drive status | |
| ^ indicates calculated on the fly | |
| */ | |
| #define FS_SAT 0000001 /* slave attention */ | |
| #define FS_BOT 0000002 /* ^beginning of tape */ | |
| #define FS_TMK 0000004 /* end of file */ | |
| #define FS_ID 0000010 /* ID burst detected */ | |
| #define FS_SLOW 0000020 /* slowing down NI */ | |
| #define FS_PE 0000040 /* ^PE status */ | |
| #define FS_SSC 0000100 /* slave stat change */ | |
| #define FS_RDY 0000200 /* ^formatter ready */ | |
| #define FS_FPR 0000400 /* formatter present */ | |
| #define FS_EOT 0002000 /* +end of tape */ | |
| #define FS_WRL 0004000 /* ^write locked */ | |
| #define FS_MOL 0010000 /* ^medium online */ | |
| #define FS_PIP 0020000 /* +pos in progress */ | |
| #define FS_ERR 0040000 /* ^error */ | |
| #define FS_ATA 0100000 /* attention active */ | |
| #define FS_REW 0200000 /* +rewinding */ | |
| #define FS_DYN (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \ | |
| FS_RDY | FS_PE | FS_BOT) | |
| /* MTER - 172454 - error register */ | |
| #define ER_ILF 0000001 /* illegal func */ | |
| #define ER_ILR 0000002 /* illegal register */ | |
| #define ER_RMR 0000004 /* reg mod refused */ | |
| #define ER_MCP 0000010 /* Mbus cpar err NI */ | |
| #define ER_FER 0000020 /* format sel err */ | |
| #define ER_MDP 0000040 /* Mbus dpar err NI */ | |
| #define ER_VPE 0000100 /* vert parity err */ | |
| #define ER_CRC 0000200 /* CRC err NI */ | |
| #define ER_NSG 0000400 /* non std gap err NI */ | |
| #define ER_FCE 0001000 /* frame count err */ | |
| #define ER_ITM 0002000 /* inv tape mark NI */ | |
| #define ER_NXF 0004000 /* wlock or fnc err */ | |
| #define ER_DTE 0010000 /* time err NI */ | |
| #define ER_OPI 0020000 /* op incomplete */ | |
| #define ER_UNS 0040000 /* drive unsafe */ | |
| #define ER_DCK 0100000 /* data check NI */ | |
| /* MTAS - 172456 - attention summary */ | |
| #define AS_U0 0000001 /* unit 0 flag */ | |
| /* MTCC - 172460 - check character, read only */ | |
| #define CC_MBZ 0177000 /* must be zero */ | |
| /* MTDB - 172462 - data buffer */ | |
| /* MTMR - 172464 - maintenance register */ | |
| #define MR_RW 0177637 /* read/write */ | |
| /* MTDT - 172466 - drive type */ | |
| #define DT_NSA 0100000 /* not sect addr */ | |
| #define DT_TAPE 0040000 /* tape */ | |
| #define DT_PRES 0002000 /* slave present */ | |
| #define DT_TM03 0000040 /* TM03 formatter */ | |
| #define DT_OFF 0000010 /* drive off */ | |
| #define DT_TE16 0000011 /* TE16 */ | |
| #define DT_TU45 0000012 /* TU45 */ | |
| #define DT_TU77 0000014 /* TU77 */ | |
| /* MTSN - 172470 - serial number */ | |
| /* MTTC - 172472 - tape control register */ | |
| #define TC_V_UNIT 0 /* unit select */ | |
| #define TC_M_UNIT 07 | |
| #define TC_V_EVN 0000010 /* even parity */ | |
| #define TC_V_FMT 4 /* format select */ | |
| #define TC_M_FMT 017 | |
| #define TC_10C 00 /* PDP-10 core dump */ | |
| #define TC_IND 03 /* industry standard */ | |
| #define TC_V_DEN 8 /* density select */ | |
| #define TC_M_DEN 07 | |
| #define TC_800 3 /* 800 bpi */ | |
| #define TC_1600 4 /* 1600 bpi */ | |
| #define TC_AER 0010000 /* abort on error */ | |
| #define TC_SAC 0020000 /* slave addr change */ | |
| #define TC_FCS 0040000 /* frame count status */ | |
| #define TC_ACC 0100000 /* accelerating NI */ | |
| #define TC_RW 0013777 | |
| #define TC_MBZ 0004000 | |
| #define TC_RIP ((TC_800 << TC_V_DEN) || (TC_10C << TC_V_FMT)) | |
| #define GET_DEN(x) (((x) >> TC_V_DEN) & TC_M_DEN) | |
| #define GET_FMT(x) (((x) >> TC_V_FMT) & TC_M_FMT) | |
| #define GET_DRV(x) (((x) >> TC_V_UNIT) & TC_M_UNIT) | |
| /* Mapping macros */ | |
| #define XWC_MBZ 0000001 /* wc<0> mbz */ | |
| #define XBA_MBZ 0000001 /* addr<0> mbz */ | |
| #define XBA_ODD 0000002 /* odd address */ | |
| #define TXFR(b,w,od) if (((b) & XBA_MBZ) || ((w) & XWC_MBZ) || \ | |
| (((b) & XBA_ODD) != ((od) << 1))) { \ | |
| tucs2 = tucs2 | CS2_NEM; \ | |
| ubcs[1] = ubcs[1] | UBCS_TMO; \ | |
| tucs1 = tucs1 & ~CS1_GO; \ | |
| update_tucs (CS1_DONE, drv); \ | |
| return SCPE_OK; \ | |
| } | |
| #define NEWPAGE(v,m) (((v) & PAG_M_OFF) == (m)) | |
| #define MAPM(v,p,f) vpn = PAG_GETVPN (v); \ | |
| if ((vpn >= UMAP_MEMSIZE) || ((ubmap[1][vpn] & \ | |
| (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != \ | |
| (UMAP_VLD | f))) { \ | |
| tucs2 = tucs2 | CS2_NEM; \ | |
| ubcs[1] = ubcs[1] | UBCS_TMO; \ | |
| break; \ | |
| } \ | |
| p = (ubmap[1][vpn] + PAG_GETOFF (v)) & PAMASK; \ | |
| if (MEM_ADDR_NXM (p)) { \ | |
| tucs2 = tucs2 | CS2_NEM; \ | |
| ubcs[1] = ubcs[1] | UBCS_TMO; \ | |
| break; \ | |
| } | |
| extern d10 *M; /* memory */ | |
| extern int32 int_req; | |
| extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus map */ | |
| extern int32 ubcs[UBANUM]; | |
| extern UNIT cpu_unit; | |
| extern int32 sim_switches; | |
| extern FILE *sim_deb; | |
| int32 tucs1 = 0; /* control/status 1 */ | |
| int32 tuwc = 0; /* word count */ | |
| int32 tuba = 0; /* bus address */ | |
| int32 tufc = 0; /* frame count */ | |
| int32 tucs2 = 0; /* control/status 2 */ | |
| int32 tufs = 0; /* formatter status */ | |
| int32 tuer = 0; /* error status */ | |
| int32 tucc = 0; /* check character */ | |
| int32 tudb = 0; /* data buffer */ | |
| int32 tumr = 0; /* maint register */ | |
| int32 tutc = 0; /* tape control */ | |
| int32 tuiff = 0; /* INTR flip/flop */ | |
| int32 tu_time = 10; /* record latency */ | |
| int32 tu_stopioe = 1; /* stop on error */ | |
| int32 tu_log = 0; /* debug */ | |
| int32 reg_in_fmtr[32] = { /* reg in formatter */ | |
| 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 | |
| }; | |
| int32 reg_in_fmtr1[32] = { /* rmr if write + go */ | |
| 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, | |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 | |
| }; | |
| int32 fmt_test[16] = { /* fmt bytes/10 wd */ | |
| 5, 0, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | |
| }; | |
| static char *tu_fname[CS1_N_FNC] = { | |
| "NOP", "UNLD", "2", "REW", "FCLR", "5", "6", "7", | |
| "RIP", "11", "ERASE", "WREOF", "SPCF", "SPCR", "16", "17", | |
| "20", "21", "22", "23", "WRCHKF", "25", "26", "WRCHKR", | |
| "WRITE", "31", "32", "33", "READF", "35", "36" "READR" | |
| }; | |
| static uint8 *xbuf = NULL; /* xfer buffer */ | |
| t_stat tu_rd (int32 *data, int32 PA, int32 access); | |
| t_stat tu_wr (int32 data, int32 PA, int32 access); | |
| int32 tu_inta (void); | |
| t_stat tu_svc (UNIT *uptr); | |
| t_stat tu_reset (DEVICE *dptr); | |
| t_stat tu_attach (UNIT *uptr, char *cptr); | |
| t_stat tu_detach (UNIT *uptr); | |
| t_stat tu_boot (int32 unitno, DEVICE *dptr); | |
| void tu_go (int32 drv); | |
| void set_tuer (int32 flag); | |
| void update_tucs (int32 flag, int32 drv); | |
| t_stat tu_map_err (UNIT *uptr, t_stat st, t_bool qdt); | |
| /* TU data structures | |
| tu_dev TU device descriptor | |
| tu_unit TU unit list | |
| tu_reg TU register list | |
| tu_mod TU modifier list | |
| */ | |
| DIB tu_dib = { | |
| IOBA_TU, IOLN_TU, &tu_rd, &tu_wr, | |
| 1, IVCL (TU), VEC_TU, { &tu_inta } | |
| }; | |
| UNIT tu_unit[] = { | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, | |
| { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } | |
| }; | |
| REG tu_reg[] = { | |
| { ORDATA (MTCS1, tucs1, 16) }, | |
| { ORDATA (MTWC, tuwc, 16) }, | |
| { ORDATA (MTBA, tuba, 16) }, | |
| { ORDATA (MTFC, tufc, 16) }, | |
| { ORDATA (MTCS2, tucs2, 16) }, | |
| { ORDATA (MTFS, tufs, 16) }, | |
| { ORDATA (MTER, tuer, 16) }, | |
| { ORDATA (MTCC, tucc, 16) }, | |
| { ORDATA (MTDB, tudb, 16) }, | |
| { ORDATA (MTMR, tumr, 16) }, | |
| { ORDATA (MTTC, tutc, 16) }, | |
| { FLDATA (IFF, tuiff, 0) }, | |
| { FLDATA (INT, int_req, INT_V_TU) }, | |
| { FLDATA (DONE, tucs1, CSR_V_DONE) }, | |
| { FLDATA (IE, tucs1, CSR_V_IE) }, | |
| { FLDATA (STOP_IOE, tu_stopioe, 0) }, | |
| { DRDATA (TIME, tu_time, 24), PV_LEFT }, | |
| { URDATA (UST, tu_unit[0].USTAT, 8, 17, 0, TU_NUMDR, 0) }, | |
| { URDATA (POS, tu_unit[0].pos, 10, T_ADDR_W, 0, | |
| TU_NUMDR, PV_LEFT | REG_RO) }, | |
| { ORDATA (LOG, tu_log, 8), REG_HIDDEN }, | |
| { NULL } | |
| }; | |
| MTAB tu_mod[] = { | |
| { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, | |
| { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, | |
| { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, | |
| { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", | |
| &sim_tape_set_capac, &sim_tape_show_capac, NULL }, | |
| { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, | |
| NULL, &show_addr, NULL }, | |
| { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, | |
| NULL, &show_vec, NULL }, | |
| { 0 } | |
| }; | |
| DEVICE tu_dev = { | |
| "TU", tu_unit, tu_reg, tu_mod, | |
| TU_NUMDR, 10, 31, 1, 8, 8, | |
| NULL, NULL, &tu_reset, | |
| &tu_boot, &tu_attach, &tu_detach, | |
| &tu_dib, DEV_UBUS | DEV_DEBUG | DEV_TAPE | |
| }; | |
| /* I/O dispatch routine, I/O addresses 17772440 - 17772472 */ | |
| t_stat tu_rd (int32 *data, int32 PA, int32 access) | |
| { | |
| int32 fmtr, drv, j; | |
| fmtr = GET_FMTR (tucs2); /* get current fmtr */ | |
| drv = GET_DRV (tutc); /* get current drive */ | |
| j = (PA >> 1) & 017; /* get reg offset */ | |
| if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ | |
| tucs2 = tucs2 | CS2_NEF; /* set error flag */ | |
| update_tucs (CS1_SC, drv); /* request intr */ | |
| *data = 0; | |
| return SCPE_OK; | |
| } | |
| update_tucs (0, drv); /* update status */ | |
| switch (j) { /* decode PA<4:1> */ | |
| case 000: /* MTCS1 */ | |
| if (fmtr != 0) | |
| *data = tucs1 & ~CS1_DRV; | |
| else *data = tucs1; | |
| break; | |
| case 001: /* MTWC */ | |
| *data = tuwc; | |
| break; | |
| case 002: /* MTBA */ | |
| *data = tuba = tuba & ~BA_MBZ; | |
| break; | |
| case 003: /* MTFC */ | |
| *data = tufc; | |
| break; | |
| case 004: /* MTCS2 */ | |
| *data = tucs2 = (tucs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; | |
| break; | |
| case 005: /* MTFS */ | |
| *data = tufs & 0177777; /* mask off rewind */ | |
| break; | |
| case 006: /* MTER */ | |
| *data = tuer; | |
| break; | |
| case 007: /* MTAS */ | |
| *data = (tufs & FS_ATA)? AS_U0: 0; | |
| break; | |
| case 010: /* MTCC */ | |
| *data = tucc = tucc & ~CC_MBZ; | |
| break; | |
| case 011: /* MTDB */ | |
| *data = tudb; | |
| break; | |
| case 012: /* MTMR */ | |
| *data = tumr; | |
| break; | |
| case 013: /* MTDT */ | |
| *data = DT_NSA | DT_TAPE | DT_TM03 | | |
| ((tu_unit[drv].flags & UNIT_DIS)? DT_OFF: (DT_PRES | DT_TU45)); | |
| break; | |
| case 014: /* MTSN */ | |
| *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1); | |
| break; | |
| case 015: /* MTTC */ | |
| *data = tutc = tutc & ~TC_MBZ; | |
| break; | |
| default: /* all others */ | |
| set_tuer (ER_ILR); | |
| update_tucs (0, drv); | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| t_stat tu_wr (int32 data, int32 PA, int32 access) | |
| { | |
| int32 cs1f, fmtr, drv, j; | |
| cs1f = 0; /* no int on cs1 upd */ | |
| fmtr = GET_FMTR (tucs2); /* get formatter */ | |
| drv = GET_DRV (tutc); /* get current unit */ | |
| j = (PA >> 1) & 017; /* get reg offset */ | |
| if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ | |
| tucs2 = tucs2 | CS2_NEF; /* set error flag */ | |
| update_tucs (CS1_SC, drv); /* request intr */ | |
| return SCPE_OK; | |
| } | |
| if (reg_in_fmtr1[j] && ((tucs1 & CS1_DONE) == 0)) { /* formatter busy? */ | |
| set_tuer (ER_RMR); /* won't write */ | |
| update_tucs (0, drv); | |
| return SCPE_OK; | |
| } | |
| switch (j) { /* decode PA<4:1> */ | |
| case 000: /* MTCS1 */ | |
| if ((access == WRITEB) && (PA & 1)) | |
| data = data << 8; | |
| if (data & CS1_TRE) { /* error clear? */ | |
| tucs1 = tucs1 & ~CS1_TRE; /* clr CS1<TRE> */ | |
| tucs2 = tucs2 & ~CS2_ERR; /* clr CS2<15:8> */ | |
| } | |
| if ((access == WRITE) || (PA & 1)) { /* hi byte write? */ | |
| if (tucs1 & CS1_DONE) /* done set? */ | |
| tucs1 = (tucs1 & ~CS1_UAE) | (data & CS1_UAE); | |
| } | |
| if ((access == WRITE) || !(PA & 1)) { /* lo byte write? */ | |
| if ((data & CS1_DONE) && (data & CS1_IE)) /* to DONE+IE? */ | |
| tuiff = 1; /* set CSTB INTR */ | |
| tucs1 = (tucs1 & ~CS1_IE) | (data & CS1_IE); | |
| if (fmtr != 0) { /* nx formatter? */ | |
| tucs2 = tucs2 | CS2_NEF; /* set error flag */ | |
| cs1f = CS1_SC; /* req interrupt */ | |
| } | |
| else if (tucs1 & CS1_GO) { /* busy? */ | |
| if (tucs1 & CS1_DONE) | |
| set_tuer (ER_RMR); | |
| else tucs2 = tucs2 | CS2_PGE; | |
| } | |
| else { | |
| tucs1 = (tucs1 & ~CS1_DRV) | (data & CS1_DRV); | |
| if (tucs1 & CS1_GO) | |
| tu_go (drv); | |
| } | |
| } | |
| break; | |
| case 001: /* MTWC */ | |
| if (access == WRITEB) | |
| data = (PA & 1)? | |
| (tuwc & 0377) | (data << 8): (tuwc & ~0377) | data; | |
| tuwc = data; | |
| break; | |
| case 002: /* MTBA */ | |
| if (access == WRITEB) | |
| data = (PA & 1)? | |
| (tuba & 0377) | (data << 8): (tuba & ~0377) | data; | |
| tuba = data & ~BA_MBZ; | |
| break; | |
| case 003: /* MTFC */ | |
| if (access == WRITEB) | |
| data = (PA & 1)? | |
| (tufc & 0377) | (data << 8): (tufc & ~0377) | data; | |
| tufc = data; | |
| tutc = tutc | TC_FCS; /* set fc flag */ | |
| break; | |
| case 004: /* MTCS2 */ | |
| if ((access == WRITEB) && (PA & 1)) | |
| data = data << 8; | |
| if (data & CS2_CLR) /* init? */ | |
| tu_reset (&tu_dev); | |
| else { | |
| if ((data & ~tucs2) & (CS2_PE | CS2_MXF)) | |
| cs1f = CS1_SC; /* diagn intr */ | |
| if (access == WRITEB) /* merge data */ | |
| data = (tucs2 & ((PA & 1)? 0377: 0177400)) | data; | |
| tucs2 = (tucs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; | |
| } | |
| break; | |
| case 007: /* MTAS */ | |
| if ((access == WRITEB) && (PA & 1)) | |
| break; | |
| if (data & AS_U0) | |
| tufs = tufs & ~FS_ATA; | |
| break; | |
| case 011: /* MTDB */ | |
| if (access == WRITEB) | |
| data = (PA & 1)? | |
| (tudb & 0377) | (data << 8): (tudb & ~0377) | data; | |
| tudb = data; | |
| break; | |
| case 012: /* MTMR */ | |
| if (access == WRITEB) | |
| data = (PA & 1)? | |
| (tumr & 0377) | (data << 8): (tumr & ~0377) | data; | |
| tumr = (tumr & ~MR_RW) | (data & MR_RW); | |
| break; | |
| case 015: /* MTTC */ | |
| if (access == WRITEB) | |
| data = (PA & 1)? | |
| (tutc & 0377) | (data << 8): (tutc & ~0377) | data; | |
| tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC; | |
| drv = GET_DRV (tutc); | |
| break; | |
| case 005: /* MTFS */ | |
| case 006: /* MTER */ | |
| case 010: /* MTCC */ | |
| case 013: /* MTDT */ | |
| case 014: /* MTSN */ | |
| break; /* read only */ | |
| default: /* all others */ | |
| set_tuer (ER_ILR); | |
| break; | |
| } /* end switch */ | |
| update_tucs (cs1f, drv); /* update status */ | |
| return SCPE_OK; | |
| } | |
| /* New magtape command */ | |
| void tu_go (int32 drv) | |
| { | |
| int32 fnc, den; | |
| UNIT *uptr; | |
| fnc = GET_FNC (tucs1); /* get function */ | |
| den = GET_DEN (tutc); /* get density */ | |
| uptr = tu_dev.units + drv; /* get unit */ | |
| if (DEBUG_PRS (tu_dev)) | |
| fprintf (sim_deb, ">>TU%d STRT: fnc=%s, cs1=%06o, cs2=%06o, ba=%06o, wc=%06o, fc=%06o, fs=%06o, er=%06o, pos=%d\n", | |
| drv, tu_fname[fnc], tucs1, tucs2, tuba, tuwc, tufc, tufs, tuer, uptr->pos); | |
| if ((fnc != FNC_FCLR) && /* not clear & err */ | |
| ((tufs & FS_ERR) || sim_is_active (uptr))) { /* or in motion? */ | |
| set_tuer (ER_ILF); /* set err, ATN */ | |
| tucs1 = tucs1 & ~CS1_GO; /* clear go */ | |
| update_tucs (CS1_SC, drv); /* request intr */ | |
| return; | |
| } | |
| tufs = tufs & ~FS_ATA; /* clear attention */ | |
| tutc = tutc & ~TC_SAC; /* clear addr change */ | |
| switch (fnc) { /* case on function */ | |
| case FNC_FCLR: /* drive clear */ | |
| tuer = 0; /* clear errors */ | |
| tutc = tutc & ~TC_FCS; /* clear fc status */ | |
| tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_ERR); | |
| sim_cancel (uptr); /* reset drive */ | |
| uptr->USTAT = 0; | |
| case FNC_NOP: | |
| tucs1 = tucs1 & ~CS1_GO; /* no operation */ | |
| return; | |
| case FNC_RIP: /* read-in preset */ | |
| tutc = TC_RIP; /* density = 800 */ | |
| sim_tape_rewind (&tu_unit[0]); /* rewind unit 0 */ | |
| tu_unit[0].USTAT = 0; | |
| tucs1 = tucs1 & ~CS1_GO; | |
| tufs = tufs & ~FS_TMK; | |
| return; | |
| case FNC_UNLOAD: /* unload */ | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ | |
| set_tuer (ER_UNS); | |
| break; | |
| } | |
| detach_unit (uptr); | |
| uptr->USTAT = FS_REW; | |
| sim_activate (uptr, tu_time); | |
| tucs1 = tucs1 & ~CS1_GO; | |
| tufs = tufs & ~FS_TMK; | |
| return; | |
| case FNC_REWIND: | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ | |
| set_tuer (ER_UNS); | |
| break; | |
| } | |
| uptr->USTAT = FS_PIP | FS_REW; | |
| sim_activate (uptr, tu_time); | |
| tucs1 = tucs1 & ~CS1_GO; | |
| tufs = tufs & ~FS_TMK; | |
| return; | |
| case FNC_SPACEF: | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ | |
| set_tuer (ER_UNS); | |
| break; | |
| } | |
| if (sim_tape_eot (uptr) || ((tutc & TC_FCS) == 0)) { | |
| set_tuer (ER_NXF); | |
| break; | |
| } | |
| uptr->USTAT = FS_PIP; | |
| goto GO_XFER; | |
| case FNC_SPACER: | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ | |
| set_tuer (ER_UNS); | |
| break; | |
| } | |
| if (sim_tape_bot (uptr) || ((tutc & TC_FCS) == 0)) { | |
| set_tuer (ER_NXF); | |
| break; | |
| } | |
| uptr->USTAT = FS_PIP; | |
| goto GO_XFER; | |
| case FNC_WREOF: /* write tape mark */ | |
| case FNC_ERASE: /* erase */ | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ | |
| set_tuer (ER_UNS); | |
| break; | |
| } | |
| if (sim_tape_wrp (uptr)) { /* write locked? */ | |
| set_tuer (ER_NXF); | |
| break; | |
| } | |
| if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ | |
| set_tuer (ER_FER); | |
| break; | |
| } | |
| if (uptr->UDENS == UD_UNK) /* set dens */ | |
| uptr->UDENS = den; | |
| uptr->USTAT = 0; | |
| goto GO_XFER; | |
| case FNC_WCHKR: /* wchk = read */ | |
| case FNC_READR: /* read rev */ | |
| if (tufs & FS_BOT) { /* beginning of tape? */ | |
| set_tuer (ER_NXF); | |
| break; | |
| } | |
| goto DATA_XFER; | |
| case FNC_WRITE: /* write */ | |
| if (((tutc & TC_FCS) == 0) || /* frame cnt = 0? */ | |
| ((den == TC_800) && (tufc > 0777765))) { /* NRZI, fc < 13? */ | |
| set_tuer (ER_NXF); | |
| break; | |
| } | |
| case FNC_WCHKF: /* wchk = read */ | |
| case FNC_READF: /* read */ | |
| DATA_XFER: | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ | |
| set_tuer (ER_UNS); | |
| break; | |
| } | |
| if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ | |
| set_tuer (ER_FER); | |
| break; | |
| } | |
| if (uptr->UDENS == UD_UNK) /* set dens */ | |
| uptr->UDENS = den; | |
| uptr->USTAT = 0; | |
| tucs1 = tucs1 & ~CS1_DONE; /* clear done */ | |
| GO_XFER: | |
| tucs2 = tucs2 & ~CS2_ERR; /* clear errors */ | |
| tucs1 = tucs1 & ~(CS1_TRE | CS1_MCPE); | |
| tufs = tufs & ~(FS_TMK | FS_ID); /* clear eof, id */ | |
| sim_activate (uptr, tu_time); | |
| return; | |
| default: /* all others */ | |
| set_tuer (ER_ILF); /* not supported */ | |
| break; | |
| } /* end case function */ | |
| tucs1 = tucs1 & ~CS1_GO; /* clear go */ | |
| update_tucs (CS1_SC, drv); /* set intr */ | |
| return; | |
| } | |
| /* Unit service | |
| Complete movement or data transfer command | |
| Unit must exist - can't remove an active unit | |
| Unit must be attached - detach cancels in progress operations | |
| */ | |
| t_stat tu_svc (UNIT *uptr) | |
| { | |
| int32 fnc, fmt, i, j, k, wc10, ba10; | |
| int32 ba, fc, wc, drv, mpa10, vpn; | |
| d10 val, v[4]; | |
| t_mtrlnt tbc; | |
| t_stat st, r = SCPE_OK; | |
| drv = (int32) (uptr - tu_dev.units); /* get drive # */ | |
| if (uptr->USTAT & FS_REW) { /* rewind or unload? */ | |
| sim_tape_rewind (uptr); /* rewind tape */ | |
| uptr->USTAT = 0; /* clear status */ | |
| tufs = tufs | FS_ATA | FS_SSC; | |
| update_tucs (CS1_SC, drv); /* update status */ | |
| return SCPE_OK; | |
| } | |
| fnc = GET_FNC (tucs1); /* get command */ | |
| fmt = GET_FMT (tutc); /* get format */ | |
| ba = GET_UAE (tucs1) | tuba; /* get bus address */ | |
| wc = 0200000 - tuwc; /* get word count */ | |
| fc = 0200000 - tufc; /* get frame count */ | |
| wc10 = wc >> 1; /* 10 word count */ | |
| ba10 = ba >> 2; /* 10 word addr */ | |
| uptr->USTAT = 0; /* clear status */ | |
| switch (fnc) { /* case on function */ | |
| /* Non-data transfer commands - set ATA when done */ | |
| case FNC_SPACEF: /* space forward */ | |
| do { | |
| tufc = (tufc + 1) & 0177777; /* incr fc */ | |
| if ((st = sim_tape_sprecf (uptr, &tbc))) { /* space rec fwd, err? */ | |
| r = tu_map_err (uptr, st, 0); /* map error */ | |
| break; | |
| } | |
| } while ((tufc != 0) && !sim_tape_eot (uptr)); | |
| if (tufc) | |
| set_tuer (ER_FCE); | |
| else tutc = tutc & ~TC_FCS; | |
| tufs = tufs | FS_ATA; | |
| break; | |
| case FNC_SPACER: /* space reverse */ | |
| do { | |
| tufc = (tufc + 1) & 0177777; /* incr wc */ | |
| if ((st = sim_tape_sprecr (uptr, &tbc))) { /* space rec rev, err? */ | |
| r = tu_map_err (uptr, st, 0); /* map error */ | |
| break; | |
| } | |
| } while (tufc != 0); | |
| if (tufc) | |
| set_tuer (ER_FCE); | |
| else tutc = tutc & ~TC_FCS; | |
| tufs = tufs | FS_ATA; | |
| break; | |
| case FNC_WREOF: /* write end of file */ | |
| if ((st = sim_tape_wrtmk (uptr))) /* write tmk, err? */ | |
| r = tu_map_err (uptr, st, 0); /* map error */ | |
| tufs = tufs | FS_ATA; | |
| break; | |
| case FNC_ERASE: | |
| if (sim_tape_wrp (uptr)) /* write protected? */ | |
| r = tu_map_err (uptr, MTSE_WRP, 0); /* map error */ | |
| tufs = tufs | FS_ATA; | |
| break; | |
| /* Data transfer commands | |
| These commands must take into account the action of the "bit fiddler", which | |
| converts between PDP-10 format and tape format. Only two tape formats are | |
| supported: | |
| PDP-10 core dump: write 36b as byte 0/byte 1/byte 2/byte 3/0000'last nibble | |
| industry mode: write hi 32b as byte 0/byte 1/byte 2/byte 3 | |
| These commands must also take into account the action of the Unibus adapter, | |
| which munges PDP-10 addresses through the Unibus map. | |
| */ | |
| case FNC_READF: /* read */ | |
| case FNC_WCHKF: /* wcheck = read */ | |
| tufc = 0; /* clear frame count */ | |
| if ((uptr->UDENS == TC_1600) && sim_tape_bot (uptr)) | |
| tufs = tufs | FS_ID; /* PE BOT? ID burst */ | |
| TXFR (ba, wc, 0); /* validate transfer */ | |
| if ((st = sim_tape_rdrecf (uptr, xbuf, &tbc, MT_MAXFR))) {/* read fwd */ | |
| if (st == MTSE_TMK) /* TMK also sets FCE */ | |
| set_tuer (ER_FCE); | |
| r = tu_map_err (uptr, st, 1); /* map error */ | |
| break; /* done */ | |
| } | |
| for (i = j = 0; (i < wc10) && (j < ((int32) tbc)); i++) { | |
| if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ | |
| MAPM (ba10 + i, mpa10, 0); | |
| } | |
| for (k = 0; k < 4; k++) | |
| v[k] = xbuf[j++]; | |
| val = (v[0] << 28) | (v[1] << 20) | (v[2] << 12) | (v[3] << 4); | |
| if (fmt == TC_10C) | |
| val = val | ((d10) xbuf[j++] & 017); | |
| if (fnc == FNC_READF) /* read? store */ | |
| M[mpa10] = val; | |
| else if (M[mpa10] != val) { /* wchk, mismatch? */ | |
| tucs2 = tucs2 | CS2_WCE; /* flag, stop */ | |
| break; | |
| } | |
| mpa10 = mpa10 + 1; | |
| } /* end for */ | |
| tufc = tbc & 0177777; | |
| tuwc = (tuwc + (i << 1)) & 0177777; | |
| ba = ba + (i << 2); | |
| if (tuwc) /* short record? */ | |
| set_tuer (ER_FCE); | |
| break; | |
| case FNC_WRITE: /* write */ | |
| TXFR (ba, wc, 0); /* validate transfer */ | |
| for (i = j = 0; (i < wc10) && (j < fc); i++) { | |
| if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ | |
| MAPM (ba10 + i, mpa10, 0); | |
| } | |
| val = M[mpa10]; | |
| xbuf[j++] = (uint8) ((val >> 28) & 0377); | |
| xbuf[j++] = (uint8) ((val >> 20) & 0377); | |
| xbuf[j++] = (uint8) ((val >> 12) & 0377); | |
| xbuf[j++] = (uint8) ((val >> 4) & 0377); | |
| if (fmt == TC_10C) | |
| xbuf[j++] = (uint8) (val & 017); | |
| mpa10 = mpa10 + 1; | |
| } /* end for */ | |
| if (j < fc) /* short record? */ | |
| fc = j; | |
| if ((st = sim_tape_wrrecf (uptr, xbuf, fc))) /* write rec, err? */ | |
| r = tu_map_err (uptr, st, 1); /* map error */ | |
| else { | |
| tufc = (tufc + fc) & 0177777; | |
| if (tufc == 0) | |
| tutc = tutc & ~TC_FCS; | |
| tuwc = (tuwc + (i << 1)) & 0177777; | |
| ba = ba + (i << 2); | |
| } | |
| break; | |
| case FNC_READR: /* read reverse */ | |
| case FNC_WCHKR: /* wcheck = read */ | |
| tufc = 0; /* clear frame count */ | |
| TXFR (ba, wc, 1); /* validate xfer rev */ | |
| if ((st = sim_tape_rdrecr (uptr, xbuf + 4, &tbc, MT_MAXFR))) {/* read rev */ | |
| if (st == MTSE_TMK) /* TMK also sets FCE */ | |
| set_tuer (ER_FCE); | |
| r = tu_map_err (uptr, st, 1); /* map error */ | |
| break; /* done */ | |
| } | |
| for (i = 0; i < 4; i++) | |
| xbuf[i] = 0; | |
| for (i = 0, j = tbc + 4; (i < wc10) && (j >= 4); i++) { | |
| if ((i == 0) || NEWPAGE (ba10 - i, PAG_M_OFF)) { /* map page */ | |
| MAPM (ba10 - i, mpa10, UMAP_RRV); | |
| } | |
| val = ((fmt == TC_10C)? (((d10) xbuf [--j]) & 017): 0); | |
| for (k = 0; k < 4; i++) | |
| v[k] = xbuf[--j]; | |
| val = val | (v[0] << 4) | (v[1] << 12) | (v[2] << 20) | (v[3] << 28); | |
| if (fnc == FNC_READR) /* read? store */ | |
| M[mpa10] = val; | |
| else if (M[mpa10] != val) { /* wchk, mismatch? */ | |
| tucs2 = tucs2 | CS2_WCE; /* flag, stop */ | |
| break; | |
| } | |
| mpa10 = mpa10 - 1; | |
| } /* end for */ | |
| tufc = tbc & 0177777; | |
| tuwc = (tuwc + (i << 1)) & 0177777; | |
| ba = ba - (i << 2); | |
| if (tuwc) /* short record? */ | |
| set_tuer (ER_FCE); | |
| break; | |
| } /* end case */ | |
| tucs1 = (tucs1 & ~CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); | |
| tuba = ba & 0177777; /* update mem addr */ | |
| tucs1 = tucs1 & ~CS1_GO; /* clear go */ | |
| if (fnc >= FNC_XFER) /* data xfer? */ | |
| update_tucs (CS1_DONE, drv); | |
| else update_tucs (CS1_SC, drv); /* no, set attn */ | |
| if (DEBUG_PRS (tu_dev)) | |
| fprintf (sim_deb, ">>TU%d DONE: fnc=%s, cs1=%06o, cs2=%06o, ba=%06o, wc=%06o, fc=%06o, fs=%06o, er=%06o, pos=%d\n", | |
| drv, tu_fname[fnc], tucs1, tucs2, tuba, tuwc, tufc, tufs, tuer, uptr->pos); | |
| return SCPE_OK; | |
| } | |
| /* Formatter error */ | |
| void set_tuer (int32 flag) | |
| { | |
| tuer = tuer | flag; | |
| tufs = tufs | FS_ATA; | |
| tucs1 = tucs1 | CS1_SC; | |
| return; | |
| } | |
| /* Controller status update | |
| Check for done transition | |
| Update drive status | |
| Update MTCS1 | |
| Update interrupt request | |
| */ | |
| void update_tucs (int32 flag, int32 drv) | |
| { | |
| int32 act = sim_activate_time (&tu_unit[drv]); | |
| if ((flag & ~tucs1) & CS1_DONE) /* DONE 0 to 1? */ | |
| tuiff = (tucs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ | |
| if (GET_FMTR (tucs2) == 0) { /* formatter present? */ | |
| tufs = (tufs & ~FS_DYN) | FS_FPR; | |
| if (tu_unit[drv].flags & UNIT_ATT) { | |
| tufs = tufs | FS_MOL | tu_unit[drv].USTAT; | |
| if (tu_unit[drv].UDENS == TC_1600) | |
| tufs = tufs | FS_PE; | |
| if (sim_tape_wrp (&tu_unit[drv])) | |
| tufs = tufs | FS_WRL; | |
| if (!act) { | |
| if (sim_tape_bot (&tu_unit[drv])) | |
| tufs = tufs | FS_BOT; | |
| if (sim_tape_eot (&tu_unit[drv])) | |
| tufs = tufs | FS_EOT; | |
| } | |
| } | |
| if (tuer) | |
| tufs = tufs | FS_ERR; | |
| } | |
| else tufs = 0; | |
| tucs1 = (tucs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ)) | CS1_DVA | flag; | |
| if (tucs2 & CS2_ERR) | |
| tucs1 = tucs1 | CS1_TRE | CS1_SC; | |
| else if (tucs1 & CS1_TRE) | |
| tucs1 = tucs1 | CS1_SC; | |
| if (tufs & FS_ATA) | |
| tucs1 = tucs1 | CS1_SC; | |
| if (tuiff || ((tucs1 & CS1_SC) && (tucs1 & CS1_DONE) && (tucs1 & CS1_IE))) | |
| int_req = int_req | INT_TU; | |
| else int_req = int_req & ~INT_TU; | |
| if ((tucs1 & CS1_DONE) && tufs && !act) | |
| tufs = tufs | FS_RDY; | |
| return; | |
| } | |
| /* Interrupt acknowledge */ | |
| int32 tu_inta (void) | |
| { | |
| tucs1 = tucs1 & ~CS1_IE; /* clear int enable */ | |
| tuiff = 0; /* clear CSTB INTR */ | |
| return VEC_TU; /* acknowledge */ | |
| } | |
| /* Map tape error status */ | |
| t_stat tu_map_err (UNIT *uptr, t_stat st, t_bool qdt) | |
| { | |
| switch (st) { | |
| case MTSE_FMT: /* illegal fmt */ | |
| case MTSE_UNATT: /* not attached */ | |
| set_tuer (ER_NXF); /* can't execute */ | |
| if (qdt) /* data xfr? set TRE */ | |
| tucs1 = tucs1 | CS1_TRE; | |
| case MTSE_OK: /* no error */ | |
| return SCPE_IERR; | |
| case MTSE_TMK: /* end of file */ | |
| tufs = tufs | FS_TMK; | |
| break; | |
| case MTSE_IOERR: /* IO error */ | |
| set_tuer (ER_VPE); /* flag error */ | |
| if (qdt) /* data xfr? set TRE */ | |
| tucs1 = tucs1 | CS1_TRE; | |
| if (tu_stopioe) | |
| return SCPE_IOERR; | |
| break; | |
| case MTSE_INVRL: /* invalid rec lnt */ | |
| set_tuer (ER_VPE); /* flag error */ | |
| if (qdt) /* data xfr? set TRE */ | |
| tucs1 = tucs1 | CS1_TRE; | |
| return SCPE_MTRLNT; | |
| case MTSE_RECE: /* record in error */ | |
| set_tuer (ER_CRC); /* set crc err */ | |
| if (qdt) /* data xfr? set TRE */ | |
| tucs1 = tucs1 | CS1_TRE; | |
| break; | |
| case MTSE_EOM: /* end of medium */ | |
| set_tuer (ER_OPI); /* incomplete */ | |
| if (qdt) /* data xfr? set TRE */ | |
| tucs1 = tucs1 | CS1_TRE; | |
| break; | |
| case MTSE_BOT: /* reverse into BOT */ | |
| break; | |
| case MTSE_WRP: /* write protect */ | |
| set_tuer (ER_NXF); /* can't execute */ | |
| if (qdt) /* data xfr? set TRE */ | |
| tucs1 = tucs1 | CS1_TRE; | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat tu_reset (DEVICE *dptr) | |
| { | |
| int32 u; | |
| UNIT *uptr; | |
| tucs1 = CS1_DVA | CS1_DONE; | |
| tucs2 = CS2_IR | CS2_OR; | |
| tuba = 0; | |
| tuwc = 0; | |
| tufc = 0; | |
| tuer = 0; | |
| tufs = FS_FPR | FS_RDY; | |
| if (sim_switches & SWMASK ('P')) /* powerup? clr TC */ | |
| tutc = 0; | |
| else tutc = tutc & ~TC_FCS; /* no, clr <fcs> */ | |
| tuiff = 0; /* clear CSTB INTR */ | |
| int_req = int_req & ~INT_TU; /* clear interrupt */ | |
| for (u = 0; u < TU_NUMDR; u++) { /* loop thru units */ | |
| uptr = tu_dev.units + u; | |
| sim_tape_reset (uptr); /* clear pos flag */ | |
| sim_cancel (uptr); /* cancel activity */ | |
| uptr->USTAT = 0; | |
| } | |
| if (xbuf == NULL) | |
| xbuf = (uint8 *) calloc (MT_MAXFR + 4, sizeof (uint8)); | |
| if (xbuf == NULL) | |
| return SCPE_MEM; | |
| return SCPE_OK; | |
| } | |
| /* Attach routine */ | |
| t_stat tu_attach (UNIT *uptr, char *cptr) | |
| { | |
| int32 drv = uptr - tu_dev.units; | |
| t_stat r; | |
| r = sim_tape_attach (uptr, cptr); | |
| if (r != SCPE_OK) | |
| return r; | |
| uptr->USTAT = 0; /* clear unit status */ | |
| uptr->UDENS = UD_UNK; /* unknown density */ | |
| tufs = tufs | FS_ATA | FS_SSC; /* set attention */ | |
| if ((GET_FMTR (tucs2) == 0) && (GET_DRV (tutc) == drv)) /* selected drive? */ | |
| tufs = tufs | FS_SAT; /* set slave attn */ | |
| update_tucs (CS1_SC, drv); /* update status */ | |
| return r; | |
| } | |
| /* Detach routine */ | |
| t_stat tu_detach (UNIT* uptr) | |
| { | |
| int32 drv = uptr - tu_dev.units; | |
| if (!(uptr->flags & UNIT_ATT)) /* attached? */ | |
| return SCPE_OK; | |
| if (sim_is_active (uptr)) { /* unit active? */ | |
| sim_cancel (uptr); /* cancel operation */ | |
| tuer = tuer | ER_UNS; /* set formatter error */ | |
| if ((uptr->USTAT & FS_REW) == 0) /* data transfer? */ | |
| tucs1 = tucs1 | CS1_DONE | CS1_TRE; /* set done, err */ | |
| } | |
| uptr->USTAT = 0; /* clear status flags */ | |
| tufs = tufs | FS_ATA | FS_SSC; /* set attention */ | |
| update_tucs (CS1_SC, drv); /* update status */ | |
| return sim_tape_detach (uptr); | |
| } | |
| /* Device bootstrap */ | |
| #define BOOT_START 0377000 /* start */ | |
| #define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) | |
| static const d10 boot_rom_dec[] = { | |
| 0515040000003, /* boot:hrlzi 1,3 ; uba # */ | |
| 0201000040001, /* movei 0,40001 ; vld,pg 1 */ | |
| 0713001000000+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ | |
| 0435040000000+(IOBA_TU & RMASK), /* iori 1,772440 ; rh addr */ | |
| 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ | |
| 0201000000040, /* movei 0,40 ; ctrl reset */ | |
| 0713001000010, /* wrio 0,10(1) ; ->MTFS */ | |
| 0201100000031, /* movei 2,31 ; space f */ | |
| 0265740377014, /* jsp 17,tpop ; skip ucode */ | |
| 0201100000071, /* movei 2,71 ; read f */ | |
| 0265740377014, /* jsp 17,tpop ; read boot */ | |
| 0254000001000, /* jrst 1000 ; start */ | |
| 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ | |
| 0713001000032, /* wrio 0,32(1) ; ->MTTC */ | |
| 0201000000011, /* movei 0,11 ; clr+go */ | |
| 0713001000000, /* wrio 0,0(1) ; ->MTCS1 */ | |
| 0201140176000, /* movei 3,176000 ; wd cnt */ | |
| 0201200004000, /* movei 4,4000 ; addr */ | |
| 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ | |
| 0201300000000, /* movei 6,0 ; fmtr */ | |
| 0713141000002, /* wrio 3,2(1) ; ->MTWC */ | |
| 0713201000004, /* wrio 4,4(1) ; ->MTBA */ | |
| 0713301000006, /* wrio 6,6(1) ; ->MTFC */ | |
| 0713301000010, /* wrio 6,10(1) ; ->MTFS */ | |
| 0713241000032, /* wrio 5,32(1) ; ->MTTC */ | |
| 0713101000000, /* wrio 2,0(1) ; ->MTCS1 */ | |
| 0712341000012, /* rdio 7,12(1) ; read FS */ | |
| 0606340000200, /* trnn 7,200 ; test rdy */ | |
| 0254000377032, /* jrst .-2 ; loop */ | |
| 0606340040000, /* trnn 7,40000 ; test err */ | |
| 0254017000000, /* jrst 0(17) ; return */ | |
| 0712341000014, /* rdio 7,14(1) ; read err */ | |
| 0302340001000, /* caie 7,1000 ; fce? */ | |
| 0254200377052, /* halt */ | |
| 0254017000000, /* jrst 0(17) ; return */ | |
| }; | |
| static const d10 boot_rom_its[] = { | |
| 0515040000003, /* boot:hrlzi 1,3 ; uba # - not used */ | |
| 0201000040001, /* movei 0,40001 ; vld,pg 1 */ | |
| 0714000000000+(IOBA_UBMAP+1 & RMASK), /* iowri 0,763001 ; set ubmap */ | |
| 0435040000000+(IOBA_TU & RMASK), /* iori 1,772440 ; rh addr */ | |
| 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ | |
| 0201000000040, /* movei 0,40 ; ctrl reset */ | |
| 0714001000010, /* iowri 0,10(1) ; ->MTFS */ | |
| 0201100000031, /* movei 2,31 ; space f */ | |
| 0265740377014, /* jsp 17,tpop ; skip ucode */ | |
| 0201100000071, /* movei 2,71 ; read f */ | |
| 0265740377014, /* jsp 17,tpop ; read boot */ | |
| 0254000001000, /* jrst 1000 ; start */ | |
| 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ | |
| 0714001000032, /* iowri 0,32(1) ; ->MTTC */ | |
| 0201000000011, /* movei 0,11 ; clr+go */ | |
| 0714001000000, /* iowri 0,0(1) ; ->MTCS1 */ | |
| 0201140176000, /* movei 3,176000 ; wd cnt */ | |
| 0201200004000, /* movei 4,4000 ; addr */ | |
| 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ | |
| 0201300000000, /* movei 6,0 ; fmtr */ | |
| 0714141000002, /* iowri 3,2(1) ; ->MTWC */ | |
| 0714201000004, /* iowri 4,4(1) ; ->MTBA */ | |
| 0714301000006, /* iowri 6,6(1) ; ->MTFC */ | |
| 0714301000010, /* iowri 6,10(1) ; ->MTFS */ | |
| 0714241000032, /* iowri 5,32(1) ; ->MTTC */ | |
| 0714101000000, /* iowri 2,0(1) ; ->MTCS1 */ | |
| 0710341000012, /* iordi 7,12(1) ; read FS */ | |
| 0606340000200, /* trnn 7,200 ; test rdy */ | |
| 0254000377032, /* jrst .-2 ; loop */ | |
| 0606340040000, /* trnn 7,40000 ; test err */ | |
| 0254017000000, /* jrst 0(17) ; return */ | |
| 0710341000014, /* iordi 7,14(1) ; read err */ | |
| 0302340001000, /* caie 7,1000 ; fce? */ | |
| 0254200377052, /* halt */ | |
| 0254017000000, /* jrst 0(17) ; return */ | |
| }; | |
| t_stat tu_boot (int32 unitno, DEVICE *dptr) | |
| { | |
| size_t i; | |
| extern a10 saved_PC; | |
| M[FE_UNIT] = 0; | |
| M[FE_MTFMT] = (unitno & TC_M_UNIT) | (TC_1600 << TC_V_DEN) | (TC_10C << TC_V_FMT); | |
| tu_unit[unitno].pos = 0; | |
| for (i = 0; i < BOOT_LEN; i++) | |
| M[BOOT_START + i] = Q_ITS? boot_rom_its[i]: boot_rom_dec[i]; | |
| saved_PC = BOOT_START; | |
| return SCPE_OK; | |
| } |