home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Boot_Images / 2.11_on_rl02 / pdpsim.tz / pdpsim / pdp8_mt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-29  |  17.7 KB  |  560 lines

  1. /* PDP-8 magnetic tape simulator
  2.  
  3.    Copyright (c) 1995, 1996, Robert M Supnik, Digital Equipment Corporation
  4.    Commercial use prohibited
  5.  
  6.    1-Jan-96    RMS    Rewritten from TM8-E Maintenance Manual
  7.  
  8.    Magnetic tapes are represented as a series of variable 16b records
  9.    of the form:
  10.  
  11.     byte count
  12.     byte 0'byte 1
  13.     :
  14.     byte n-2'byte n-1
  15.  
  16.    If the byte count is odd, the record is padded with an extra byte
  17.    of junk.  File marks are represented by a byte count of 0.
  18. */
  19.  
  20. #include "pdp8_defs.h"
  21.  
  22. #define MT_NUMDR    8                /* #drives */
  23. #define UNIT_V_WLK    (UNIT_V_UF + 0)            /* write locked */
  24. #define UNIT_WLK    1 << UNIT_V_WLK
  25. #define USTAT        u3                /* unit status */
  26. #define UNUM        u4                /* unit number */
  27. #define SBSIZE        (1 << 12)            /* max space cmd */
  28. #define SBMASK        (SBSIZE - 1)
  29.  
  30. /* Command/unit - mt_cu */
  31.  
  32. #define CU_V_UNIT    9                /* unit */
  33. #define CU_M_UNIT    07
  34. #define CU_PARITY    00400                /* parity select */
  35. #define CU_IEE        00200                /* error int enable */
  36. #define CU_IED        00100                /* done int enable */
  37. #define CU_V_EMA    3                /* ext mem address */
  38. #define CU_M_EMA    07
  39. #define CU_EMA        (CU_M_EMA << CU_V_EMA)
  40. #define CU_DTY        00002                /* drive type */
  41. #define CU_UNPAK    00001                /* 6b vs 8b mode */
  42. #define GET_UNIT(x)    (((x) >> CU_V_UNIT) & CU_M_UNIT)
  43. #define GET_EMA(x)    (((x) & CU_EMA) << (12 - CU_V_EMA))
  44.  
  45. /* Function - mt_fn */
  46.  
  47. #define FN_V_FNC    9                /* function */
  48. #define FN_M_FNC    07
  49. #define  FN_UNLOAD     00
  50. #define  FN_REWIND     01
  51. #define  FN_READ     02
  52. #define  FN_CMPARE     03
  53. #define  FN_WRITE     04
  54. #define  FN_WREOF     05
  55. #define  FN_SPACEF     06
  56. #define  FN_SPACER     07
  57. #define FN_ERASE    00400                /* erase */
  58. #define FN_CRC        00200                /* read CRC */
  59. #define FN_GO        00100                /* go */
  60. #define FN_INC        00040                /* incr mode */
  61. #define FN_RMASK    07740                /* readable bits */
  62. #define GET_FNC(x)    (((x) >> FN_V_FNC) & FN_M_FNC)
  63.  
  64. /* Status - stored in mt_sta or (*) uptr -> USTAT */
  65.  
  66. #define STA_ERR        (04000 << 12)            /* error */
  67. #define STA_REW        (02000 << 12)            /* *rewinding */
  68. #define STA_BOT        (01000 << 12)            /* *start of tape */
  69. #define STA_REM        (00400 << 12)            /* *offline */
  70. #define STA_PAR        (00200 << 12)            /* parity error */
  71. #define STA_EOF        (00100 << 12)            /* *end of file */
  72. #define STA_RLE        (00040 << 12)            /* rec lnt error */
  73. #define STA_DLT        (00020 << 12)            /* data late */
  74. #define STA_EOT        (00010 << 12)            /* *end of tape */
  75. #define STA_WLK        (00004 << 12)            /* *write locked */
  76. #define STA_CPE        (00002 << 12)            /* compare error */
  77. #define STA_ILL        (00001 << 12)            /* illegal */
  78. #define STA_INC        00010                /* increment error */
  79. #define STA_LAT        00004                /* lateral par error */
  80. #define STA_CRC        00002                /* CRC error */
  81. #define STA_LON        00001                /* long par error */
  82.  
  83. #define STA_CLR        (FN_RMASK | 00020)        /* always clear */
  84. #define STA_DYN        (STA_REW | STA_BOT | STA_REM | STA_EOF | \
  85.              STA_EOT | STA_WLK)        /* kept in USTAT */
  86. #define STA_EFLGS    (STA_BOT | STA_PAR | STA_RLE | STA_DLT | \
  87.              STA_EOT | STA_CPE | STA_ILL | STA_EOF | STA_INC)
  88.                              /* set error */
  89. #define TUR(u)        (!sim_is_active (u))        /* tape unit ready */
  90.  
  91. extern unsigned short M[];
  92. extern int int_req, stop_inst;
  93. extern UNIT cpu_unit;
  94. int mt_cu = 0;                        /* command/unit */
  95. int mt_fn = 0;                        /* function */
  96. int mt_ca = 0;                        /* current address */
  97. int mt_wc = 0;                        /* word count */
  98. int mt_sta = 0;                        /* status register */
  99. int mt_db = 0;                        /* data buffer */
  100. int mt_done = 0;                    /* mag tape flag */
  101. int mt_time = 10;                    /* record latency */
  102. int mt_stopioe = 1;                    /* stop on error */
  103. int mt_svc (UNIT *uptr);
  104. int mt_reset (DEVICE *dptr);
  105. int mt_attach (UNIT *uptr, char *cptr);
  106. int mt_detach (UNIT *uptr);
  107. int mt_updcsta (UNIT *uptr);
  108. int mt_ixma (int xma);
  109. int mt_vlock (UNIT *uptr, int val);
  110. UNIT *mt_busy (void);
  111. void mt_set_done (void);
  112. extern int sim_activate (UNIT *uptr, int interval);
  113. extern int sim_cancel (UNIT *uptr);
  114. extern int sim_is_active (UNIT *uptr);
  115. extern int attach_unit (UNIT *uptr, char *cptr);
  116. extern int detach_unit (UNIT *uptr);
  117.  
  118. /* MT data structures
  119.  
  120.    mt_dev    MT device descriptor
  121.    mt_unit    MT unit list
  122.    mt_reg    MT register list
  123.    mt_mod    MT modifier list
  124. */
  125.  
  126. UNIT mt_unit[] = {
  127.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  128.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  129.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  130.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  131.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  132.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  133.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) },
  134.     { UDATA (&mt_svc, UNIT_ATTABLE, 0) }  };
  135.  
  136. REG mt_reg[] = {
  137.     { ORDATA (CMD, mt_cu, 12) },
  138.     { ORDATA (FNC, mt_fn, 12) },
  139.     { ORDATA (CA, mt_ca, 12) },
  140.     { ORDATA (WC, mt_wc, 12) },
  141.     { ORDATA (DB, mt_db, 12) },
  142.     { GRDATA (STA, mt_sta, 8, 12, 12) },
  143.     { ORDATA (STA2, mt_sta, 6) },
  144.     { FLDATA (DONE, mt_done, 0) },
  145.     { FLDATA (INT, int_req, INT_V_MT) },
  146.     { FLDATA (STOP_IOE, mt_stopioe, 0) },
  147.     { DRDATA (TIME, mt_time, 24), PV_LEFT },
  148.     { ORDATA (UST0, mt_unit[0].USTAT, 24) },
  149.     { ORDATA (UST1, mt_unit[1].USTAT, 24) },
  150.     { ORDATA (UST2, mt_unit[2].USTAT, 24) },
  151.     { ORDATA (UST3, mt_unit[3].USTAT, 24) },
  152.     { ORDATA (UST4, mt_unit[4].USTAT, 24) },
  153.     { ORDATA (UST5, mt_unit[5].USTAT, 24) },
  154.     { ORDATA (UST6, mt_unit[6].USTAT, 24) },
  155.     { ORDATA (UST7, mt_unit[7].USTAT, 24) },
  156.     { GRDATA (POS0, mt_unit[0].pos, 10, 31, 1), PV_LEFT + REG_RO },
  157.     { GRDATA (POS1, mt_unit[1].pos, 10, 31, 1), PV_LEFT + REG_RO },
  158.     { GRDATA (POS2, mt_unit[2].pos, 10, 31, 1), PV_LEFT + REG_RO },
  159.     { GRDATA (POS3, mt_unit[3].pos, 10, 31, 1), PV_LEFT + REG_RO },
  160.     { GRDATA (POS4, mt_unit[4].pos, 10, 31, 1), PV_LEFT + REG_RO },
  161.     { GRDATA (POS5, mt_unit[5].pos, 10, 31, 1), PV_LEFT + REG_RO },
  162.     { GRDATA (POS6, mt_unit[6].pos, 10, 31, 1), PV_LEFT + REG_RO },
  163.     { GRDATA (POS7, mt_unit[7].pos, 10, 31, 1), PV_LEFT + REG_RO },
  164.     { FLDATA (WLK0, mt_unit[0].flags, UNIT_V_WLK), REG_HRO },
  165.     { FLDATA (WLK1, mt_unit[1].flags, UNIT_V_WLK), REG_HRO },
  166.     { FLDATA (WLK2, mt_unit[2].flags, UNIT_V_WLK), REG_HRO },
  167.     { FLDATA (WLK3, mt_unit[3].flags, UNIT_V_WLK), REG_HRO },
  168.     { FLDATA (WLK4, mt_unit[4].flags, UNIT_V_WLK), REG_HRO },
  169.     { FLDATA (WLK5, mt_unit[5].flags, UNIT_V_WLK), REG_HRO },
  170.     { FLDATA (WLK6, mt_unit[6].flags, UNIT_V_WLK), REG_HRO },
  171.     { FLDATA (WLK7, mt_unit[7].flags, UNIT_V_WLK), REG_HRO },
  172.     { NULL }  };
  173.  
  174. MTAB mt_mod[] = {
  175.     { UNIT_WLK, 0, "write enabled", "ENABLED", &mt_vlock },
  176.     { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &mt_vlock }, 
  177.     { 0 }  };
  178.  
  179. DEVICE mt_dev = {
  180.     "MT", mt_unit, mt_reg, mt_mod,
  181.     MT_NUMDR, 10, 32, 1, 8, 16,
  182.     NULL, NULL, &mt_reset,
  183.     NULL, &mt_attach, &mt_detach };
  184.  
  185. /* IOT routines */
  186.  
  187. int mt70 (int pulse, int AC)
  188. {
  189. int f;
  190. UNIT *bu, *uptr;
  191.  
  192. uptr = mt_dev.units + GET_UNIT (mt_cu);            /* get unit */
  193. switch (pulse) {                    /* decode IR<9:11> */
  194. case 1:                            /* LWCR */
  195.     mt_wc = AC;                    /* load word count */
  196.     return 0;
  197. case 2:                            /* CWCR */
  198.     mt_wc = 0;                    /* clear word count */
  199.     return AC;
  200. case 3:                            /* LCAR */
  201.     mt_ca = AC;                    /* load mem address */
  202.     return 0;
  203. case 4:                            /* CCAR */
  204.     mt_ca = 0;                    /* clear mem address */
  205.     return AC;
  206. case 5:                            /* LCMR */
  207.     if (mt_busy ()) mt_sta = mt_sta | STA_ILL;    /* busy? illegal op */
  208.     mt_cu = AC;                    /* load command reg */
  209.     mt_updcsta (mt_dev.units + GET_UNIT (mt_cu));
  210.     return 0;
  211.  
  212. /* MT70, continued */
  213.  
  214. case 6:                            /* LFGR */
  215.     if (mt_busy ()) mt_sta = mt_sta | STA_ILL;    /* busy? illegal op */
  216.     mt_fn = AC;                    /* load function */
  217.     if ((mt_fn & FN_GO) == 0) {            /* go set? */
  218.         mt_updcsta (uptr);            /* update status */
  219.         return 0;  }
  220.     f = GET_FNC (mt_fn);                /* get function */
  221.     if (((uptr -> flags & UNIT_ATT) == 0) || !TUR (uptr) ||
  222.        (((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WLK))
  223.        || (((f == FN_SPACER) || (f == FN_REWIND)) && (uptr -> pos == 0))) {
  224.         mt_sta = mt_sta | STA_ILL;        /* illegal op error */
  225.         mt_set_done ();                /* set done */
  226.         mt_updcsta (uptr);            /* update status */
  227.         return 0;  }
  228.     uptr -> USTAT = uptr -> USTAT & STA_WLK;    /* clear status */
  229.     if (f == FN_UNLOAD) {                /* unload? */
  230.         detach_unit (uptr);            /* set offline */
  231.         uptr -> USTAT = STA_REW | STA_REM;    /* rewinding, off */
  232.         mt_set_done ();  }            /* set done */
  233.     else if (f == FN_REWIND) {            /* rewind */
  234.         uptr -> USTAT = uptr -> USTAT | STA_REW; /* rewinding */
  235.         mt_set_done ();  }            /* set done */
  236.     else mt_done = 0;                /* clear done */
  237.     mt_updcsta (uptr);                /* update status */
  238.     sim_activate (uptr, mt_time);            /* start io */
  239.     return 0;
  240. case 7:                            /* LDBR */
  241.     if (mt_busy ()) mt_sta = mt_sta | STA_ILL;    /* busy? illegal op */
  242.     mt_db = AC;                    /* load buffer */
  243.     mt_set_done ();                    /* set done */
  244.     mt_updcsta (uptr);                /* update status */
  245.     return 0;
  246. default:
  247.     return (stop_inst << IOT_V_REASON) + AC;  }    /* end switch */
  248. return AC;
  249. }
  250.  
  251. /* IOTs, continued */
  252.  
  253. int mt71 (int pulse, int AC)
  254. {
  255. UNIT *uptr;
  256.  
  257. uptr = mt_dev.units + GET_UNIT (mt_cu);
  258. switch (pulse) {                    /* decode IR<9:11> */
  259. case 1:                            /* RWCR */
  260.     return mt_wc;                    /* read word count */
  261. case 2:                            /* CLT */
  262.     mt_reset (&mt_dev);                /* reset everything */
  263.     return AC;
  264. case 3:                            /* RCAR */
  265.     return mt_ca;                    /* read mem address */
  266. case 4:                            /* RMSR */
  267.     return ((mt_updcsta (uptr) >> 12) & 07777);    /* read status */
  268. case 5:                            /* RCMR */
  269.     return mt_cu;                    /* read command */
  270. case 6:                            /* RFSR */
  271.     return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK))
  272.          & 07777);                /* read function */
  273. case 7:                            /* RDBR */
  274.     return mt_db;                    /* read data buffer */
  275. default:
  276.     return (stop_inst << IOT_V_REASON) + AC;  }    /* end switch */
  277. return AC;
  278. }
  279.  
  280. int mt72 (int pulse, int AC)
  281. {
  282. UNIT *uptr;
  283.  
  284. uptr = mt_dev.units + GET_UNIT (mt_cu);            /* get unit */
  285. switch (pulse) {                    /* decode IR<9:11> */
  286. case 1:                            /* SKEF */
  287.     return (mt_sta & STA_ERR)? IOT_SKP + AC: AC;
  288. case 2:                            /* SKCB */
  289.     return (!mt_busy ())? IOT_SKP + AC: AC;
  290. case 3:                            /* SKJD */
  291.     return mt_done? IOT_SKP + AC: AC;
  292. case 4:                            /* SKTR */
  293.     return (TUR (uptr))? IOT_SKP + AC: AC;
  294. case 5:                            /* CLF */
  295.     if (TUR (uptr)) mt_reset (&mt_dev);        /* if TUR, zap */
  296.     else {    mt_sta = 0;                /* clear status */
  297.         mt_done = 0;                /* clear done */
  298.         mt_updcsta (uptr);  }            /* update status */
  299.     return AC;
  300. default:
  301.     return (stop_inst << IOT_V_REASON) + AC;  }    /* end switch */
  302. return AC;
  303. }
  304.  
  305. /* Unit service
  306.  
  307.    If rewind done, reposition to start of tape, set status
  308.    else, do operation, set done, interrupt
  309. */
  310.  
  311. int mt_svc (UNIT *uptr)
  312. {
  313. int rval, c, f, i, p, u, err;
  314. int awc, wc, xma;
  315. unsigned short bc, sbuf[SBSIZE + 1];
  316. static const unsigned short bceof = 0;
  317.  
  318. u = uptr -> UNUM;                    /* get unit number */
  319. if (uptr -> USTAT & STA_REW) {                /* rewind? */
  320.     uptr -> pos = 0;                /* update position */
  321.     if (uptr -> flags & UNIT_ATT)            /* still on line? */
  322.         uptr -> USTAT = (uptr -> USTAT & STA_WLK) | STA_BOT;
  323.     else uptr -> USTAT = STA_REM;
  324.     if (u == GET_UNIT (mt_cu)) {            /* selected? */
  325.         mt_set_done ();                /* set done */
  326.         mt_updcsta (uptr);  }            /* update status */
  327.     return SCPE_OK;  }
  328.  
  329. if ((uptr -> flags & UNIT_ATT) == 0) {            /* if not attached */
  330.     uptr -> USTAT = STA_REM;            /* unit off line */
  331.     mt_sta = mt_sta | STA_ILL;            /* illegal operation */
  332.     mt_set_done ();                    /* set done */
  333.     mt_updcsta (uptr);                /* update status */
  334.     return IORETURN (mt_stopioe, SCPE_UNATT);  }
  335.  
  336. f = GET_FNC (mt_fn);                    /* get command */
  337. if (((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WLK)) {
  338.     mt_sta = mt_sta | STA_ILL;            /* illegal operation */
  339.     mt_set_done ();                    /* set done */
  340.     mt_updcsta (uptr);                /* update status */
  341.     return SCPE_OK;  }
  342.  
  343. err = 0;
  344. rval = SCPE_OK;
  345. xma = GET_EMA (mt_cu) + mt_ca;                /* get mem addr */
  346. wc = 010000 - mt_wc;                    /* get wc */
  347. switch (f) {                        /* case on function */
  348.  
  349. /* Unit service, continued */
  350.  
  351. case FN_READ:                        /* read */
  352. case FN_CMPARE:                        /* read/compare */
  353.     fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
  354.     fread (&bc, sizeof (short), 1, uptr -> fileref); /* read byte count */
  355.     if ((err = ferror (uptr -> fileref)) ||        /* error or eof? */
  356.         (feof (uptr -> fileref))) {
  357.         uptr -> USTAT = uptr -> USTAT | STA_EOT | STA_RLE;
  358.         break;  }
  359.     if (bc == 0) {                    /* tape mark? */
  360.         uptr -> USTAT = uptr -> USTAT | STA_EOF | STA_RLE;
  361.         uptr -> pos = uptr -> pos + sizeof (short);
  362.         break;  }
  363.     awc = (mt_cu & CU_UNPAK)? ((int) bc): ((int) bc + 1) >> 1;
  364.     if (awc != wc) mt_sta = mt_sta | STA_RLE;    /* wrong size? */
  365.     if (awc < wc) wc = awc;                /* use smaller */
  366.     i = fread (sbuf, sizeof (short), wc, uptr -> fileref);
  367.     for ( ; i < wc; i++) sbuf[i] = 0;        /* fill with 0's */
  368.     err = ferror (uptr -> fileref);
  369.     for (i = p = 0; i < wc; i++) {            /* copy buffer */
  370.         xma = mt_ixma (xma);            /* increment xma */
  371.         if (mt_cu & CU_UNPAK) {            /* unpacked? */
  372.             if (i & 1) c = sbuf[p++] & 0377;
  373.             else c = (sbuf[p] >> 8) & 0377;  }
  374.             else {    c = ((sbuf[p] >> 2) & 07700) | (sbuf[p] & 077);
  375.             p++;  }
  376.         if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c;
  377.         else if ((f == FN_CMPARE) && (M[xma] != c)) {
  378.             mt_sta = mt_sta | STA_CPE;
  379.             break;  }  }
  380.     mt_wc = (mt_wc + wc) & 07777;            /* update wc */
  381.     uptr -> pos = uptr -> pos + (((int) bc + 1) & ~1) + sizeof (short);
  382.     break;
  383. case FN_WRITE:                        /* write */
  384.     fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
  385.     sbuf[0] = bc = (mt_cu & CU_UNPAK)? wc: wc << 1;
  386.     for (i = p = 0; i < wc; i++) {            /* copy buf to tape */
  387.         xma = mt_ixma (xma);            /* incr mem addr */
  388.         if (mt_cu & CU_UNPAK) {            /* unpacked? */
  389.             if (i & 1) sbuf[p] = sbuf[p] | (M[xma] & 0377);
  390.             else sbuf[++p] = (M[xma] & 0377) << 8;  }
  391.         else sbuf[++p] = ((M[xma] & 07700) << 2) | (M[xma] & 077);  }
  392.     fwrite (sbuf, sizeof (short), p + 1, uptr -> fileref);
  393.     err = ferror (uptr -> fileref);
  394.     mt_wc = 0;
  395.     uptr -> pos = uptr -> pos + (((int) bc + 1) & ~1) + sizeof (short);
  396.     break;
  397. case FN_WREOF:
  398.     fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
  399.     fwrite (&bceof, sizeof (short), 1, uptr -> fileref); /* write eof */
  400.     err = ferror (uptr -> fileref);
  401.     uptr -> pos = uptr -> pos + sizeof (short);    /* update position */
  402.     break;
  403.  
  404. /* Unit service, continued */
  405.  
  406. case FN_SPACEF:                        /* space forward */
  407.     do {    mt_wc = (mt_wc + 1) & 07777;        /* incr wc */
  408.         fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
  409.         fread (&bc, sizeof (short), 1, uptr -> fileref); /* read bc */
  410.         if ((err = ferror (uptr -> fileref)) ||    /* error or eof? */
  411.              feof (uptr -> fileref)) {
  412.             uptr -> USTAT = uptr -> USTAT | STA_EOT;
  413.             break;  }
  414.         uptr -> pos = uptr -> pos + sizeof (short);
  415.         if (bc == 0) {                /* zero bc? */
  416.             uptr -> USTAT = uptr -> USTAT | STA_EOF;
  417.             break;  }
  418.         uptr -> pos = uptr -> pos + (((int) bc + 1) & ~1);  }
  419.     while (mt_wc != 0);
  420.     break;
  421. case FN_SPACER:                        /* space reverse */
  422.     for (i = 0; i < SBSIZE; i++) sbuf[i] = 0;    /* clear table */
  423.     for (i = 0, p = 0; p < uptr -> pos; ) {        /* build table */
  424.         fseek (uptr -> fileref, p, SEEK_SET);
  425.         fread (&sbuf[i], sizeof (short), 1, uptr -> fileref);
  426.         if ((err = ferror (uptr -> fileref)) ||
  427.             (feof (uptr -> fileref))) {
  428.             uptr -> pos = p;
  429.             break;  }
  430.         p = p + sizeof (short) + (((int) sbuf[i] + 1) & ~1);
  431.         i = (i + 1) & SBMASK;  }
  432.     if (uptr -> pos == 0) {                /* at BOT? */
  433.         uptr -> USTAT = uptr -> USTAT | STA_BOT;
  434.         break;  }
  435.     do {    mt_wc = (mt_wc + 1) & 07777;        /* incr wc */
  436.         i = (i - 1) & SBMASK;
  437.         uptr -> pos = uptr -> pos - sizeof (short) -
  438.             (((int) sbuf[i] + 1) & ~1);
  439.         if (uptr -> pos == 0)            /* start of tape? */
  440.             uptr -> USTAT = uptr -> USTAT | STA_BOT;
  441.         if (sbuf[i] == 0)            /* start of prv file? */
  442.             uptr -> USTAT = uptr -> USTAT | STA_EOF;
  443.         if (uptr -> USTAT & (STA_BOT | STA_EOF)) break;  }
  444.     while (mt_wc != 0);
  445.     break;  }                    /* end case */
  446.  
  447. /* Unit service, continued */
  448.  
  449. if (err != 0) {                        /* I/O error */
  450.     mt_sta = mt_sta | STA_PAR | STA_CRC;        /* flag error */
  451.     perror ("MT I/O error");
  452.     rval = SCPE_IOERR;
  453.     clearerr (uptr -> fileref);  }
  454. mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA);
  455. mt_ca = xma & 07777;                    /* update mem addr */
  456. mt_set_done ();                        /* set done */
  457. mt_updcsta (uptr);                    /* update status */
  458. return IORETURN (mt_stopioe, rval);
  459. }
  460.  
  461. /* Update controller status */
  462.  
  463. int mt_updcsta (UNIT *uptr)
  464. {
  465. mt_sta = (mt_sta & ~(STA_DYN | STA_ERR | STA_CLR)) | (uptr -> USTAT & STA_DYN);
  466. if (mt_sta & STA_EFLGS) mt_sta = mt_sta | STA_ERR;
  467. if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) ||
  468.      (mt_done && (mt_cu & CU_IED))) int_req = int_req | INT_MT;
  469. else int_req = int_req & ~INT_MT;
  470. return mt_sta;
  471. }
  472.  
  473. /* Test if controller busy */
  474.  
  475. UNIT *mt_busy (void)
  476. {
  477. int u;
  478. UNIT *uptr;
  479.  
  480. for (u = 0; u < MT_NUMDR; u++) {            /* loop thru units */
  481.     uptr = mt_dev.units + u;
  482.     if (sim_is_active (uptr) && ((uptr -> USTAT & STA_REW) == 0))
  483.         return uptr;  }
  484. return NULL;
  485. }
  486.  
  487. /* Increment extended memory address */
  488.  
  489. int mt_ixma (int xma)                    /* incr extended ma */
  490. {
  491. int v;
  492.  
  493. v = ((xma + 1) & 07777) | (xma & 070000);        /* wrapped incr */
  494. if (mt_fn & FN_INC) {                    /* increment mode? */
  495.     if (xma == 077777) mt_sta = mt_sta | STA_INC;    /* at limit? error */
  496.     else v = xma + 1;  }                /* else 15b incr */
  497. return v;
  498. }
  499.  
  500. /* Set done */
  501.  
  502. void mt_set_done (void)
  503. {
  504. mt_done = 1;                        /* set done */
  505. mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC);        /* clear func<4:6> */
  506. return;
  507. }
  508.  
  509. /* Reset routine */
  510.  
  511. int mt_reset (DEVICE *dptr)
  512. {
  513. int i, u;
  514. UNIT *uptr;
  515.  
  516. mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0;
  517. int_req = int_req & ~INT_MT;                /* clear interrupt */
  518. for (u = 0; u < MT_NUMDR; u++) {            /* loop thru units */
  519.     uptr = mt_dev.units + u;
  520.     uptr -> UNUM = u;                /* init drive number */
  521.     sim_cancel (uptr);                /* cancel activity */
  522.     if (uptr -> flags & UNIT_ATT) uptr -> USTAT =
  523.         ((uptr -> pos)? 0: STA_BOT) |
  524.         ((uptr -> flags & UNIT_WLK)? STA_WLK: 0);
  525.     else uptr -> USTAT = STA_REM;  }
  526. return SCPE_OK;
  527. }
  528.  
  529. /* Attach routine */
  530.  
  531. int mt_attach (UNIT *uptr, char *cptr)
  532. {
  533. int r;
  534.  
  535. r = attach_unit (uptr, cptr);
  536. if (r != SCPE_OK) return r;
  537. uptr -> USTAT = STA_BOT | ((uptr -> flags & UNIT_WLK)? STA_WLK: 0);
  538. if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr);
  539. return r;
  540. }
  541.  
  542. /* Detach routine */
  543.  
  544. int mt_detach (UNIT* uptr)
  545. {
  546. if (!sim_is_active (uptr)) uptr -> USTAT = STA_REM;
  547. if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr);
  548. return detach_unit (uptr);
  549. }
  550.  
  551. /* Write lock/enable routine */
  552.  
  553. int mt_vlock (UNIT *uptr, int val)
  554. {
  555. if ((uptr -> flags & UNIT_ATT) && val) uptr -> USTAT = uptr -> USTAT | STA_WLK;
  556. else uptr -> USTAT = uptr -> USTAT & ~STA_WLK;
  557. if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr);
  558. return SCPE_OK;
  559. }
  560.