1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-01-13 15:27:04 +00:00
rcornwell.sims/ICL1900/icl1900_eds8.c

658 lines
24 KiB
C

/* icl1900_eds8.c: ICL1900 EDS8 disk drive simulator.
Copyright (c) 2018, Richard Cornwell
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
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell
*/
#include "icl1900_defs.h"
#if (NUM_DEVS_EDS8 > 0)
#define UNIT_EDS8 UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE
#define CMD u3 /* Command */
#define CYL u4 /* Current cylinder. */
#define HDSEC u5 /* Head and sector to transfer */
#define POS u6 /* Current position in block */
#define SECT_TRK 8 /* Sectors/ track */
#define HD_CYL 10 /* Heads / cylinder */
#define CYLS 203 /* Cylinders */
#define WD_SEC 128 /* Number of words per sector */
/* Command is packed follows:
*
* Low order 5 bits indicate current command.
* Bit 6 indicates command terminated.
* Bit 7 indicates that first qualifier has not been recieved.
* Bit 8 indicates that the second qualifier has not been recieved.
* Bit 9 set when unit is started.
* Bit 10 set when command starts transfering data.
* Bit 11 set during seeking.
* Bit 12 indicates error.
* Bit 13 indicates wrong track.
* Bit 14 indicates long block
* Bit 15 indicates drive IRQ.
*
* Bit 16-24 stores current qualifier.
*/
#define EDS8_CMD 037
#define EDS8_NOP 000
#define EDS8_WRID 007 /* Write Identifiers Q1=head, Q2=F */
#define EDS8_ERASE 014 /* Erase Q1=head, Q2=sect */
#define EDS8_TSTWR 016 /* Test Write Q1=head, Q2=sect */
#define EDS8_SEEK 030 /* Seek Q1=Th, Q2=Tl */
#define EDS8_READ 031 /* Read Q1=head, Q2=sect */
#define EDS8_WRITE 032 /* Write Q1=head, Q2=sect */
#define EDS8_WRCHK 033 /* Write & Check Q1=head, Q2=sect */
#define EDS8_SUP_RD 034 /* Suppress read Q1=head, Q2=sect */
#define EDS8_RD_TRK 035 /* Read Track Q1=head, Q2=ignored */
#define EDS8_DISC 036 /* Disoconect No Qualifier */
#define EDS8_BOOT 037 /* Boot No Qualifier */
#define EDS8_TERM 000040 /* Command terminated */
#define EDS8_QUAL1 000100 /* 1st Qualifier expected */
#define EDS8_QUAL2 000200 /* 2nd Qualifier expected */
#define EDS8_BUSY 000400 /* Device running command */
#define EDS8_RUN 001000 /* Command executing. */
#define EDS8_SK 002000 /* Seeking in progress. */
#define EDS8_ERR 004000 /* Hard error */
#define EDS8_PATH 010000 /* Wrong track */
#define EDS8_LONG 020000 /* Reached end of cylinder during read/write */
#define ST1_OK 001 /* Unit available */
#define ST1_ERR 002 /* Hard error */
#define ST1_PATH 004 /* Wrong track */
#define ST1_LONG 010 /* Reached end of cylinder during read/write */
#define ST1_IRQ 020 /* Drive changed status */
#define STQ_TERM 001 /* Operation terminated */
#define STQ_DSK_RDY 004 /* Disk can accept orders */
#define STQ_CTL_RDY 030 /* Controller ready to accept new order */
#define STQ_P1 040 /* P1 Status on */
int eds8_busy; /* Indicates that controller is talking to a drive */
int eds8_drive; /* Indicates last selected drive */
uint32 eds8_buffer[WD_SEC];
void eds8_cmd (uint32 dev, uint32 cmd, uint32 *resp);
t_stat eds8_svc (UNIT *uptr);
t_stat eds8_reset (DEVICE *dptr);
t_stat eds8_boot (int32 unit_num, DEVICE * dptr);
t_stat eds8_attach(UNIT *, CONST char *);
t_stat eds8_detach(UNIT *);
t_stat eds8_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
CONST char *eds8_description (DEVICE *dptr);
DIB eds8_dib = { WORD_DEV|MULT_DEV, &eds8_cmd, NULL, NULL };
MTAB eds8_mod[] = {
{ MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED",
&set_writelock, &show_writelock, NULL, "Write enable drive" },
{ MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED",
&set_writelock, NULL, NULL, "Write lock drive" },
{MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "DEV", "DEV",
&set_chan, &get_chan, NULL, "Device Number"},
{0}
};
UNIT eds8_unit[] = {
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 0 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 1 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 2 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 3 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 4 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 5 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 6 */
{UDATA(&eds8_svc, UNIT_EDS8, 0) }, /* 7 */
};
DEVICE eds8_dev = {
"ED", eds8_unit, NULL, eds8_mod,
NUM_DEVS_EDS8, 8, 22, 1, 8, 22,
NULL, NULL, &eds8_reset, &eds8_boot, &eds8_attach, &detach_unit,
&eds8_dib, DEV_DISABLE | DEV_DEBUG | UNIT_ADDR(27), 0, dev_debug,
NULL, NULL, &eds8_help, NULL, NULL, &eds8_description
};
void eds8_cmd(uint32 dev, uint32 cmd, uint32 *resp) {
UNIT *uptr = &eds8_unit[eds8_drive];
*resp = 0;
if (cmd & 0400) {
sim_debug(DEBUG_CMD, &eds8_dev, "Cmd: set unit=%d %04o\n", eds8_drive, cmd);
eds8_drive = cmd & 07;
uptr = &eds8_unit[eds8_drive];
if ((uptr->flags & UNIT_ATT) != 0)
*resp = 5;
return;
}
cmd &= ~02000;
switch(cmd & 070) {
case 000: if (cmd == 7)
cmd |= EDS8_QUAL1|EDS8_QUAL2;
else if (cmd != 0) {
*resp = 3;
return;
}
break;
case 010: if ((cmd & 05) != 4) {
*resp = 3;
return;
}
cmd |= EDS8_QUAL1|EDS8_QUAL2;
break;
case 020: if (cmd == SEND_Q) {
if (uptr->CMD & EDS8_TERM)
*resp |= STQ_TERM;
if ((uptr->CMD & (EDS8_BUSY|EDS8_QUAL1|EDS8_QUAL2)) == 0)
*resp |= STQ_DSK_RDY;
if (eds8_busy == 0)
*resp |= STQ_CTL_RDY;
if ((uptr->flags & UNIT_ATT) != 0)
*resp |= STQ_P1;
uptr->CMD &= ~EDS8_TERM;
chan_clr_done(dev);
} else if (cmd == SEND_P) {
*resp = (uptr->CMD >> 10) & 036;
if ((uptr->flags & UNIT_ATT) != 0)
*resp |= 1;
uptr->CMD &= ~(036 << 10);
}
sim_debug(DEBUG_STATUS, &eds8_dev, "Status: unit:=%d %02o %02o\n", eds8_drive, cmd, *resp);
return;
case 030:
sim_debug(DEBUG_CMD, &eds8_dev, "Cmd: unit=%d %02o\n", eds8_drive, cmd);
cmd &= 077;
if (cmd < 036)
cmd |= EDS8_QUAL1|EDS8_QUAL2;
break;
case 040:
case 050:
if (uptr->CMD & EDS8_QUAL1) {
uptr->CMD |= ((cmd & 017) << 20);
uptr->CMD &= ~EDS8_QUAL1;
} else if (uptr->CMD & EDS8_QUAL2) {
uptr->CMD |= ((cmd & 017) << 16);
uptr->CMD &= ~EDS8_QUAL2;
}
sim_debug(DEBUG_STATUS, &eds8_dev, "Qual: unit:=%d %02o %02o\n", eds8_drive, cmd, *resp);
cmd = uptr->CMD;
*resp = 5;
break;
default:
*resp = 3;
return;
}
sim_debug(DEBUG_CMD, &eds8_dev, "Cmd: unit=%d %02o\n", eds8_drive, cmd);
if ((uptr->flags & UNIT_ATT) == 0) {
*resp = 0;
return;
}
if (eds8_busy || (uptr->CMD & EDS8_BUSY)) {
*resp = 3;
return;
}
if (cmd == 0) {
*resp = 5;
return;
}
uptr->CMD = cmd;
if ((uptr->CMD & (EDS8_QUAL1|EDS8_QUAL2)) == 0) {
sim_debug(DEBUG_CMD, &eds8_dev, "Cmd: unit=%d start %02o\n", eds8_drive, uptr->CMD);
eds8_busy = 1;
uptr->CMD |= EDS8_BUSY;
chan_clr_done(dev);
sim_activate(uptr, 100);
}
*resp = 5;
return;
}
t_stat eds8_svc (UNIT *uptr)
{
DEVICE *dptr = &eds8_dev;
int unit = (uptr - dptr->units);
int dev = GET_UADDR(dptr->flags);
uint32 word;
int i;
int wc;
int eor;
int da;
/* If not busy, false schedule, just exit */
if ((uptr->CMD & EDS8_BUSY) == 0)
return SCPE_OK;
/* If we need to seek, move heads. */
if (uptr->CMD & EDS8_SK) {
int diff;
sim_debug(DEBUG_DETAIL, &eds8_dev, "Seek: unit:=%d %d %d\n", unit, uptr->CYL, (uptr->CMD >> 16) & 0377);
diff = uptr->CYL - ((uptr->CMD >> 16) & 0377);
i = 1;
if (diff < 0) {
diff = -diff;
i = -1;
}
if (diff > 80) {
uptr->CYL -= i*20;
sim_activate(uptr, 2000);
return SCPE_OK;
} else if (diff > 50) {
uptr->CYL -= i*10;
sim_activate(uptr, 1000);
return SCPE_OK;
} else if (diff > 10) {
uptr->CYL -= i*10;
sim_activate(uptr, 500);
return SCPE_OK;
} else if (diff > 0) {
uptr->CYL -= i*1;
sim_activate(uptr, 10);
return SCPE_OK;
} else if (diff == 0) {
uptr->CMD &= ~EDS8_SK;
}
}
switch (uptr->CMD & EDS8_CMD) {
case EDS8_TSTWR:
/* Compare disk control word with generated one */
break;
case EDS8_ERASE:
/* Write data directly to disk ignoring headers. */
/* 146 to 166 words per sector */
break;
case EDS8_DISC:
/* Retract heads and offline the drive */
detach_unit(uptr);
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
case EDS8_SEEK:
/* Set desired cylinder to value */
if ((uptr->CMD & EDS8_RUN) == 0) {
int trk = (uptr->CMD >> 16) & 0377;
sim_debug(DEBUG_DETAIL, &eds8_dev, "Seek: start unit:=%d %d %d\n", unit, uptr->CYL, trk);
if (uptr->CYL == trk) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
} else if (trk > CYLS) {
/* Terminate with error */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
} else {
uptr->CMD |= EDS8_RUN|EDS8_SK|EDS8_TERM;
sim_activate(uptr, 500);
}
eds8_busy = 0;
/* trigger controller available */
chan_set_done(dev);
break;
}
if (uptr->CYL == ((uptr->CMD >> 16) & 0377)) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
chan_set_done(dev);
}
break;
case EDS8_SUP_RD:
/* Start reading sectors at the requested value. */
/* Read one sector for each word transfered. */
if ((uptr->CMD & EDS8_RUN) == 0) {
uptr->CMD |= EDS8_RUN;
uptr->HDSEC = (uptr->CMD >> 16) & 0377;
if (((uptr->HDSEC >> 4) & 017) > HD_CYL || (uptr->HDSEC & 017) > SECT_TRK) {
/* Terminate wrong track */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_PATH;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
eor = chan_output_word(dev, &word, 0);
if (eor) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
}
sim_debug(DEBUG_DATA, &eds8_dev, "RSUP: %08o\n", word);
uptr->HDSEC += 9; /* Bump sector number, if more then 8, will bump head */
uptr->HDSEC &= 0367;
/* If empty buffer, fill */
if (((uptr->HDSEC >> 4) & 017) > HD_CYL) {
/* Terminate long read */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_LONG;
eds8_busy = 0;
chan_set_done(dev);
break;
}
sim_activate(uptr, 100);
break;
case EDS8_BOOT:
/* Set cylinder to zero and set sector to zero and start read */
if ((uptr->CMD & EDS8_RUN) == 0 && uptr->CYL != 0) {
uptr->CMD |= EDS8_SK; /* Restore to cylinder zero */
sim_activate(uptr, 100);
return SCPE_OK;
}
/* Fall through */
case EDS8_RD_TRK:
/* Read a track into memory */
/* Format is: Word 0: T4 T5 T6 T7 0 0
T0 T1 T2 T3 0 0
H0 H1 H2 H3 0 0
S0 S1 S2 F 0 0
Word 1-129: sector data.
Word 130: control word of data block.
*/
case EDS8_READ:
if ((uptr->CMD & EDS8_RUN) == 0) {
uptr->CMD |= EDS8_RUN;
uptr->HDSEC = (uptr->CMD >> 16) & 0377;
if (((uptr->HDSEC >> 4) & 017) > HD_CYL || (uptr->HDSEC & 017) > SECT_TRK) {
/* Terminate wrong track */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_PATH;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
da = ((((uptr->CYL * HD_CYL) + ((uptr->HDSEC >> 4) & 017)) * SECT_TRK) +
(uptr->HDSEC & 07)) * WD_SEC;
(void)sim_fseek(uptr->fileref, da * sizeof(uint32), SEEK_SET);
wc = sim_fread(&eds8_buffer[0], sizeof(uint32), WD_SEC, uptr->fileref);
while(wc < WD_SEC)
eds8_buffer[wc++] = 0;
/* Compute header word */
if ((uptr->CMD & EDS8_CMD) == EDS8_RD_TRK) {
word = (((uptr->CYL >> 4) & 017) << 18) |
((uptr->CYL & 017) << 12) |
(((uptr->HDSEC >> 4) & 017) << 6) |
((uptr->HDSEC << 1) & 016);
eor = chan_input_word(dev, &word, 0);
if (eor) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
for (i = 0; i < WD_SEC; i++) {
sim_debug(DEBUG_DATA, &eds8_dev, "Data: %d <%08o\n", i, eds8_buffer[i]);
eor = chan_input_word(dev, &eds8_buffer[i], 0);
if (eor) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
/* Compute control word */
if ((uptr->CMD & EDS8_CMD) == EDS8_RD_TRK) {
uint32 even = 0;
uint32 odd = 0;
for (i = 0; i <WD_SEC; i++) {
word = eds8_buffer[i];
even ^= (word >> 20) ^ (word >> 16) ^ (word >> 12) ^
(word >> 8) ^ (word >> 4) ^ word;
odd ^= (word >> 18) ^ (word >> 12) ^ (word >> 6) ^ word;
}
word = ((even & 017) << 12) | (((odd ^ 017) & 017) << 8) | 0100;
eor = chan_input_word(dev, &word, 0);
if (eor) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
uptr->HDSEC += 9; /* Bump sector number, if more then 8, will bump head */
uptr->HDSEC &= 0367;
/* If empty buffer, fill */
if (((uptr->HDSEC >> 4) & 017) > HD_CYL) {
/* Terminate long read */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_LONG;
eds8_busy = 0;
chan_set_done(dev);
break;
}
sim_activate(uptr, 100);
break;
case EDS8_WRID:
/* Write track id's set F if set */
if ((uptr->CMD & EDS8_RUN) == 0) {
/* Write the starting at head/sector, if check, then check
that data can be read back at end of each track */
/* Check if write protected */
if ((uptr->flags & UNIT_WPRT) != 0) {
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_ERR;
eds8_busy = 0;
chan_set_done(dev);
break;
}
uptr->CMD |= EDS8_RUN;
uptr->HDSEC = (uptr->CMD >> 16) & 0360;
if (((uptr->HDSEC >> 4) & 017) > HD_CYL) {
/* Terminate wrong track */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_PATH;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
for (wc = 0; wc < WD_SEC; wc++)
eds8_buffer[wc] = 0;
da = ((((uptr->CYL * HD_CYL) + ((uptr->HDSEC >> 4) & 017)) * SECT_TRK) +
(uptr->HDSEC & 07)) * WD_SEC;
(void)sim_fseek(uptr->fileref, da * sizeof(uint32), SEEK_SET);
wc = sim_fwrite(&eds8_buffer[0], sizeof(uint32), WD_SEC, uptr->fileref);
uptr->HDSEC += 9; /* Bump sector number, if more then 8, will bump head */
/* If empty buffer, fill */
if ((uptr->HDSEC & 010) != 0) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
}
sim_activate(uptr, 100);
break;
case EDS8_WRCHK:
case EDS8_WRITE:
/* Write the starting at head/sector, if check, then check
that data can be read back at end of each track */
if ((uptr->CMD & EDS8_RUN) == 0) {
/* Check if write protected */
if ((uptr->flags & UNIT_WPRT) != 0) {
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_ERR;
eds8_busy = 0;
chan_set_done(dev);
break;
}
uptr->CMD |= EDS8_RUN;
uptr->HDSEC = (uptr->CMD >> 16) & 0377;
if (((uptr->HDSEC >> 4) & 017) > HD_CYL || (uptr->HDSEC & 017) > SECT_TRK) {
/* Terminate wrong track */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_PATH;
eds8_busy = 0;
chan_set_done(dev);
break;
}
}
for (wc = 0; wc < WD_SEC; wc++)
eds8_buffer[wc] = 0;
for (i = 0; i < WD_SEC; i++) {
eor = chan_output_word(dev, &eds8_buffer[i], 0);
if (eor)
break;
sim_debug(DEBUG_DATA, &eds8_dev, "Data: %d >%08o\n", i, eds8_buffer[i]);
}
da = ((((uptr->CYL * HD_CYL) + ((uptr->HDSEC >> 4) & 017)) * SECT_TRK) +
(uptr->HDSEC & 07)) * WD_SEC;
(void)sim_fseek(uptr->fileref, da * sizeof(uint32), SEEK_SET);
wc = sim_fwrite(&eds8_buffer[0], sizeof(uint32), WD_SEC, uptr->fileref);
if (eor) {
/* Terminate */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM;
eds8_busy = 0;
chan_set_done(dev);
break;
}
uptr->HDSEC += 9; /* Bump sector number, if more then 8, will bump head */
uptr->HDSEC &= 0367;
/* If empty buffer, fill */
if (((uptr->HDSEC >> 4) & 017) > HD_CYL) {
/* Terminate long read */
uptr->CMD &= ~(EDS8_RUN|EDS8_SK|EDS8_BUSY);
uptr->CMD |= EDS8_TERM|EDS8_LONG;
eds8_busy = 0;
chan_set_done(dev);
break;
}
sim_activate(uptr, 100);
break;
}
return SCPE_OK;
}
/* Reset */
t_stat eds8_reset (DEVICE *dptr)
{
UNIT *uptr = dptr->units;
uint32 unit;
for (unit = 0; unit < dptr->numunits; unit++, uptr++) {
uptr->CMD = 0;
uptr->CYL = 0;
eds8_busy = 0;
}
chan_clr_done(GET_UADDR(dptr->flags));
return SCPE_OK;
}
/* Boot from given device */
t_stat
eds8_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
int chan = GET_UADDR(dptr->flags);
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT; /* attached? */
M[64 + chan] = 0;
M[256 + 4 * chan] = B2;
M[257 + 4 * chan] = 020;
loading = 1;
eds8_busy = 1;
uptr->CMD = EDS8_BUSY|EDS8_BOOT;
sim_activate (uptr, 100);
return SCPE_OK;
}
t_stat
eds8_attach(UNIT * uptr, CONST char *file)
{
t_stat r;
if ((r = attach_unit(uptr, file)) != SCPE_OK)
return r;
uptr->CYL = 0;
uptr->CMD = EDS8_TERM;
chan_set_done(GET_UADDR(eds8_dev.flags));
return SCPE_OK;
}
t_stat eds8_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cmt)
{
fprintf (st, "The Paper Tape Reader can be set to one of twp modes: 7P, or 7B.\n\n");
fprintf (st, " mode \n");
fprintf (st, " 7P Process even parity input tapes. \n");
fprintf (st, " 7B Ignore parity of input data.\n");
fprintf (st, "The default mode is 7B.\n");
return SCPE_OK;
}
CONST char *eds8_description (DEVICE *dptr)
{
return "ED";
}
#endif