Historically this functionality was reimplemented within each DEVICE simulator often with slightly different implementations and inconsistencies. Solving this globally within SCP required changes in many places, but should henceforth be reasonably managed. As discussed in #1034
666 lines
25 KiB
C
666 lines
25 KiB
C
/* i650_mt.c: IBM 650 Magnetic tape
|
|
|
|
Copyright (c) 2018, Roberto Sancho
|
|
|
|
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 ni
|
|
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
|
|
ROBERTO SANCHO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Magnetic tapes are represented as a series of variable records
|
|
of the form:
|
|
|
|
32b byte count
|
|
byte 0
|
|
byte 1
|
|
:
|
|
byte n-2
|
|
byte n-1
|
|
32b byte count
|
|
|
|
If the byte count is odd, the record is padded with an extra byte
|
|
of junk. File marks are represented by a byte count of 0.
|
|
*/
|
|
|
|
#include "i650_defs.h"
|
|
#include "sim_tape.h"
|
|
|
|
#define UNIT_MT UNIT_ATTABLE | UNIT_ROABLE | UNIT_DISABLE
|
|
|
|
/* in u3 is tape medium length used on current position */
|
|
/* in u4 is tape medium max length (28800 for 2400 ft reel) */
|
|
/* in u5 holds the command being executed by tape unit */
|
|
#define MT_CMDMSK 0x00FF /* Command being run */
|
|
#define MT_RDY 0x0100 /* Unit is ready for command */
|
|
#define MT_IND 0x0200 /* Unit has Indicator light on */
|
|
|
|
/* u6 holds the current buffer position */
|
|
|
|
|
|
/* Definitions */
|
|
uint32 mt_cmd(UNIT *, uint16, uint16);
|
|
t_stat mt_srv(UNIT *);
|
|
void mt_ini(UNIT *, t_bool);
|
|
t_stat mt_reset(DEVICE *);
|
|
t_stat mt_attach(UNIT *, CONST char *);
|
|
t_stat mt_detach(UNIT *);
|
|
t_stat mt_rew(UNIT * uptr, int32 val, CONST char *cptr,void *desc);
|
|
t_stat mt_set_len (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat mt_show_len (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
const char *mt_description (DEVICE *dptr);
|
|
|
|
UNIT mt_unit[6] = {
|
|
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 0 */
|
|
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 1 */
|
|
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 2 */
|
|
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 3 */
|
|
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 4 */
|
|
{UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 5 */
|
|
};
|
|
|
|
MTAB mt_mod[] = {
|
|
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", &set_writelock, &show_writelock, NULL, "Write ring in place" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", &set_writelock, NULL, NULL, "no Write ring in place" },
|
|
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL,
|
|
"Set/Display tape format (SIMH, E11, TPC, P7B)"},
|
|
{MTAB_XTD | MTAB_VUN, 0, "LENGTH", "LENGTH", &mt_set_len, &mt_show_len, NULL,
|
|
"Set tape medium length (50 to 10000 foot)" },
|
|
{MTAB_XTD | MTAB_VUN, 0, NULL, "REWIND", &mt_rew, NULL, NULL, "Rewind tape"},
|
|
{0}
|
|
};
|
|
|
|
DEVICE mt_dev = {
|
|
"MT", mt_unit, NULL, mt_mod,
|
|
6, 8, 15, 1, 8, 8,
|
|
NULL, NULL, &mt_reset, NULL, &mt_attach, &mt_detach,
|
|
&mt_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
|
|
NULL, NULL, &mt_help, NULL, NULL, &mt_description
|
|
};
|
|
|
|
// IBM 652 Control Unit internal state
|
|
int LastTapeSelected = -1; // last tape selected. =0 to 5, -1=none yet
|
|
int LastTapeIndicator = 0; // last tape operation has some indication to tell to program/operator
|
|
int bFastMode = 0; // =1 for FAST operation
|
|
|
|
const char * TapeIndicatorStr[11] = { "OK",
|
|
"WRITE PROTECTED",
|
|
"IO CHECK",
|
|
"END OF FILE",
|
|
"END OF TAPE",
|
|
"LONG RECORD",
|
|
"SHORT RECORD",
|
|
"NO TAPE UNIT AT THIS ADDRESS",
|
|
"NO REEL LOADED",
|
|
"NOT READY",
|
|
"BAD CHAR"};
|
|
|
|
// return 1 if tape unit n (0..5) is ready to receive a command
|
|
int mt_ready(int n)
|
|
{
|
|
if ((n < 0) || (n > 5)) return 0;
|
|
if (mt_unit[n].u5 & MT_RDY) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Rewind tape drive */
|
|
t_stat mt_rew(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
/* If drive is offline or not attached return not ready */
|
|
if ((uptr->flags & UNIT_ATT) == 0)
|
|
return SCPE_NOATT;
|
|
uptr->u3 = 0; // tape at begin of medium
|
|
uptr->u5 = MT_RDY; // clear indicator flag, clear last command, set ready flag
|
|
return sim_tape_rewind(uptr);
|
|
}
|
|
|
|
int mt_read_numeric_word(uint8 * buf, t_int64 * d, int * ZeroNeg)
|
|
{
|
|
int i, neg;
|
|
char c;
|
|
|
|
neg = 0;
|
|
*d = 0;
|
|
if (ZeroNeg != NULL) *ZeroNeg = 0;
|
|
for (i=0;i<10;i++) {
|
|
c = *buf++;
|
|
if (i==9) { // is last word digit
|
|
if ((c >= '0') && (c <= '9')) return MT_IND_BADCHAR; // last digit should have sign
|
|
if (c == '?') c = '0'; // +0
|
|
if ((c >= 'A') && (c <= 'I')) c = c - 'A' + '1'; // +1 to +9
|
|
if ((c >= 'J') && (c <= 'R')) {c = c - 'J' + '1'; neg=1;} // -1 to -9
|
|
if (c == '!') {c = '0'; neg=1;} // -0
|
|
}
|
|
if ((c < '0') || (c > '9')) return MT_IND_BADCHAR;
|
|
*d = *d * 10 + (c - '0');
|
|
}
|
|
if (neg) *d = -*d;
|
|
if (ZeroNeg != NULL) *ZeroNeg = ((neg) && (*d == 0)) ? 1:0;
|
|
return 0;
|
|
}
|
|
|
|
int mt_read_alpha_word(uint8 * buf, t_int64 * d)
|
|
{
|
|
int i, n;
|
|
char c;
|
|
|
|
*d = 0;
|
|
for (i=0;i<5;i++) {
|
|
c = *buf++;
|
|
n = ascii_to_NN(c);
|
|
if ((n==0) && (c != ' ')) return MT_IND_BADCHAR;
|
|
*d = *d * 100 + n;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mt_transfer_tape_rec_to_IAS(uint8 * buf, t_mtrlnt reclen, char mode)
|
|
{
|
|
int n,ic,r, ZeroNeg;
|
|
t_int64 d, CtrlWord;
|
|
char s[6];
|
|
t_mtrlnt expected_reclen;
|
|
|
|
if (mode == 'N') {
|
|
// numeric mode
|
|
expected_reclen = (60 - IAS_TimingRing) * 10; // record len expected
|
|
// does expected record len match read record from tape?
|
|
if (expected_reclen != reclen) {
|
|
return (reclen > expected_reclen) ? MT_IND_LONG_REC : MT_IND_SHORT_REC;
|
|
}
|
|
// yes, record length match -> load IAS with tape record data
|
|
ic = 0;
|
|
while (1) {
|
|
// read numeric word from tape
|
|
r = mt_read_numeric_word(&buf[ic], &d, &ZeroNeg);
|
|
if (r) return r;
|
|
ic += 10;
|
|
// store into IAS
|
|
IAS[IAS_TimingRing] = d;
|
|
IAS_NegativeZeroFlag[IAS_TimingRing] = ZeroNeg;
|
|
sim_debug(DEBUG_DETAIL, &cpu_dev, "... Tape to IAS %04d: %06d%04d%c '%s'\n",
|
|
IAS_TimingRing+9000, printfw(d,ZeroNeg),
|
|
word_to_ascii(s, 1, 5, d));
|
|
// incr IAS_TimingRing, exit if arrived to end of IAS
|
|
IAS_TimingRing = (IAS_TimingRing + 1) % 60;
|
|
if (IAS_TimingRing == 0) break;
|
|
}
|
|
return 0;
|
|
}
|
|
// alphabetic mode
|
|
// check tape record size limits
|
|
if (reclen < 10 + 9*5) return MT_IND_SHORT_REC;
|
|
if (reclen > 10 + 9*10) return MT_IND_LONG_REC;
|
|
ic = 0;
|
|
while(1) {
|
|
// get control word
|
|
if (ic + 10 > (int)reclen) return MT_IND_SHORT_REC;
|
|
r = mt_read_numeric_word(&buf[ic], &CtrlWord, NULL);
|
|
if (r) return r;
|
|
ic += 10;
|
|
// store it in IAS[nnn9]
|
|
n = (IAS_TimingRing / 10) * 10 + 9;
|
|
IAS[n] = CtrlWord;
|
|
IAS_NegativeZeroFlag[n] = 0;
|
|
// load rest of words
|
|
for (n=0;n<9;n++) {
|
|
if ((CtrlWord % 10) != 8) {
|
|
// read a numeric word form tape
|
|
if (ic + 10 > (int)reclen) return MT_IND_SHORT_REC;
|
|
r = mt_read_numeric_word(&buf[ic], &d, &ZeroNeg);
|
|
if (r) return r;
|
|
ic += 10;
|
|
} else {
|
|
// read alphanumeric word from tape
|
|
if (ic + 5 > (int)reclen) return MT_IND_SHORT_REC;
|
|
r = mt_read_alpha_word(&buf[ic], &d); ZeroNeg=0;
|
|
if (r) return r;
|
|
ic += 5;
|
|
}
|
|
CtrlWord = CtrlWord / 10;
|
|
// store into IAS
|
|
IAS[IAS_TimingRing] = d;
|
|
IAS_NegativeZeroFlag[IAS_TimingRing] = ZeroNeg;
|
|
sim_debug(DEBUG_DETAIL, &cpu_dev, "... Tape to IAS %04d: %06d%04d%c '%s'\n",
|
|
IAS_TimingRing+9000, printfw(d,ZeroNeg),
|
|
word_to_ascii(s, 1, 5, d));
|
|
// incr IAS_TimingRing, exit if arrived to end of IAS
|
|
IAS_TimingRing = (IAS_TimingRing + 1) % 60;
|
|
if (IAS_TimingRing == 0) return MT_IND_LONG_REC;
|
|
}
|
|
IAS_TimingRing = (IAS_TimingRing + 1) % 60; // skip control word
|
|
if ((IAS_TimingRing == 0) && (ic != reclen)) return MT_IND_LONG_REC;
|
|
if (ic == reclen) {
|
|
if (IAS_TimingRing != 0) return MT_IND_SHORT_REC;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void mt_write_numeric_word(uint8 * buf, t_int64 d, int ZeroNeg)
|
|
{
|
|
int i, neg;
|
|
char c;
|
|
|
|
neg = 0;
|
|
if (d < 0) {neg=1; d=-d;}
|
|
if (ZeroNeg) neg=1;
|
|
for (i=0;i<10;i++) {
|
|
c = Shift_Digits(&d,1) + '0';
|
|
if (i==9) {
|
|
if (neg==0) { // last digit has sign
|
|
if (c == '0') c = '?'; // +0
|
|
if ((c >= '1') && (c <= '9')) c = c - '1' + 'A'; // +1 to +9
|
|
} else {
|
|
if ((c >= '1') && (c <= '9')) {c = c - '1' + 'J';} // -1 to -9
|
|
if (c == '0') {c = '!';} // -0
|
|
}
|
|
}
|
|
*buf++ = c;
|
|
}
|
|
}
|
|
|
|
void mt_write_alpha_word(uint8 * buf, t_int64 d)
|
|
{
|
|
int i, n;
|
|
char c;
|
|
|
|
for (i=0;i<5;i++) {
|
|
n = Shift_Digits(&d,2);
|
|
c = mem_to_ascii[n];
|
|
*buf++ = c;
|
|
}
|
|
}
|
|
|
|
void mt_transfer_IAS_to_tape_rec(uint8 * buf, t_mtrlnt * reclen, char mode)
|
|
{
|
|
int n,ic,ZeroNeg;
|
|
t_int64 d, CtrlWord;
|
|
char s[6];
|
|
|
|
if (mode == 'N') {
|
|
// numeric mode
|
|
ic = 0;
|
|
while (1) {
|
|
// read IAS
|
|
d = IAS[IAS_TimingRing];
|
|
ZeroNeg = IAS_NegativeZeroFlag[IAS_TimingRing];
|
|
sim_debug(DEBUG_DETAIL, &cpu_dev, "... IAS %04d to Tape: %06d%04d%c '%s'\n",
|
|
IAS_TimingRing+9000, printfw(d,ZeroNeg),
|
|
word_to_ascii(s, 1, 5, d));
|
|
// write numeric to tape buf
|
|
mt_write_numeric_word(&buf[ic], d, ZeroNeg);
|
|
ic += 10;
|
|
// incr IAS_TimingRing, exit if arrived to end of IAS
|
|
IAS_TimingRing = (IAS_TimingRing + 1) % 60;
|
|
if (IAS_TimingRing == 0) break;
|
|
}
|
|
*reclen = (t_mtrlnt) ic;
|
|
return;
|
|
}
|
|
// alphabetic mode
|
|
ic = 0;
|
|
while(1) {
|
|
// get control word form IAS[nnn9]
|
|
n = (IAS_TimingRing / 10) * 10 + 9;
|
|
CtrlWord = IAS[n];
|
|
// write control word in tape buf
|
|
mt_write_numeric_word(&buf[ic], CtrlWord, 0);
|
|
ic += 10;
|
|
// write rest of words
|
|
for (n=0;n<9;n++) {
|
|
// read from IAS
|
|
d = IAS[IAS_TimingRing];
|
|
ZeroNeg = IAS_NegativeZeroFlag[IAS_TimingRing];
|
|
if ((CtrlWord % 10) != 8) {
|
|
// write a numeric word to tape buf
|
|
mt_write_numeric_word(&buf[ic], d, ZeroNeg);
|
|
ic += 10;
|
|
} else {
|
|
// write alphanumeric word to tape buf
|
|
mt_write_alpha_word(&buf[ic], d);
|
|
ic += 5;
|
|
}
|
|
CtrlWord = CtrlWord / 10;
|
|
// incr IAS_TimingRing, exit if arrived to end of IAS
|
|
IAS_TimingRing = (IAS_TimingRing + 1) % 60;
|
|
if (IAS_TimingRing == 0) break;
|
|
}
|
|
if (IAS_TimingRing == 0) break;
|
|
IAS_TimingRing = (IAS_TimingRing + 1) % 60; // skip control word
|
|
if (IAS_TimingRing == 0) break;
|
|
}
|
|
*reclen = (t_mtrlnt) ic;
|
|
}
|
|
|
|
/* Start off a mag tape command */
|
|
uint32 mt_cmd(UNIT * uptr, uint16 cmd, uint16 fast)
|
|
{
|
|
DEVICE *dptr = find_dev_from_unit(uptr);
|
|
int unit = uptr - &mt_unit[0];
|
|
int i, ic, time;
|
|
t_stat r;
|
|
uint8 buf[1024];
|
|
char cbuf[100];
|
|
t_mtrlnt reclen;
|
|
|
|
time = 0;
|
|
/* Make sure valid drive number */
|
|
if ((unit > 5) || (unit < 0)) return STOP_ADDR;
|
|
// init IBM 652 Control Unit internal registers
|
|
LastTapeSelected = unit;
|
|
LastTapeIndicator = 0;
|
|
bFastMode = fast;
|
|
/* If tape unit disabled return error */
|
|
if (uptr->flags & UNIT_DIS) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape %d: command %02d attempted on disabled tape\n", unit, cmd);
|
|
LastTapeIndicator = MT_IND_DIS;
|
|
// not stated in manual: what happends if command to non existant tape?
|
|
// option 1 -> cpu halt
|
|
// option 2 -> tape indictor flag set (used this)
|
|
return SCPE_OK;
|
|
}
|
|
/* If tape has no file attached return error */
|
|
if ((uptr->flags & UNIT_ATT) == 0) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape %d: command %02d attempted on tape without file attached\n", unit, cmd);
|
|
LastTapeIndicator = MT_IND_NOATT;
|
|
uptr->u5 |= MT_IND; // turn on tape indicator light to signal to operator the faulting tape
|
|
return SCPE_OK;
|
|
}
|
|
uptr->u5 &= ~(MT_CMDMSK | MT_RDY | MT_IND); // remove last command sent to tape, remove ready flag, remove tape indicator flag
|
|
uptr->u5 |= cmd; // set current command in execution
|
|
switch (cmd) {
|
|
case OP_RTC:
|
|
case OP_RTA:
|
|
case OP_RTN:
|
|
sim_debug(DEBUG_DATA, dptr, "Tape unit %d: init read\n", unit);
|
|
// actual simulated tape read
|
|
reclen = 0;
|
|
r = sim_tape_rdrecf(uptr, buf, &reclen, sizeof(buf));
|
|
// calc tape pos:
|
|
// each char uses 0,005 inches. at the end of record the IRG (inter gap record) uses 3/4 inchs (0.75)
|
|
// scaled x1000 to use integer values
|
|
uptr->u3 += (int32) ((reclen * 0.005 + 0.75) * 1000);
|
|
// process result conditions
|
|
if (r == MTSE_TMK) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape unit %d: tape mark sensed\n", unit);
|
|
LastTapeIndicator = MT_IND_EOF;
|
|
uptr->u5 |= MT_IND;
|
|
} else if ((r == MTSE_EOM) || (uptr->u3 > uptr->u4*1000)) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape unit %d: end of tape sensed\n", unit);
|
|
LastTapeIndicator = MT_IND_EOT;
|
|
uptr->u5 |= MT_IND;
|
|
} else if (r == MTSE_RECE) {
|
|
// record header contains error flag
|
|
sim_debug(DEBUG_EXP, dptr, "Tape unit %d: longitudinal or vertical check error\n", unit);
|
|
LastTapeIndicator = MT_IND_IOCHECK;
|
|
uptr->u5 |= MT_IND;
|
|
} else if (r != MTSE_OK) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape unit %d: read error %d\n", unit, r);
|
|
return STOP_IO;
|
|
}
|
|
// debug output: display buf as 50 chars per line
|
|
sim_debug(DEBUG_DETAIL, dptr, "Read record (%d chars) from tape:\n", (int) reclen);
|
|
ic = 0;
|
|
while (1) {
|
|
for (i=0;i<50;i++) {
|
|
cbuf[i] = 0;
|
|
if (ic == reclen) break;
|
|
cbuf[i] = buf[ic++];
|
|
}
|
|
sim_debug(DEBUG_DETAIL, dptr, "... '%s'\n", cbuf);
|
|
if (ic == reclen) break;
|
|
}
|
|
// calc wordcount time needed to finish tape operation
|
|
time = msec_to_wordtime(11 + reclen * 0.068);
|
|
// transfer read data to IAS
|
|
if ((cmd != OP_RTC) && (LastTapeIndicator == 0)) {
|
|
LastTapeIndicator = mt_transfer_tape_rec_to_IAS(buf, reclen, (cmd == OP_RTN) ? 'N':'A');
|
|
if (LastTapeIndicator) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape unit %d: decode error %s\n", unit, TapeIndicatorStr[LastTapeIndicator]);
|
|
uptr->u5 |= MT_IND;
|
|
}
|
|
}
|
|
break;
|
|
case OP_WTM:
|
|
case OP_WTA:
|
|
case OP_WTN:
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: init write\n", unit);
|
|
if (cmd == OP_WTM) {
|
|
r = sim_tape_wrtmk(uptr);
|
|
// calc tape pos:
|
|
uptr->u3 += (int32) ((1 * 0.005 + 0.75) * 1000); // Tape Mark is 1 word long
|
|
reclen=1;
|
|
sim_debug(DEBUG_DETAIL, dptr, "Write Tape Mark\n");
|
|
} else {
|
|
sim_debug(DEBUG_DETAIL, dptr, "IAS TimingRing is %d\n", IAS_TimingRing+9000);
|
|
mt_transfer_IAS_to_tape_rec(buf, &reclen, (cmd == OP_WTN) ? 'N':'A');
|
|
// actual simulated tape write
|
|
r = sim_tape_wrrecf(uptr, buf, reclen);
|
|
// calc tape pos:
|
|
uptr->u3 += (int32) ((reclen * 0.005 + 0.75) * 1000);
|
|
// debug output: display buf as 50 chars per line
|
|
sim_debug(DEBUG_DETAIL, dptr, "Write record (%d chars) to tape:\n", (int) reclen);
|
|
ic = 0;
|
|
while (1) {
|
|
for (i=0;i<50;i++) {
|
|
cbuf[i] = 0;
|
|
if (ic == reclen) break;
|
|
cbuf[i] = buf[ic++];
|
|
}
|
|
sim_debug(DEBUG_DETAIL, dptr, "... '%s'\n", cbuf);
|
|
if (ic == reclen) break;
|
|
}
|
|
sim_debug(DEBUG_DETAIL, dptr, " IAS TimingRing is %d\n", IAS_TimingRing+9000);
|
|
}
|
|
// process result conditions
|
|
if (r == MTSE_WRP) {
|
|
LastTapeIndicator = MT_IND_WRT_PROT;
|
|
uptr->u5 |= MT_IND;
|
|
} else if ((r == MTSE_EOM) || (uptr->u3 > uptr->u4*1000)) {
|
|
LastTapeIndicator = MT_IND_EOT;
|
|
uptr->u5 |= MT_IND;
|
|
} else if (r != MTSE_OK) {
|
|
sim_debug(DEBUG_EXP, dptr, "Tape unit %d: write error %d\n", unit, r);
|
|
return STOP_IO;
|
|
}
|
|
// calc wordcount time needed
|
|
time = msec_to_wordtime(11 + reclen * 0.068); // time to remove Tape Control interlock
|
|
break;
|
|
case OP_BST:
|
|
case OP_RWD:
|
|
/* Check if at load point, quick return if so */
|
|
if (sim_tape_bot(uptr)) {
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: at BOT\n", unit);
|
|
uptr->u5 |= MT_RDY;
|
|
uptr->u3 = 0;
|
|
return SCPE_OK;
|
|
}
|
|
if (cmd == OP_RWD) {
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: init rewind\n", unit);
|
|
sim_tape_rewind(uptr);
|
|
uptr->u3 = 0;
|
|
time = msec_to_wordtime(35); // 35 msec to remove Tape Control interlock
|
|
}
|
|
if (cmd == OP_BST) {
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: init backstep record\n", unit);
|
|
r = sim_tape_sprecr(uptr, &reclen);
|
|
if ((r != MTSE_OK) && (r != MTSE_TMK)) {
|
|
return r;
|
|
}
|
|
uptr->u3 -= (int32) ((reclen * 0.005 + 0.75) * 1000);
|
|
time = msec_to_wordtime(38.5 + reclen * 0.068); // time to remove Tape Control interlock
|
|
}
|
|
break;
|
|
default:
|
|
sim_debug(DEBUG_EXP, dptr, "Tape %d: unknown command %02d\n", unit, cmd);
|
|
// should never occurs. just to catch it if so.
|
|
}
|
|
if (bFastMode) time = 0;
|
|
sim_cancel(uptr);
|
|
sim_activate(uptr, time);
|
|
return SCPE_OK_INPROGRESS;
|
|
}
|
|
|
|
/* Handle processing of tape requests. */
|
|
t_stat mt_srv(UNIT * uptr)
|
|
{
|
|
DEVICE *dptr = find_dev_from_unit(uptr);
|
|
int unit = (uptr - dptr->units);
|
|
int cmd = uptr->u5 & MT_CMDMSK;
|
|
int time;
|
|
|
|
switch (cmd) {
|
|
case OP_RTC:
|
|
case OP_RTA:
|
|
case OP_RTN:
|
|
case OP_WTM:
|
|
case OP_WTA:
|
|
case OP_WTN:
|
|
if (InterLockCount[IL_Tape]) {
|
|
// remove Tape Control Interlock
|
|
InterLockCount[IL_Tape] = 0;
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: free TCI interlock\n", unit);
|
|
}
|
|
if (InterLockCount[IL_IAS]) {
|
|
// remove IAS Interlock
|
|
InterLockCount[IL_IAS] = 0;
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: free IAS interlock\n", unit);
|
|
}
|
|
// command finished
|
|
goto tape_done;
|
|
break;
|
|
case OP_BST:
|
|
case OP_RWD:
|
|
if (InterLockCount[IL_Tape]) {
|
|
// remove Tape Control Interlock
|
|
InterLockCount[IL_Tape] = 0;
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: free TCI interlock\n", unit);
|
|
// calculate end of backspace/rew time
|
|
if (cmd == OP_BST) {
|
|
time = msec_to_wordtime(38.5 + 22);
|
|
} else {
|
|
// max time to rew is 1.2 minutes.
|
|
// get a rought aprox on % medium used (not exacta as not taking into account Hi/low speed rew)
|
|
time = (int) ((uptr->u3 / (uptr->u4*1000.0))*1.2*60+1); // number of seconds
|
|
time = msec_to_wordtime(time * 1000); // number of word times
|
|
}
|
|
if (bFastMode) time = 0;
|
|
sim_cancel(uptr);
|
|
sim_activate(uptr, time);
|
|
} else {
|
|
// command finished
|
|
goto tape_done;
|
|
}
|
|
break;
|
|
default:
|
|
return SCPE_ARG; // should never occurs. just to catch it if so.
|
|
tape_done:
|
|
sim_debug(DEBUG_CMD, dptr, "Tape unit %d: ready\n", unit);
|
|
sim_debug(DEBUG_DETAIL, &cpu_dev, "... Tape %d done, used %4.2f%% of medium\n",
|
|
unit,
|
|
(uptr->u3 / (uptr->u4*1000.0))*100.0
|
|
);
|
|
// set unit ready to accept new commands
|
|
uptr->u5 |= MT_RDY;
|
|
break;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
void mt_ini(UNIT * uptr, t_bool f)
|
|
{
|
|
if (uptr->flags & UNIT_ATT) {
|
|
uptr->u5 = MT_RDY;
|
|
} else {
|
|
uptr->u5 = 0;
|
|
}
|
|
uptr->u3 = 0;
|
|
if (uptr->u4 == 0) uptr->u4 = 28800; // default 2400 ft reel; 1 foot = 12 inches; 2400 ft = 28800 inches
|
|
}
|
|
|
|
t_stat mt_reset(DEVICE * dptr)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 6; i++) {
|
|
mt_ini(&mt_unit[i], 0);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat mt_attach(UNIT * uptr, CONST char *file)
|
|
{
|
|
t_stat r;
|
|
|
|
if ((r = sim_tape_attach(uptr, file)) != SCPE_OK)
|
|
return r;
|
|
uptr->u3 = 0;
|
|
uptr->u5 = MT_RDY;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat mt_detach(UNIT * uptr)
|
|
{
|
|
uptr->u3 = 0;
|
|
uptr->u5 = 0;
|
|
sim_cancel(uptr); // cancel any pending command
|
|
return sim_tape_detach(uptr);
|
|
}
|
|
|
|
/* Set tape length */
|
|
|
|
t_stat mt_set_len (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
int len;
|
|
t_stat r;
|
|
|
|
if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG;
|
|
len = (int) get_uint (cptr, 10, 10000, &r);
|
|
if (r != SCPE_OK) return SCPE_ARG;
|
|
if (len < 50) return SCPE_ARG;
|
|
uptr->u4 = 28800 * len / 2400;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show tape length */
|
|
|
|
t_stat mt_show_len (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fprintf (st, "length %d foot", uptr->u4 * 2400 / 28800);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
t_stat
|
|
mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
fprintf (st, "%s\n\n", mt_description(dptr));
|
|
fprintf (st, "The magnetic tape assumes that all tapes are 7 track\n");
|
|
fprintf (st, "with valid parity. Tapes are assumed to be 200 characters per\n");
|
|
fprintf (st, "inch. \n\n");
|
|
sim_tape_attach_help (st, dptr, uptr, flag, cptr);
|
|
fprint_set_help(st, dptr);
|
|
fprint_show_help(st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
const char *
|
|
mt_description(DEVICE *dptr)
|
|
{
|
|
return "IBM 727 Magnetic tape unit";
|
|
}
|
|
|
|
|