Because some key files have changed, V3.0 should be unzipped to a clean directory. 1. New Features in 3.0-0 1.1 SCP and Libraries - Added ASSIGN/DEASSIGN (logical name) commands. - Changed RESTORE to unconditionally detach files. - Added E11 and TPC format support to magtape library. - Fixed bug in SHOW CONNECTIONS. - Added USE_ADDR64 support 1.2 All magtapes - Magtapes support SIMH format, E11 format, and TPC format (read only). - SET <tape_unit> FORMAT=format sets the specified tape unit's format. - SHOW <tape_unit> FORMAT displays the specified tape unit's format. - Tape format can also be set as part of the ATTACH command, using the -F switch. 1.3 VAX - VAX can be compiled without USE_INT64. - If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support files > 2GB. - VAX ROM has speed control (SET ROM DELAY/NODELAY). 2. Bugs Fixed in 3.01-0 2.1 VAX - Fixed CVTfi bug: integer overflow not set if exponent out of range - Fixed EMODx bugs: o First and second operands reversed o Separated fraction received wrong exponent o Overflow calculation on separated integer incorrect o Fraction not set to zero if exponent out of range - Fixed interval timer and ROM access to pass power-up self-test even on very fast host processors (fixes from Mark Pizzolato). 2.2 1401 - Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS. - Fixed MCE bug, BS off by 1 if zero suppress. - Fixed chaining bug, D lost if return to SCP. - Fixed H branch, branch occurs after continue. - Added check for invalid 8 character MCW, LCA. - Fixed magtape load-mode end of record response. 2.3 Nova - Fixed DSK variable size interaction with restore. 2.4 PDP-1 - Fixed DT variable size interaction with restore. 2.5 PDP-11 - Fixed DT variable size interaction with restore. - Fixed bug in MMR1 update (found by Tim Stark). - Added XQ features and fixed bugs: o Corrected XQ interrupts on IE state transition (code by Tom Evans). o Added XQ interrupt clear on soft reset. o Removed XQ interrupt when setting XL or RL (multiple people). o Added SET/SHOW XQ STATS. o Added SHOW XQ FILTERS. o Added ability to split received packet into multiple buffers. o Added explicit runt and giant packet processing. 2.6 PDP-18B - Fixed DT, RF variable size interaction with restore. - Fixed MT bug in MTTR. 2.7 PDP-8 - Fixed DT, DF, RF, RX variable size interaction with restore. - Fixed MT bug in SKTR. 2.8 HP2100 - Fixed bug in DP (13210A controller only), DQ read status. - Fixed bug in DP, DQ seek complete. 2.9 GRI - Fixed bug in SC queue pointer management. 3. New Features in 3.0 vs prior releases N/A 4. Bugs Fixed in 3.0 vs prior releases N/A 5. General Notes WARNING: The RESTORE command has changed. RESTORE will now detach an attached file on a unit, if that unit did not have an attached file in the saved configuration. This is required to assure that the unit flags and the file state are consistent. WARNING: The compilation scheme for the PDP-10, PDP-11, and VAX has changed. Use one of the supplied build files, or read the documentation carefully, before compiling any of these simulators.
463 lines
16 KiB
C
463 lines
16 KiB
C
/* i1620_dp.c: IBM 1311 disk simulator
|
||
|
||
Copyright (c) 2002-2003, Robert M. Supnik
|
||
|
||
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
|
||
ROBERT M SUPNIK 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 Robert M Supnik shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from Robert M Supnik.
|
||
|
||
dp 1311 disk pack
|
||
|
||
The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track.
|
||
Each sector contains 105 characters of information:
|
||
|
||
5c sector address
|
||
100c sector data
|
||
|
||
By default, a sector's address field will be '00000', which is interpreted
|
||
to mean the implied sector number that would be in place if the disk pack
|
||
had been formatted with sequential sector numbers.
|
||
|
||
18-Oct-02 RMS Fixed bug in error testing (found by Hans Pufal)
|
||
*/
|
||
|
||
#include "i1620_defs.h"
|
||
|
||
#define DP_NUMDR 4 /* #drives */
|
||
#define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */
|
||
#define UNIT_WAE (1 << UNIT_V_WAE)
|
||
|
||
/* Disk format */
|
||
|
||
#define DP_ADDR 5 /* address */
|
||
#define DP_DATA 100 /* data */
|
||
#define DP_NUMCH (DP_ADDR + DP_DATA)
|
||
|
||
#define DP_NUMSC 20 /* #sectors */
|
||
#define DP_NUMSF 10 /* #surfaces */
|
||
#define DP_NUMCY 100 /* #cylinders */
|
||
#define DP_TOTSC (DP_NUMCY * DP_NUMSF * DP_NUMSC)
|
||
#define DP_SIZE (DP_TOTSC * DP_NUMCH)
|
||
|
||
/* Disk control field */
|
||
|
||
#define DCF_DRV 0 /* drive select */
|
||
#define DCF_SEC 1 /* sector addr */
|
||
#define DCF_SEC_LEN 5
|
||
#define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */
|
||
#define DCF_CNT_LEN 3
|
||
#define DCF_ADR (DCF_CNT + DCF_CNT_LEN) /* buffer address */
|
||
#define DCF_ADR_LEN 5
|
||
#define DCF_LEN (DCF_ADR + DCF_ADR_LEN)
|
||
|
||
/* Functions */
|
||
|
||
#define FNC_SEEK 1 /* seek */
|
||
#define FNC_SEC 0 /* sectors */
|
||
#define FNC_WCH 1 /* write check */
|
||
#define FNC_NRL 2 /* no rec lnt chk */
|
||
#define FNC_TRK 4 /* tracks */
|
||
#define FNC_WRI 8 /* write offset */
|
||
|
||
#define CYL u3 /* current cylinder */
|
||
|
||
extern uint8 M[MAXMEMSIZE]; /* memory */
|
||
extern uint8 ind[NUM_IND];
|
||
extern UNIT cpu_unit;
|
||
|
||
int32 dp_stop = 1; /* disk err stop */
|
||
uint32 dp_ba = 0; /* buffer addr */
|
||
|
||
t_stat dp_reset (DEVICE *dptr);
|
||
t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc);
|
||
t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc);
|
||
t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr);
|
||
t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr);
|
||
int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd);
|
||
t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd);
|
||
t_bool dp_zeroad (uint8 *ap);
|
||
int32 dp_cvt_ad (uint8 *ap);
|
||
int32 dp_trkop (int32 drv, int32 sec);
|
||
int32 dp_cvt_bcd (uint32 ad, int32 len);
|
||
void dp_fill (UNIT *uptr, uint32 da, int32 cnt);
|
||
t_stat dp_tstgm (uint32 c, int32 qnr);
|
||
|
||
/* DP data structures
|
||
|
||
dp_dev DP device descriptor
|
||
dp_unit DP unit list
|
||
dp_reg DP register list
|
||
dp_mod DP modifier list
|
||
*/
|
||
|
||
UNIT dp_unit[] = {
|
||
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
|
||
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
|
||
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
|
||
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
|
||
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
|
||
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
|
||
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
|
||
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } };
|
||
|
||
REG dp_reg[] = {
|
||
{ FLDATA (ADCHK, ind[IN_DACH], 0) },
|
||
{ FLDATA (WLRC, ind[IN_DWLR], 0) },
|
||
{ FLDATA (CYLO, ind[IN_DCYO], 0) },
|
||
{ FLDATA (ERR, ind[IN_DERR], 0) },
|
||
{ FLDATA (DPSTOP, dp_stop, 0) },
|
||
{ URDATA (CYL, dp_unit[0].CYL, 10, 8, 0,
|
||
DP_NUMDR, PV_LEFT + REG_RO) },
|
||
{ NULL } };
|
||
|
||
MTAB dp_mod[] = {
|
||
{ UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL },
|
||
{ UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL },
|
||
{ 0 } };
|
||
|
||
DEVICE dp_dev = {
|
||
"DP", dp_unit, dp_reg, dp_mod,
|
||
DP_NUMDR, 10, 21, 1, 16, 5,
|
||
NULL, NULL, &dp_reset,
|
||
NULL, NULL, NULL };
|
||
|
||
/* Disk IO routine */
|
||
|
||
t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1)
|
||
{
|
||
int32 drv, sa, sec, psec, cnt, qwc, qnr, t;
|
||
UNIT *uptr;
|
||
t_stat r;
|
||
|
||
if (pa & 1) return STOP_INVDCF; /* dcf must be even */
|
||
ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */
|
||
ind[IN_DERR] = ind[IN_DCYO] = 0;
|
||
sa = ADDR_A (pa, DCF_SEC); /* ptr to sector */
|
||
if (((dp_unit[0].flags & UNIT_DIS) == 0) && /* only drive 0? */
|
||
(dp_unit[1].flags & UNIT_DIS) &&
|
||
(dp_unit[2].flags & UNIT_DIS) &&
|
||
(dp_unit[3].flags & UNIT_DIS)) drv = 0; /* ignore drv select */
|
||
else drv = (((M[pa] & 1)? M[pa]: M[sa]) & 0xE) >> 1; /* drive # */
|
||
if (drv >= DP_NUMDR) return STOP_INVDRV; /* invalid? */
|
||
uptr = dp_dev.units + drv; /* get unit ptr */
|
||
if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
|
||
ind[IN_DERR] = 1; /* no, error */
|
||
CRETIOE (dp_stop, SCPE_UNATT); }
|
||
|
||
sec = dp_cvt_bcd (sa, DCF_SEC_LEN); /* cvt sector */
|
||
if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */
|
||
return STOP_INVDSC;
|
||
if (op == OP_K) { /* seek? */
|
||
if (f1 != FNC_SEEK) return STOP_INVFNC; /* really? */
|
||
uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */
|
||
DP_NUMCY;
|
||
return SCPE_OK; } /* done! */
|
||
|
||
cnt = dp_cvt_bcd (ADDR_A (pa, DCF_CNT), DCF_CNT_LEN); /* get count */
|
||
t = dp_cvt_bcd (ADDR_A (pa, DCF_ADR), DCF_ADR_LEN); /* get address */
|
||
if ((t < 0) || (t & 1)) return STOP_INVDBA; /* bad address? */
|
||
dp_ba = t; /* save addr */
|
||
|
||
if (f1 >= FNC_WRI) return STOP_INVFNC; /* invalid func? */
|
||
if (op == OP_RN) qwc = f1 & FNC_WCH; /* read? set wch */
|
||
else if (op == OP_WN) { /* write? */
|
||
if (op & FNC_WCH) return STOP_INVFNC; /* cant check */
|
||
f1 = f1 + FNC_WRI; } /* offset fnc */
|
||
else return STOP_INVFNC; /* not R or W */
|
||
qnr = f1 & FNC_NRL; /* no rec check? */
|
||
|
||
switch (f1 & ~(FNC_WCH | FNC_NRL)) { /* case on function */
|
||
case FNC_SEC: /* read sectors */
|
||
if (cnt <= 0) return STOP_INVDCN; /* bad count? */
|
||
psec = dp_fndsec (uptr, sec, TRUE); /* find sector */
|
||
if (psec < 0) CRETIOE (dp_stop, STOP_DACERR); /* error? */
|
||
do { /* loop on count */
|
||
if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read sector */
|
||
break;
|
||
sec++; psec++; } /* next sector */
|
||
while ((--cnt > 0) &&
|
||
((r = dp_nexsec (uptr, sec, psec, TRUE)) == SCPE_OK));
|
||
break; /* done, clean up */
|
||
|
||
case FNC_TRK: /* read track */
|
||
psec = dp_trkop (drv, sec); /* start of track */
|
||
for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */
|
||
if (r = dp_rdadr (uptr, psec, qnr, qwc)) /* read addr */
|
||
break; /* error? */
|
||
if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read data */
|
||
break; /* error? */
|
||
psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); }
|
||
break; /* done, clean up */
|
||
|
||
case FNC_SEC + FNC_WRI: /* write */
|
||
if (cnt <= 0) return STOP_INVDCN; /* bad count? */
|
||
psec = dp_fndsec (uptr, sec, FALSE); /* find sector */
|
||
if (psec < 0) CRETIOE (dp_stop, STOP_DACERR); /* error? */
|
||
do { /* loop on count */
|
||
if (r = dp_tstgm (M[dp_ba], qnr)) break; /* start with gm? */
|
||
if (r = dp_wrsec (uptr, psec, qnr)) break; /* write data */
|
||
sec++; psec++; } /* next sector */
|
||
while ((--cnt > 0) &&
|
||
((r = dp_nexsec (uptr, sec, psec, FALSE)) == SCPE_OK));
|
||
break; /* done, clean up */
|
||
|
||
case FNC_TRK + FNC_WRI: /* write track */
|
||
if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */
|
||
return STOP_WRADIS;
|
||
psec = dp_trkop (drv, sec); /* start of track */
|
||
for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */
|
||
if (r = dp_tstgm (M[dp_ba], qnr)) break; /* start with gm? */
|
||
if (r = dp_wradr (uptr, psec, qnr)) break; /* write addr */
|
||
if (r = dp_wrsec (uptr, psec, qnr)) break; /* write data */
|
||
psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); }
|
||
break; /* done, clean up */
|
||
|
||
default: /* unknown */
|
||
return STOP_INVFNC; }
|
||
|
||
if ((r == SCPE_OK) && !qnr) { /* eor check? */
|
||
if ((M[dp_ba] & DIGIT) != GRP_MARK) { /* GM at end? */
|
||
ind[IN_DWLR] = ind[IN_DERR] = 1; /* no, error */
|
||
r = STOP_WRLERR; } }
|
||
if ((r != SCPE_OK) && /* error? */
|
||
(dp_stop || !ind[IN_DERR])) return r; /* iochk or stop? */
|
||
return SCPE_OK; /* continue */
|
||
}
|
||
|
||
/* Read or compare address with memory */
|
||
|
||
t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc)
|
||
{
|
||
int32 i;
|
||
uint8 ad;
|
||
int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
|
||
t_bool zad = dp_zeroad (ap); /* zero address */
|
||
static const int32 dec_tab[DP_ADDR] = /* powers of 10 */
|
||
{ 10000, 1000, 100, 10, 1} ;
|
||
|
||
for (i = 0; i < DP_ADDR; i++) { /* copy/check addr */
|
||
if (zad) { /* addr zero? */
|
||
ad = sec / dec_tab[i]; /* get addr digit */
|
||
sec = sec % dec_tab[i]; } /* get remainder */
|
||
else ad = *ap; /* addr digit */
|
||
if (qwc) { /* write check? */
|
||
if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */
|
||
return STOP_WRLERR; /* yes, error */
|
||
if (!zad && (M[dp_ba] != ad)) { /* digits equal? */
|
||
ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */
|
||
return STOP_DWCERR; } }
|
||
else M[dp_ba] = ad & (FLAG | DIGIT); /* store digit */
|
||
if (dp_tstgm (*ap, qnr)) return STOP_WRLERR; /* grp mrk on disk? */
|
||
ap++; PP (dp_ba); } /* adv ptrs */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Read or compare data with memory */
|
||
|
||
t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc)
|
||
{
|
||
int32 i;
|
||
int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */
|
||
|
||
for (i = 0; i < DP_DATA; i++) { /* copy data */
|
||
if (qwc) { /* write check? */
|
||
if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */
|
||
return STOP_WRLERR; /* yes, error */
|
||
if (M[dp_ba] != *ap) { /* dig+flags equal? */
|
||
ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */
|
||
return STOP_DWCERR; } }
|
||
else M[dp_ba] = *ap & (FLAG | DIGIT); /* flag + digit */
|
||
if (dp_tstgm (*ap, qnr)) return STOP_WRLERR; /* grp mrk on disk? */
|
||
ap++; PP (dp_ba); } /* adv ptrs */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Write address to disk */
|
||
|
||
t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr)
|
||
{
|
||
int32 i;
|
||
uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
|
||
|
||
for (i = 0; i < DP_ADDR; i++) { /* copy address */
|
||
*ap = M[dp_ba] & (FLAG | DIGIT); /* flag + digit */
|
||
if (da >= uptr->hwmark) uptr->hwmark = da + 1;
|
||
if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */
|
||
dp_fill (uptr, da + 1, DP_NUMCH - i - 1); /* fill addr+data */
|
||
return STOP_WRLERR; } /* error */
|
||
da++; ap++; PP (dp_ba); /* adv ptrs */
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Write data to disk */
|
||
|
||
t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr)
|
||
{
|
||
int32 i;
|
||
uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
|
||
|
||
for (i = 0; i < DP_DATA; i++) { /* copy data */
|
||
*ap = M[dp_ba] & (FLAG | DIGIT); /* get character */
|
||
if (da >= uptr->hwmark) uptr->hwmark = da + 1;
|
||
if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */
|
||
dp_fill (uptr, da + 1, DP_DATA - i - 1); /* fill data */
|
||
return STOP_WRLERR; } /* error */
|
||
da++; ap++; PP (dp_ba); /* adv ptrs */
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Find sector */
|
||
|
||
int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd)
|
||
{
|
||
int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
|
||
int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;
|
||
int32 da = psec * DP_NUMCH; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
|
||
int32 dskad, i;
|
||
|
||
if (dp_zeroad (ap)) return psec; /* addr zero? ok */
|
||
dskad = dp_cvt_ad (ap); /* cvt addr */
|
||
if (dskad == sec) { /* match? */
|
||
if (rd || ((*ap & FLAG) == 0)) return psec; /* read or !wprot? */
|
||
ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */
|
||
return -1; }
|
||
psec = psec - (psec % DP_NUMSC); /* sector 0 */
|
||
for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */
|
||
da = psec * DP_NUMCH; /* char number */
|
||
ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */
|
||
if (dp_zeroad (ap)) continue; /* no implicit match */
|
||
dskad = dp_cvt_ad (ap); /* cvt addr */
|
||
if (dskad == sec) { /* match? */
|
||
if (rd || ((*ap & FLAG) == 0)) return psec; /* read or !wprot? */
|
||
ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */
|
||
return -1; } }
|
||
ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */
|
||
return -1;
|
||
}
|
||
|
||
/* Find next sector - must be sequential, cannot cross cylinder boundary */
|
||
|
||
t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd)
|
||
{
|
||
int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
|
||
int32 da = psec * DP_NUMCH; /* word number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
|
||
int32 dskad;
|
||
|
||
if (ctrk) { /* not trk zero? */
|
||
if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */
|
||
dskad = dp_cvt_ad (ap); /* cvt addr */
|
||
if ((dskad == sec) && /* match? */
|
||
(rd || ((*ap & FLAG) == 0))) return SCPE_OK; /* read or !wprot? */
|
||
ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */
|
||
return STOP_DACERR; }
|
||
ind[IN_DCYO] = ind[IN_DERR] = 1; /* cyl overflow */
|
||
return STOP_CYOERR;
|
||
}
|
||
|
||
/* Test for zero address */
|
||
|
||
t_bool dp_zeroad (uint8 *ap)
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
|
||
if (*ap & DIGIT) return FALSE; } /* nonzero? lose */
|
||
return TRUE; /* all zeroes */
|
||
}
|
||
|
||
/* Test for group mark when enabled */
|
||
|
||
t_stat dp_tstgm (uint32 c, int32 qnr)
|
||
{
|
||
if (!qnr && ((c & DIGIT) == GRP_MARK)) { /* premature GM? */
|
||
ind[IN_DWLR] = ind[IN_DERR] = 1; /* error */
|
||
return STOP_WRLERR; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Convert disk address to binary - invalid char force bad address */
|
||
|
||
int32 dp_cvt_ad (uint8 *ap)
|
||
{
|
||
int32 i, r;
|
||
uint8 c;
|
||
|
||
for (i = r = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
|
||
c = *ap & DIGIT; /* get digit */
|
||
if (BAD_DIGIT (c)) return -1; /* bad digit? */
|
||
r = (r * 10) + c; } /* bcd to binary */
|
||
return r;
|
||
}
|
||
|
||
/* Track operation setup */
|
||
|
||
int32 dp_trkop (int32 drv, int32 sec)
|
||
{
|
||
int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;
|
||
|
||
return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +
|
||
(ctrk * DP_NUMSC));
|
||
}
|
||
|
||
/* Convert DCF BCD field to binary */
|
||
|
||
int32 dp_cvt_bcd (uint32 ad, int32 len)
|
||
{
|
||
uint8 c;
|
||
int32 r;
|
||
|
||
for (r = 0; len > 0; len--) { /* loop thru char */
|
||
c = M[ad] & DIGIT; /* get digit */
|
||
if (BAD_DIGIT (c)) return -1; /* invalid? */
|
||
r = (r * 10) + c; /* cvt to bin */
|
||
PP (ad); } /* next digit */
|
||
return r;
|
||
}
|
||
|
||
/* Fill sector buffer with zero */
|
||
|
||
void dp_fill (UNIT *uptr, uint32 da, int32 cnt)
|
||
{
|
||
while (cnt-- > 0) { /* fill with zeroes*/
|
||
*(((uint8 *) uptr->filebuf) + da) = 0;
|
||
if (da >= uptr->hwmark) uptr->hwmark = da + 1;
|
||
da++; }
|
||
return;
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat dp_reset (DEVICE *dptr)
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */
|
||
ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */
|
||
ind[IN_DERR] = ind[IN_DCYO] = 0;
|
||
return SCPE_OK;
|
||
}
|