- Integration with updated sim_card API - Addition of MT (Mag Tape) device - Addition of DSK (Disk) device - Build time simulator test
665 lines
24 KiB
C
665 lines
24 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 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
|
|
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[] = {
|
|
{MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL, "Write ring in place"},
|
|
{MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, NULL, NULL, "No write ring in place"},
|
|
{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);
|
|
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";
|
|
}
|
|
|
|
|