1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-02-18 13:27:51 +00:00
Files
rcornwell.sims/SEL32/sel32_mt.c
AZBevier 1a2eefec9d SEL32: Add more UTX support for V6 & V9 processors.
SEL32: Fix ADFD, SUFD, MPFD, DVFD instructions display.
SEL32: Change console output to handle 8 bit parity for UTX.
SEL32: Correct RPSWT instruction for V9 processor.
SEL32: Reverse scan order of channel complete and interrupts.
SEL32: Move memory and map register r/w macros to sel32_defs.h.
2020-01-11 13:55:02 -07:00

1168 lines
53 KiB
C

/* sel32_mt.c: SEL-32 8051 Buffered Tape Processor
Copyright (c) 2018, James C. Bevier
Portions provided by Richard Cornwell and other SIMH contributers
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
JAMES C. BEVIER 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. EOT is
represented as 0xffffffff (-1) byte count.
*/
#include "sel32_defs.h"
#include "sim_tape.h"
extern t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
extern t_stat show_dev_addr(FILE *st, UNIT *uptr, int32 v, CONST void *desc);
extern void chan_end(uint16 chan, uint8 flags);
extern int chan_read_byte(uint16 chan, uint8 *data);
extern int chan_write_byte(uint16 chan, uint8 *data);
extern void set_devattn(uint16 addr, uint8 flags);
extern t_stat chan_boot(uint16 addr, DEVICE *dptr);
extern uint32 SPAD[]; /* cpu SPAD */
#ifdef NUM_DEVS_MT
#define BUFFSIZE (64 * 1024)
#define UNIT_MT UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE
#define DEV_BUF_NUM(x) (((x) & 07) << DEV_V_UF)
#define GET_DEV_BUF(x) (((x) >> DEV_V_UF) & 07)
#if 0
CMNDCODE EQU $-1B
/* IOCD cmd bits 0-7 OP */
DATAB X'23' 1 REW
DATAB X'02' 2 READ
DATAB X'01' 3 WRITE
DATAB X'93' 4 WEOF
DATAB X'FF' 5 XCHANP
DATAB X'43' 6 ADVR
DATAB X'63' 7 ADVF
DATAB X'53' 8 BKSR
DATAB X'73' 9 BKXF
DATAB X'01' A UPSPACE (REALLY A WRITE)
DATAB X'A3' B ERASE
SPACE 3
* TIMER TABLE VALUES
*
* ENTRIES CORRESPOND ONE-FOR-ONE WITH ENTRIES IN OTAB
* AND REPRESENT THE MAXIMUM NUMBER OF SECONDS WHICH THE
* CORRESPONDING FUNCTION WOULD REQUIRE TO COMPLETE ON A 25 IPS
* TAPE DRIVE ON WHICH WAS MOUNTED A 2400 ft REEL OF TAPE.
BOUND 1W
TIMETBL DATAH 0 OPEN
DATAH 2 REWIND
DATAH 128 READ ASSUME TRANSFER OF 128K
DATAH 128 WRITE ASSUME TRANSFER OF 128K
DATAH 2 WRITE END-OF-FILE
DATAH 0 EXECUTE CHANNEL PROGRAM
DATAH 2 ADVANCE RECORD
DATAH 1152 SPACE FORWARD TO END-OF-FILE
DATAH 2 BACKSPACE RECORD
DATAH 1152 SPACE BACKWARD TO END-OF-FILE
DATAH 2 UPSPACE
DATAH 2 ERASE
DATAH 0 EJECT
DATAH 0 CLOSE
DATAH 0 TERM
DATAH 0 TEST
TOENTS EQU $-TIMETBL/2 NUMBER OF ENTRIES IN TABLE
SPACE 2
*
* HANDLER OP CODE VECTOR TABLE
*
BOUND 1W
OTAB EQU $
ACH OPEN 0 OPEN
ACH RWND 1 RWND
ACH READ 2 READ
ACH WRITE 3 WRITE
ACH WEOF 4 WEOF
ACH XCHANP 5 EXECUTE CHANNEL PROGRAM
ACH ADVR 6 ADVR
ACH ADVF 7 ADVF
ACH BKSR 8 BKSR
ACH BKSF 9 BKSF
ACH UPSP A UPSP
ACH ERASE B ERASE
ACH EJCT C EJCT
ACH CLOSE D CLOSE
ACH TERM E TERM
ACH TEST F TEST
SPACE 3
#endif
#define CMD u3
/* BTP tape commands */
#define MT_INCH 0x00 /* Initialize channel command */
#define MT_WRITE 0x01 /* Write command */
#define MT_READ 0x02 /* Read command */
#define MT_NOP 0x03 /* Control command */
#define MT_SENSE 0x04 /* Sense command */
#define MT_RDBK 0x0c /* Read Backward */
#define MT_RDCMP 0x13 /* Read and compare command */
#define MT_REW 0x23 /* Rewind command */
#define MT_RUN 0x33 /* Rewind and unload */
#define MT_FSR 0x43 /* Advance record */
#define MT_BSR 0x53 /* Backspace record */
#define MT_FSF 0x63 /* Advance filemark */
#define MT_BSF 0x73 /* Backspace filemark */
#define MT_SETM 0x83 /* Set Mode command */
#define MT_WTM 0x93 /* Write Tape filemark */
#define MT_ERG 0xA3 /* Erase 3.5 of tape */
#define MT_MODEMSK 0xFF /* Mode Mask */
/* set mode bits for BTP (MT_SETM) */
#define MT_MODE_AUTO 0x80 /* =0 Perform auto error recovery on read */
#define MT_MODE_FORCE 0x80 /* =1 Read regardless if error recovery fails */
#define MT_MDEN_800 0x40 /* =0 select 800 BPI NRZI mode 9 track only */
#define MT_MDEN_1600 0x40 /* =1 select 1600 BPI PE mode 9 track only */
#define MT_MDEN_6250 0x02 /* =0 Use mode from bit one for NRZI/PE */
#define MT_MDEN_6250 0x02 /* =1 6250 BPI GCR mode 9 track only */
#define MT_MDEN_SCATGR 0x01 /* =1 HSTP scatter/gather mode */
#define MT_MDEN_MSK 0xc0 /* Density mask */
#define MT_CTL_MSK 0x38 /* Mask for control flags */
#define MT_CTL_NOP 0x00 /* Nop control mode */
#define MT_CTL_NRZI 0x08 /* 9 track 800 bpi mode */
#define MT_CTL_RST 0x10 /* Set density, odd, convert on, trans off */
#define MT_CTL_NOP2 0x18 /* 9 track 1600 NRZI mode */
/* in u3 is device command code and status */
#define MT_CMDMSK 0x00ff /* Command being run */
#define MT_READDONE 0x0400 /* Read finished, end channel */
#define MT_MARK 0x0800 /* Sensed tape mark in move command */
#define MT_ODD 0x1000 /* Odd parity */
#define MT_TRANS 0x2000 /* Translation turned on ignored 9 track */
#define MT_CONV 0x4000 /* Data converter on ignored 9 track */
#define MT_BUSY 0x8000 /* Flag to send a CUE */
#define POS u4
/* in u4 is current buffer position */
#define SNS u5
/* in u5 packs sense byte 0, 1, 2 and 3 */
/* Sense byte 0 */
#define SNS_CMDREJ 0x80000000 /* Command reject */
#define SNS_INTVENT 0x40000000 /* Unit intervention required */
#define SNS_SPARE1 0x20000000 /* Spare */
#define SNS_EQUCHK 0x10000000 /* Equipment check */
#define SNS_DATCHK 0x08000000 /* Data Check */
#define SNS_OVRRUN 0x04000000 /* Data overrun */
#define SNS_SPARE2 0x02000000 /* Spare */
#define SNS_LOOKER 0x01000000 /* lookahead error */
/* Sense byte 1 */
#define SNS_PEMODER 0x800000 /* PE tape mode error */
#define SNS_TPECHK 0x400000 /* Tape PE mode check */
#define SNS_FMRKDT 0x200000 /* File mark detected EOF */
#define SNS_CORERR 0x100000 /* Corrected Error */
#define SNS_HARDER 0x080000 /* Hard Error */
#define SNS_MRLDER 0x040000 /* Mode register load error */
#define SNS_DATAWR 0x020000 /* Data written */
#define SNS_SPARE3 0x010000 /* Spare */
/* Sense byte 2 mode bits */
#define SNS_MREG0 0x8000 /* Mode register bit 0 */
#define SNS_MREG1 0x4000 /* Mode register bit 1 */
#define SNS_MREG2 0x2000 /* Mode register bit 2 */
#define SNS_MREG3 0x1000 /* Mode register bit 3 */
#define SNS_MREG4 0x0800 /* Mode register bit 4 */
#define SNS_MREG5 0x0400 /* Mode register bit 5 */
#define SNS_MREG6 0x0200 /* Mode register bit 6 */
#define SNS_MREG7 0x0100 /* Mode register bit 7 */
/* Sense byte 3 */
#define SNS_RDY 0x80 /* Drive Ready */
#define SNS_ONLN 0x40 /* Drive Online */
#define SNS_WRP 0x20 /* Drive is file protected (write ring missing) */
#define SNS_NRZI 0x10 /* Drive is NRZI */
#define SNS_SPARE4 0x08 /* Spare */
#define SNS_LOAD 0x04 /* Drive is at load point */
#define SNS_EOT 0x02 /* Drive is at EOT */
#define SNS_SPARE5 0x01 /* Spare */
#define SNS_BYTE4 0x00 /* Hardware errors not supported */
#define SNS_BYTE5 0x00 /* Hardware errors not supported */
#define MT_CONV1 0x40
#define MT_CONV2 0x80
#define MT_CONV3 0xc0
/* u6 holds the packed characters and unpack counter */
#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF)
#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF
uint8 mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ;
t_stat mt_srv(UNIT *);
t_stat mt_boot(int32, DEVICE *);
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_boot(int32, DEVICE *);
t_stat mt_help(FILE *, DEVICE *, UNIT *, int32, const char *);
const char *mt_description(DEVICE *);
/* One buffer per channel */
uint8 mt_buffer[NUM_DEVS_MT][BUFFSIZE];
uint8 mt_busy[NUM_DEVS_MT];
/* Gould Buffered Tape Processor (BTP) - Model 8051 */
/* Integrated channel controller */
/* Class F MT BTP I/O device status responce in IOCD address pointer location */
/* |-------+-------+-------+-------+-------+-------+-------+-------| */
/* |0 0 0 0|0 0 0 0|0 0 1 1|1 1 1 1|1 1 1 1|2 2 2 2|2 2 2 2|2 2 3 3| */
/* |0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 3 1| */
/* | Cond |0 0 0 0| Address of status doubleword or zero | */
/* | Code | */
/* |-------+-------+-------+-------+-------+-------+-------+-------| */
/* */
/* Bits 0-3 - Condition codes */
/* 0000 - operation accepted will echo status not sent by the channel */
/* 0001 - channel busy */
/* 0010 - channel inop or undefined */
/* 0011 - subchannel busy */
/* 0100 - status stored */
/* 0101 - unsupported transaction */
/* 1000 - Operation accepted/queued, no echo status */
/* Status Doubleword */
/* Word 1 */
/* |-------+-------+-------+-------+-------+-------+-------+-------| */
/* |0 0 0 0|0 0 0 0|0 0 1 1|1 1 1 1|1 1 1 1|2 2 2 2|2 2 2 2|2 2 3 3| */
/* |0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 3 1| */
/* |Sub Address | 24 bit IOCD address | */
/* |-------+-------+-------+-------+-------+-------+-------+-------| */
/* Word 2 */
/* |-------+-------+-------+-------+-------+-------+-------+-------| */
/* |0 0 0 0|0 0 0 0|0 0 1 1|1 1 1 1|1 1 1 1|2 2 2 2|2 2 2 2|2 2 3 3| */
/* |0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 3 1| */
/* | 16 bit of status | Residual Byte Count | */
/* |-------+-------+-------+-------+-------+-------+-------+-------| */
/* Status Bits */
/* Bit 00 - ECHO Halt I/O and Stop I/O function */
/* Bit 01 - PCI Program Controlled Interrupt */
/* Bit 02 - IL Incorrect Length */
/* Bit 03 - CPC Channel Program Check */
/* Bit 04 - CDC Channel Data Check */
/* Bit 05 - CCC Channel Control Check */
/* Bit 06 - IC Interface Check */
/* Bit 07 - CHC Chaining Check */
/* Bit 08 - DB Device Busy */
/* Bit 09 - SM Status Modifier */
/* Bit 10 - CNTE Controller End */
/* Bit 11 - ATTN Attention */
/* Bit 12 - CE Channel End */
/* Bit 13 - DE Device End */
/* Bit 14 - UC Unit Check */
/* Bit 15 - UE Unit Exception */
/* 41 Word Main memory channel buffer provided by INCH command */
/* when software is initializing the channel */
/* Word 01 - Status Doubleword 1 - Word 1 */
/* Word 02 - Status Doubleword 1 - Word 2 */
/* Word 03 - Status Doubleword 2 - Word 1 */
/* Word 04 - Status Doubleword 2 - Word 2 */
/* Word 05 - BTP Error Recovery IOCD Address */
/* Word 06 - Queue Command List Doubleword - Word 1 */
/* Word 07 - Queue Command List Doubleword - Word 2 */
/* Word 08 - 16 bit Logical Q-pointer | 16 bit Physical Q-pointer */
/* Word 09 - 16 bit Active Retry Count | 16 bit Constant Retry Count */
/* Word 10 - Accumulated Write Count - Drive 0 */
/* Word 11 - Accumulated Read Count - Drive 0 */
/* Word 12 - Write Error Count - Drive 0 */
/* Word 13 - Read Error Count - Drive 0 */
/* Word 14 - Accumulated Write Count - Drive 1 */
/* Word 15 - Accumulated Read Count - Drive 1 */
/* Word 16 - Write Error Count - Drive 1 */
/* Word 17 - Read Error Count - Drive 1 */
/* Word 18 - Accumulated Write Count - Drive 2 */
/* Word 19 - Accumulated Read Count - Drive 2 */
/* Word 20 - Write Error Count - Drive 2 */
/* Word 21 - Read Error Count - Drive 2 */
/* Word 22 - Accumulated Write Count - Drive 3 */
/* Word 23 - Accumulated Read Count - Drive 3 */
/* Word 24 - Write Error Count - Drive 3 */
/* Word 25 - Read Error Count - Drive 3 */
/* Word 26 - Accumulated Write Count - Drive 4 */
/* Word 27 - Accumulated Read Count - Drive 4 */
/* Word 28 - Write Error Count - Drive 4 */
/* Word 29 - Read Error Count - Drive 4 */
/* Word 30 - Accumulated Write Count - Drive 5 */
/* Word 31 - Accumulated Read Count - Drive 5 */
/* Word 32 - Write Error Count - Drive 5 */
/* Word 33 - Read Error Count - Drive 5 */
/* Word 34 - Accumulated Write Count - Drive 6 */
/* Word 35 - Accumulated Read Count - Drive 6 */
/* Word 36 - Write Error Count - Drive 6 */
/* Word 37 - Read Error Count - Drive 6 */
/* Word 38 - Accumulated Write Count - Drive 7 */
/* Word 39 - Accumulated Read Count - Drive 7 */
/* Word 40 - Write Error Count - Drive 7 */
/* Word 41 - Read Error Count - Drive 7 */
int32 valid_dens = MT_800_VALID|MT_1600_VALID|MT_6250_VALID;
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|MTAB_VALR, 0, "DENSITY", "DENSITY",
&sim_tape_set_dens, &sim_tape_show_dens, &valid_dens,
"Set tape density"},
{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 | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr,
&show_dev_addr, NULL, "Device address"},
{0}
};
UNIT mta_unit[] = {
/* Unit data layout for MT devices */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1000)}, /* 0 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1001)}, /* 1 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1002)}, /* 2 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1003)}, /* 3 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1004)}, /* 4 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1005)}, /* 5 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1006)}, /* 6 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1007)}, /* 7 */
};
/* channel program information */
CHANP mta_chp[NUM_UNITS_MT] = {0};
DIB mta_dib = {
NULL, /* Pre Start I/O */
mt_startcmd, /* Start a command */
NULL, /* Stop I/O */
NULL, /* Test I/O */
NULL, /* Post I/O */
mt_ini, /* init function */
mta_unit, /* Pointer to units structure */
mta_chp, /* Pointer to chan_prg structure */
NUM_UNITS_MT, /* number of units defined */
0xFF, /* 256 devices - device mask */
0x1000, /* parent channel address */
0, /* fifo input index */
0, /* fifo output index */
{0} /* interrupt status fifo for channel */
};
DEVICE mta_dev = {
"MTA", mta_unit, NULL, mt_mod,
NUM_UNITS_MT, 16, 24, 4, 16, 32,
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
&mta_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug,
NULL, NULL, &mt_help, NULL, NULL, &mt_description
};
#if NUM_DEVS_MT > 1
/* channel program information */
CHANP mtb_chp[NUM_UNITS_MT] = {0};
UNIT mtb_unit[] = {
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1800)}, /* 0 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1801)}, /* 1 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1802)}, /* 2 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1803)}, /* 3 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1804)}, /* 4 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1805)}, /* 5 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1806)}, /* 6 */
{UDATA(&mt_srv, UNIT_MT|UNIT_IDLE, 0), 0, UNIT_ADDR(0x1807)}, /* 7 */
};
/* device information block */
DIB mtb_dib = {
NULL, /* Pre Start I/O */
mt_startcmd, /* Start a command */
NULL, /* Stop I/O */
NULL, /* Test I/O */
NULL, /* Post I/O */
mt_ini, /* init function */
mtb_unit, /* Pointer to units structure */
mtb_chp, /* Pointer to chan_prg structure */
NUM_UNITS_MT, /* number of units defined */
0xFF, /* 256 devices - device mask */
0x1000, /* parent channel address */
0, /* fifo input index */
0, /* fifo output index */
0, /* interrupt status fifo for channel */
};
DEVICE mtb_dev = {
"MTB", mtb_unit, NULL, mt_mod,
NUM_UNITS_MT, 8, 15, 1, 8, 8,
NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach,
&mtb_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug
NULL, NULL, &mt_help, NULL, NULL, &mt_description
};
#endif
/* start an I/O operation */
uint8 mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd)
{
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
sim_debug(DEBUG_EXP, &mta_dev, "mt_startcmd entry chan %04x cmd %02x\n", chan, cmd);
if (mt_busy[GET_DEV_BUF(dptr->flags)] != 0 || (uptr->CMD & MT_CMDMSK) != 0) {
sim_debug(DEBUG_EXP, &mta_dev, "mt_startcmd busy chan %04x cmd %02x\n", chan, cmd);
uptr->flags |= MT_BUSY; /* Flag we need to send CUE */
return SNS_BSY;
}
sim_debug(DEBUG_EXP, &mta_dev, "mt_startcmd processing unit %04x cmd %02x\n", unit, cmd);
switch (cmd & 0xFF) {
case 0x00: /* INCH command */
/* POS has INCH buffer address and us9 the count */
/* just return OK and channel software will use POS as status buffer */
sim_debug(DEBUG_EXP, &mta_dev, "mt_startcmd INCH done unit %04x cmd %02x\n",
unit, cmd);
/* UTX_needs_interrupt */
cmd = MT_CMDMSK; /* insert INCH cmd as 0xff */
/* fall through */
case 0x03: /* Tape motion commands or NOP */
case 0x13: /* Read and compare command */
case 0x23: /* Rewind command */
case 0x33: /* Rewind and unload */
case 0x43: /* Advance record */
case 0x53: /* Backspace record */
case 0x63: /* Advance filemark */
case 0x73: /* Backspace filemark */
case 0x83: /* Set Mode command */
case 0x93: /* Write Tape filemark */
case 0xA3: /* Erase 3.5 of tape */
/* UTX_needs_interrupt on NOP or INCH */
/* fall through */
case 0x01: /* Write command */
case 0x02: /* Read command */
case 0x0C: /* Read backward */
if (cmd != 0x03) /* if this is a nop do not zero status */
uptr->SNS = (uptr->SNS & 0x0000ff00); /* clear all but byte 2 */
uptr->SNS |= (SNS_RDY|SNS_ONLN); /* set ready status */
/* Fall through */
// case 0x4: /* Sense */
if (sim_tape_wrp(uptr))
uptr->SNS |= (SNS_WRP); /* write protected */
if (sim_tape_bot(uptr))
uptr->SNS |= (SNS_LOAD); /* tape at load point */
if (sim_tape_eot(uptr))
uptr->SNS |= (SNS_EOT); /* tape at EOM */
/* Fall through */
case 0x4: /* Sense */
#ifndef FIX_DIAG
case 0x80: /* Unknown diag cmd with byte cnt of 0x0c */
#endif
uptr->CMD &= ~(MT_CMDMSK); /* clear out last cmd */
uptr->CMD |= cmd & MT_CMDMSK; /* insert new cmd */
CLR_BUF(uptr); /* buffer is empty */
/* INCH cmd has INCH buffer address in POS, so leave it */
if (cmd != MT_CMDMSK)
uptr->POS = 0; /* reset buffer position pointer */
sim_activate(uptr, 100); /* Start unit off */
mt_busy[GET_DEV_BUF(dptr->flags)] = 1; /* show we are busy */
sim_debug(DEBUG_EXP, &mta_dev, "mt_startcmd sense %08x return 0 chan %04x cmd %02x\n",
uptr->SNS, chan, cmd);
return 0;
default: /* invalid command */
sim_debug(DEBUG_EXP, &mta_dev, "mt_startcmd CMDREJ return chan %04x cmd %02x\n",
chan, cmd);
uptr->SNS |= SNS_CMDREJ;
break;
}
if (uptr->SNS & 0xff000000) /* errors? */
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
sim_debug(DEBUG_EXP, &mta_dev,
"mt_startcmd ret CHNEND|DEVEND chan %04x unit %04x cmd %02x\n", chan, unit, cmd);
return SNS_CHNEND|SNS_DEVEND;
}
/* Map simH errors into machine errors */
t_stat mt_error(UNIT *uptr, uint16 addr, t_stat r, DEVICE *dptr)
{
sim_debug(DEBUG_CMD, &mta_dev, "mt_error status %08x\n", r);
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; /* not busy anymore */
switch (r) { /* switch on return value */
case MTSE_OK: /* no error */
break;
case MTSE_TMK: /* tape mark */
sim_debug(DEBUG_CMD, &mta_dev, "FILE MARK\n");
uptr->SNS |= SNS_FMRKDT; /* file mark detected */
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
break;
case MTSE_WRP: /* write protected */
uptr->SNS |= SNS_WRP; /* write protected */
sim_debug(DEBUG_CMD, &mta_dev, "WRITE PROTECT %08x\n", r); /* operator intervention */
break;
case MTSE_UNATT: /* unattached */
uptr->SNS |= SNS_INTVENT; /* unit intervention required */
sim_debug(DEBUG_CMD, &mta_dev, "ATTENTION %08x\n", r); /* operator intervention */
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP);
break;
case MTSE_IOERR: /* IO error */
case MTSE_FMT: /* invalid format */
case MTSE_RECE: /* error in record */
sim_debug(DEBUG_CMD, &mta_dev, "ERROR %08x\n", r);
break;
case MTSE_BOT: /* beginning of tape */
uptr->SNS |= SNS_LOAD; /* tape at BOT */
sim_debug(DEBUG_CMD, &mta_dev, "BOT\n");
break;
case MTSE_INVRL: /* invalid rec lnt */
case MTSE_EOM: /* end of medium */
uptr->SNS |= SNS_EOT; /* tape at EOT */
sim_debug(DEBUG_CMD, &mta_dev, "EOT\n");
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP);
break;
}
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done with command */
return SCPE_OK;
}
/* Handle processing of tape requests. */
t_stat mt_srv(UNIT *uptr)
{
uint16 addr = GET_UADDR(uptr->CMD);
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units);
int cmd = uptr->CMD & MT_CMDMSK;
int bufnum = GET_DEV_BUF(dptr->flags);
t_mtrlnt reclen;
t_stat r = SCPE_ARG; /* Force error if not set */
uint8 ch;
uint8 zc = 0;
sim_debug(DEBUG_DETAIL, &mta_dev, "mt_srv unit %04x cmd %02x\n", unit, cmd);
if ((uptr->flags & UNIT_ATT) == 0) { /* unit attached status */
uptr->SNS |= SNS_INTVENT; /* unit intervention required */
mt_busy[bufnum] &= ~1; /* make our buffer not busy */
if (cmd != MT_SENSE) { /* we are completed with unit check status */
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
}
switch (cmd) {
case MT_CMDMSK: /* 0x0ff for inch 0x00 */ /* INCH is for channel, nothing for us */
/* uptr->POS has INCH buffer address, just leave it */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv cmd 0 INCH unit=%04x\n", unit);
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* make our buffer not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */
break;
#ifndef FIX_DIAG
case 0x80: /* other? */ /* default to NOP */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv cmd 80 DIAG unit=%04x SNS %08x\n", unit, uptr->SNS);
ch = (uptr->SNS >> 24) & 0xff; /* get sense byte 0 status */
sim_debug(DEBUG_CMD, &mta_dev, "sense unit %02x byte 0 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 0 */
ch = (uptr->SNS >> 16) & 0xff; /* get sense byte 1 status */
sim_debug(DEBUG_CMD, &mta_dev, "sense unit %02x byte 1 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 1 */
ch = (uptr->SNS >> 8) & 0xff; /* get sense byte 2 status */
sim_debug(DEBUG_CMD, &mta_dev, "sense unit %02x byte 2 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 2 */
ch = (uptr->SNS >> 0) & 0xff; /* get sense byte 3 status */
sim_debug(DEBUG_CMD, &mta_dev, "sense unit %02x byte 3 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 3 */
for (ch=4; ch < 0xc; ch++) {
// chan_write_byte(addr, &ch); /* write byte */
chan_write_byte(addr, &zc); /* write zero byte */
}
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* make our buffer not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv DIAG SNS %08x char complete unit=%02x\n",
uptr->SNS, unit);
break;
#endif
case MT_NOP: /* 0x03 */ /* NOP motion command */
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* make our buffer not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */
break;
case MT_SENSE: /* 0x04 */ /* get sense data */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv cmd 4 SENSE %08x unit=%04x\n", uptr->SNS, unit);
ch = (uptr->SNS >> 24) & 0xff; /* get sense byte 0 status */
sim_debug(DEBUG_DETAIL, &mta_dev, "sense unit %02x byte 0 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 0 */
ch = (uptr->SNS >> 16) & 0xff; /* get sense byte 1 status */
sim_debug(DEBUG_DETAIL, &mta_dev, "sense unit %02x byte 1 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 1 */
ch = (uptr->SNS >> 8) & 0xff; /* get sense byte 2 status */
sim_debug(DEBUG_DETAIL, &mta_dev, "sense unit %02x byte 2 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 2 */
ch = (uptr->SNS >> 0) & 0xff; /* get sense byte 3 status */
sim_debug(DEBUG_DETAIL, &mta_dev, "sense unit %02x byte 3 %02x\n", unit, ch);
chan_write_byte(addr, &ch); /* write byte 3 */
ch = 4;
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* make our buffer not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv SENSE %08x char complete unit=%02x\n",
uptr->SNS, unit);
break;
case MT_READ: /* 0x02 */ /* read a record from the device */
sim_debug(DEBUG_DATA, &mta_dev, "mt_srv cmd 2 READ unit=%02x\n", unit);
if (uptr->CMD & MT_READDONE) { /* is the read complete */
uptr->SNS &= ~(SNS_LOAD|SNS_EOT); /* reset BOT & EOT */
if (sim_tape_eot(uptr)) { /* see if at EOM */
uptr->SNS |= SNS_EOT; /* set EOT status */
}
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE); /* clear all but readdone & cmd */
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* not busy anymore */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* set chan end, dev end status */
sim_debug(DEBUG_CMD, &mta_dev,
"mt_srv READ %04x char complete unit=%02x sense %08x\n",
uptr->POS, unit, uptr->SNS);
break;
}
/* read is not completed, get an input char */
/* If empty buffer, fill */
if (BUF_EMPTY(uptr)) {
/* buffer is empty, so fill it with next record data */
if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen, BUFFSIZE)) != MTSE_OK) {
sim_debug(DEBUG_DETAIL, &mta_dev, "mt_srv READ fill buffer unit=%02x\n", unit);
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE); /* clear all but readdone & cmd */
return mt_error(uptr, addr, r, dptr); /* process any error & return status */
}
uptr->SNS &= ~(SNS_LOAD|SNS_EOT); /* reset BOT & EOT */
uptr->POS = 0; /* reset buffer position */
uptr->hwmark = reclen; /* set buffer chars read in */
sim_debug(DEBUG_DETAIL, &mta_dev, "mt_srv READ fill buffer complete count %04x\n", reclen);
}
/* get a char from the buffer */
ch = mt_buffer[bufnum][uptr->POS++];
/* Send character over to channel */
if (chan_write_byte(addr, &ch)) {
sim_debug(DEBUG_CMD, &mta_dev, "Read unit %02x EOR cnt %04x\n", unit, uptr->POS);
/* If not read whole record, skip till end */
if ((uint32)uptr->POS < uptr->hwmark) {
/* Send dummy character to force SLI */
chan_write_byte(addr, &ch); /* write the byte */
sim_debug(DEBUG_CMD, &mta_dev, "Read unit %02x send dump SLI\n", unit);
sim_activate(uptr, (uptr->hwmark-uptr->POS) * 10); /* wait again */
uptr->CMD |= MT_READDONE; /* read is done */
break;
}
sim_debug(DEBUG_CMD, &mta_dev,
"Read data @1 unit %02x cnt %04x ch %02x hwm %04x\n",
unit, uptr->POS, ch, uptr->hwmark);
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* set not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* return end status */
} else {
sim_debug(DEBUG_DATA, &mta_dev,
"Read data @2 unit %02x cnt %04x ch %02x hwm %04x\n", unit, uptr->POS, ch,
uptr->hwmark);
if ((uint32)uptr->POS >= uptr->hwmark) { /* In IRG */
/* Handle end of data record */
sim_debug(DEBUG_CMD, &mta_dev,
"Read data out of data unit %02x cnt %04x ch %02x hwm %04x\n",
unit, uptr->POS, ch, uptr->hwmark);
#ifdef UTX_EOF_CHANGE
uptr->CMD &= ~MT_CMDMSK; /* clear the cmd */
mt_busy[bufnum] &= ~1; /* set not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* return end status */
#else
uptr->CMD |= MT_READDONE; /* read is done */
sim_activate(uptr, 20); /* wait again */
#endif
} else
sim_activate(uptr, 20); /* wait again */
}
break;
case MT_SETM: /* 0x83 */ /* set mode byte */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv cmd 0x83 SETM unit=%02x\n", unit);
/* Grab data until channel has no more */
if (chan_read_byte(addr, &ch)) {
if (uptr->POS > 0) { /* Only if data in record */
reclen = uptr->hwmark; /* set record length */
ch = mt_buffer[bufnum][0]; /* get the first byte read */
sim_debug(DEBUG_CMD, &mta_dev,
"Write mode data done unit %02x chars %04x char %02x\n", unit, reclen, ch);
/* put mode bits into byte 2 of SNS */
uptr->SNS = (uptr->SNS & 0xffff00ff) | (ch << 8);
uptr->POS = 0; /* no bytes anymore */
uptr->CMD &= ~MT_CMDMSK; /* no cmd to do */
mt_busy[bufnum] &= ~1; /* set not busy */
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* return end status */
}
} else {
mt_buffer[bufnum][uptr->POS++] = ch; /* save the character read in */
sim_debug(DEBUG_CMD, &mta_dev, "Write mode data in unit %02x POS %04x ch %02x\n",
unit, uptr->POS, ch);
uptr->hwmark = uptr->POS; /* set high water mark */
sim_activate(uptr, 20); /* wait time */
}
break;
case MT_WRITE: /* 0x01 */ /* write record */
/* Check if write protected */
if (sim_tape_wrp(uptr)) {
uptr->SNS |= SNS_CMDREJ;
uptr->CMD &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
sim_debug(DEBUG_CMD, &mta_dev, "Write write protected unit=%02x\n", unit);
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
break;
}
/* Grab data until channel has no more */
if (chan_read_byte(addr, &ch)) {
if (uptr->POS > 0) { /* Only if data in record */
reclen = uptr->hwmark;
sim_debug(DEBUG_CMD, &mta_dev, "Write unit=%02x Block %04x chars\n",
unit, reclen);
r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen);
uptr->POS = 0;
uptr->CMD &= ~MT_CMDMSK;
mt_error(uptr, addr, r, dptr); /* Record errors */
}
} else {
mt_buffer[bufnum][uptr->POS++] = ch;
sim_debug(DEBUG_DATA, &mta_dev, "Write data unit=%02x %04x %02x\n",
unit, uptr->POS, ch);
uptr->hwmark = uptr->POS;
}
sim_activate(uptr, 20);
break;
case MT_RDBK: /* 0x0C */ /* Read Backwards */
if (uptr->CMD & MT_READDONE) {
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
return SCPE_OK;
}
/* If at end of record, fill buffer */
if (BUF_EMPTY(uptr)) {
if (sim_tape_bot(uptr)) {
uptr->CMD &= ~MT_CMDMSK;
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
sim_debug(DEBUG_CMD, &mta_dev, "Read backward unit=%02x\n", unit);
if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen, BUFFSIZE)) != MTSE_OK) {
uptr->CMD &= ~(MT_CMDMSK|MT_READDONE);
return mt_error(uptr, addr, r, dptr);
}
uptr->POS = reclen;
uptr->hwmark = reclen;
sim_debug(DEBUG_CMD, &mta_dev, "Binary Block %04x chars\n", reclen);
}
ch = mt_buffer[bufnum][--uptr->POS];
if (chan_write_byte(addr, &ch)) {
sim_debug(DEBUG_CMD, &mta_dev, "Read unit=%02x EOR cnt %04x\n",
unit, uptr->POS);
/* If not read whole record, skip till end */
if (uptr->POS >= 0) {
sim_activate(uptr, (uptr->POS) * 20);
uptr->CMD |= MT_READDONE;
return SCPE_OK;
}
uptr->CMD &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
} else {
sim_debug(DEBUG_DATA, &mta_dev, "Read data unit=%02x %04x %02x\n",
unit, uptr->POS, ch);
if (uptr->POS == 0) { /* In IRG */
uptr->CMD &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
} else
sim_activate(uptr, 20);
}
break;
case MT_WTM: /* 0x93 */ /* Write tape filemark */
if (uptr->POS == 0) {
if (sim_tape_wrp(uptr)) {
uptr->SNS |= SNS_CMDREJ;
uptr->CMD &= ~MT_CMDMSK;
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
uptr->POS ++;
sim_activate(uptr, 500);
} else {
sim_debug(DEBUG_CMD, &mta_dev, "Write Mark unit=%02x\n", unit);
uptr->CMD &= ~(MT_CMDMSK);
r = sim_tape_wrtmk(uptr);
chan_end(addr, SNS_DEVEND); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND);
mt_busy[bufnum] &= ~1;
}
break;
case MT_BSR: /* 0x53 */ /* Backspace record */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv cmd 0x53 BSR unit %02x POS %04x\n",
unit, uptr->POS);
switch (uptr->POS ) {
case 0:
if (sim_tape_bot(uptr)) {
uptr->CMD &= ~MT_CMDMSK;
mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
return SCPE_OK;
}
uptr->POS++;
sim_activate(uptr, 50);
break;
case 1:
uptr->POS++;
sim_debug(DEBUG_CMD, &mta_dev, "Backspace rec unit %02x POS %04x\n",
unit, uptr->POS);
r = sim_tape_sprecr(uptr, &reclen);
/* We don't set EOF on BSR */
if (r == MTSE_TMK) {
uptr->POS++;
sim_debug(DEBUG_CMD, &mta_dev, "MARK\n");
sim_activate(uptr, 50);
} else {
sim_debug(DEBUG_CMD, &mta_dev, "Backspace reclen %04x\n", reclen);
sim_activate(uptr, 50);
}
break;
case 2:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
case 3:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_DEVEND|SNS_UNITEXP);
break;
}
break;
case MT_BSF: /* 0x73 */ /* Backspace file */
sim_debug(DEBUG_CMD, &mta_dev, "mt_srv cmd 0x73 BSF unit %02x\n", unit);
switch(uptr->POS) {
case 0:
if (sim_tape_bot(uptr)) {
uptr->CMD &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
break;
}
uptr->POS++;
sim_activate(uptr, 500);
break;
case 1:
sim_debug(DEBUG_CMD, &mta_dev, "Backspace file unit=%02x\n", unit);
r = sim_tape_sprecr(uptr, &reclen);
if (r == MTSE_TMK) {
uptr->POS++;
sim_debug(DEBUG_DETAIL, &mta_dev, "MARK\n");
sim_activate(uptr, 50);
} else if (r == MTSE_BOT) {
uptr->POS+= 2;
sim_activate(uptr, 50);
} else {
sim_activate(uptr, 20);
}
break;
case 2: /* File Mark */
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_DEVEND); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
case 3: /* BOT */
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_DEVEND); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
}
break;
case MT_FSR: /* 0x43 */ /* Advance record */
switch(uptr->POS) {
case 0:
sim_debug(DEBUG_CMD, &mta_dev, "Skip rec entry unit=%02x ", unit);
uptr->POS++;
sim_activate(uptr, 50);
break;
case 1:
uptr->POS++;
sim_debug(DEBUG_CMD, &mta_dev, "Skip rec unit=%02x ", unit);
r = sim_tape_sprecf(uptr, &reclen);
if (r == MTSE_TMK) {
uptr->POS = 3;
uptr->SNS |= SNS_FMRKDT; /* file mark detected */
sim_debug(DEBUG_CMD, &mta_dev, "FSR MARK\n");
sim_activate(uptr, 50);
} else if (r == MTSE_EOM) {
uptr->POS = 4;
uptr->SNS |= SNS_EOT; /* set EOT status */
sim_activate(uptr, 50);
} else {
sim_debug(DEBUG_CMD, &mta_dev, "FSR skipped %04x byte record\n",
reclen);
sim_activate(uptr, 10 + (10 * reclen));
}
break;
case 2:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
sim_debug(DEBUG_CMD, &mta_dev, "Skip record Completed\n");
chan_end(addr, SNS_DEVEND); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
case 3:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_DEVEND|SNS_UNITEXP); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP);
sim_debug(DEBUG_CMD, &mta_dev, "Skip record at EOF\n");
break;
case 4:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
sim_debug(DEBUG_CMD, &mta_dev, "Skip record at EOT\n");
//BAD chan_end(addr, SNS_DEVEND|SNS_UNITCHK); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
chan_end(addr, SNS_DEVEND|SNS_UNITEXP);
break;
}
break;
case MT_FSF: /* 0x63 */ /* advance filemark */
switch(uptr->POS) {
case 0:
sim_debug(DEBUG_CMD, &mta_dev, "Skip file entry sense %08x unit %02x\n", uptr->SNS, unit);
uptr->POS++;
sim_activate(uptr, 50);
break;
case 1:
sim_debug(DEBUG_CMD, &mta_dev, "Skip file unit=%02x\n", unit);
r = sim_tape_sprecf(uptr, &reclen);
if (r == MTSE_TMK) {
uptr->POS++;
uptr->SNS |= SNS_FMRKDT; /* file mark detected */
sim_debug(DEBUG_CMD, &mta_dev, "FSF EOF MARK sense %08x\n", uptr->SNS);
sim_activate(uptr, 50);
} else if (r == MTSE_EOM) {
uptr->SNS |= SNS_EOT; /* set EOT status */
sim_debug(DEBUG_CMD, &mta_dev, "FSF EOT sense %08x\n", uptr->SNS);
uptr->POS+= 2;
sim_activate(uptr, 50);
} else {
sim_debug(DEBUG_CMD, &mta_dev, "FSF skipped %04x byte record\n", reclen);
sim_activate(uptr, 50);
}
break;
case 2:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
uptr->SNS &= ~SNS_LOAD; /* reset BOT */
sim_debug(DEBUG_CMD, &mta_dev, "Skip file done sense %08x unit %02x\n", uptr->SNS, unit);
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */
break;
case 3:
uptr->CMD &= ~(MT_CMDMSK);
uptr->SNS &= ~SNS_LOAD; /* reset BOT */
mt_busy[bufnum] &= ~1;
sim_debug(DEBUG_CMD, &mta_dev, "Skip file got EOT sense %08x unit %02x\n", uptr->SNS, unit);
//BAD chan_end(addr, SNS_DEVEND|SNS_UNITCHK); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
chan_end(addr, SNS_DEVEND|SNS_UNITEXP);
break;
}
break;
case MT_ERG: /* 0xA3 */ /* Erace 3.5 in tape */
switch (uptr->POS) {
case 0:
if (sim_tape_wrp(uptr)) {
uptr->SNS |= SNS_CMDREJ;
uptr->CMD &= ~MT_CMDMSK;
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_DEVEND|SNS_UNITCHK); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
} else {
uptr->POS ++;
sim_activate(uptr, 500);
}
break;
case 1:
sim_debug(DEBUG_CMD, &mta_dev, "Erase unit=%02x\n", unit);
r = sim_tape_wrgap(uptr, 35);
sim_activate(uptr, 5000);
uptr->POS++;
break;
case 2:
uptr->CMD &= ~(MT_CMDMSK);
mt_busy[bufnum] &= ~1;
/* we are done dev|chan end */
chan_end(addr, SNS_DEVEND); //NEW chan_end(addr, SNS_CHNEND|SNS_DEVEND);
break;
}
break;
case MT_REW: /* 0x23 */ /* rewind tape */
if (uptr->POS == 0) {
uptr->POS++;
sim_debug(DEBUG_CMD, &mta_dev, "Start rewind unit %02x\n", unit);
sim_activate(uptr, 1500);
} else {
sim_debug(DEBUG_CMD, &mta_dev, "Rewind complete unit %02x\n", unit);
uptr->CMD &= ~(MT_CMDMSK);
r = sim_tape_rewind(uptr);
uptr->SNS |= SNS_LOAD; /* set BOT */
mt_busy[bufnum] &= ~1;
chan_end(addr, SNS_CHNEND|SNS_DEVEND); /* we are done dev|chan end */
}
break;
case MT_RUN: /* 0x33 */ /* Rewind and unload tape */
if (uptr->POS == 0) {
uptr->POS++;
mt_busy[bufnum] &= ~1;
sim_activate(uptr, 30000);
} else {
sim_debug(DEBUG_CMD, &mta_dev, "Unload unit=%02x\n", unit);
uptr->CMD &= ~(MT_CMDMSK);
r = sim_tape_detach(uptr);
}
break;
}
return SCPE_OK;
}
/* initialize the tape chan/unit */
void mt_ini(UNIT *uptr, t_bool f)
{
DEVICE *dptr = find_dev_from_unit(uptr);
if (MT_DENS(uptr->dynflags) == 0)
uptr->dynflags |= MT_DENS_6250 << UNIT_S_DF_TAPE;
uptr->CMD &= ~0xffff; /* clear out the flags but leave ch/sa */
uptr->SNS = 0; /* clear sense data */
uptr->SNS |= (SNS_RDY|SNS_ONLN|SNS_LOAD); /* set initial status */
mt_busy[GET_DEV_BUF(dptr->flags)] = 0; /* set not busy */
sim_debug(DEBUG_EXP, dptr, "MT init device %s unit %02x\n", dptr->name,
GET_UADDR(uptr->CMD));
}
/* reset the mag tape */
t_stat mt_reset(DEVICE *dptr)
{
/* nothing to do?? */
sim_debug(DEBUG_EXP, &mta_dev, "MT reset name %s\n", dptr->name);
return SCPE_OK;
}
/* attach the specified file to the tape device */
t_stat mt_attach(UNIT *uptr, CONST char *file)
{
uint16 addr = GET_UADDR(uptr->CMD); /* get address of mt device */
t_stat r;
/* mount the specified file to the MT */
if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) {
sim_debug(DEBUG_EXP, &mta_dev, "mt_attach ERROR filename %s status %08x\n", file, r);
return r; /* report any error */
}
sim_debug(DEBUG_EXP, &mta_dev, "mt_attach complete filename %s\n", file);
set_devattn(addr, SNS_DEVEND); /* ready int???? */
return SCPE_OK; /* return good status */
}
/* detach the MT device and unload any tape */
t_stat mt_detach(UNIT *uptr)
{
sim_debug(DEBUG_EXP, &mta_dev, "mt_detach\n");
uptr->CMD = 0;
return sim_tape_detach(uptr);
}
/* boot from the specified tape unit */
t_stat mt_boot(int32 unit_num, DEVICE *dptr)
{
UNIT *uptr = &dptr->units[unit_num]; /* find tape unit pointer */
sim_debug(DEBUG_EXP, &mta_dev, "MT Boot dev/unit %04x\n", GET_UADDR(uptr->CMD));
if ((uptr->flags & UNIT_ATT) == 0) { /* Is MT device already attached? */
sim_debug(DEBUG_EXP, &mta_dev, "MT Boot attach error dev/unit %04x\n",
GET_UADDR(uptr->CMD));
return SCPE_UNATT; /* not attached, return error */
}
SPAD[0xf4] = GET_UADDR(uptr->CMD); /* put boot device chan/sa into spad */
SPAD[0xf8] = 0xF000; /* show as F class device */
uptr->CMD &= ~0xffff; /* clear out old status */
return chan_boot(GET_UADDR(uptr->CMD), dptr); /* boot the ch/sa */
}
t_stat mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
char buffer[256];
fprintf (st, "%s\n\n", mt_description(dptr));
fprintf (st, "The mag tape drives support the BOOT command\n\n");
(void)sim_tape_density_supported (buffer, sizeof(buffer), valid_dens);
fprintf (st, " The density of the mag tape drive can be set with\n");
fprintf (st, " SET %s DENSITY=%s\n\n", dptr->name, buffer);
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 "8051 Buffered Tape Processor";
}
#endif /* NUM_DEVS_MT */