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.
555 lines
20 KiB
C
555 lines
20 KiB
C
/* i1401_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
|
||
|
||
18-Oct-02 RMS Fixed bug in address comparison logic
|
||
19-Sep-02 RMS Minor edit for consistency with 1620
|
||
15-Jun-02 RMS Reworked address comparison logic
|
||
|
||
The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track.
|
||
Each sector contains 106 characters of information:
|
||
|
||
6c sector address
|
||
100c sector data
|
||
|
||
By default, a sector's address field will be '000000', which is illegal.
|
||
This is interpreted to mean the implied sector number that would be in
|
||
place if the disk pack had been formatted with sequential sector numbers.
|
||
|
||
The sector data can be 100 characters without word marks, or 90 characters
|
||
with word marks. Load mode transfers 90 characters per sector with
|
||
word marks, move mode transfers 100 characters per sector without word
|
||
marks. No attempt is made to catch incompatible writes (eg, load mode
|
||
write followed by move mode read).
|
||
*/
|
||
|
||
#include "i1401_defs.h"
|
||
|
||
#define DP_NUMDR 5 /* #drives */
|
||
#define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */
|
||
#define UNIT_WAE (1 << UNIT_V_WAE)
|
||
|
||
/* Disk format */
|
||
|
||
#define DP_ADDR 6 /* 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 6
|
||
#define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */
|
||
#define DCF_CNT_LEN 3
|
||
#define DCF_LEN (DCF_CNT + DCF_CNT_LEN)
|
||
#define DCF_DIR 1 /* direct seek */
|
||
#define DCF_DIR_LEN 4
|
||
#define DCF_DIR_FL (DCF_DIR + DCF_DIR_LEN) /* direct seek flag */
|
||
#define DCF_DSEEK 0xB
|
||
|
||
/* Functions */
|
||
|
||
#define FNC_SEEK 0 /* seek */
|
||
#define FNC_CHECK 3 /* check */
|
||
#define FNC_READ 1 /* read sectors */
|
||
#define FNC_RSCO 5 /* read sec cnt overlay */
|
||
#define FNC_RTRK 6 /* read track */
|
||
#define FNC_WOFF 10 /* offset for write */
|
||
#define FNC_WRITE 11 /* write sectors */
|
||
#define FNC_WRSCO 15 /* write sec cnt overlay */
|
||
#define FNC_WRTRK 16 /* write track */
|
||
|
||
#define CYL u3 /* current cylinder */
|
||
|
||
extern uint8 M[]; /* memory */
|
||
extern int32 ind[64];
|
||
extern int32 AS, BS, iochk;
|
||
extern int32 bcd_to_bin[16];
|
||
extern int32 bin_to_bcd[16];
|
||
extern UNIT cpu_unit;
|
||
|
||
int32 dp_lastf = 0; /* prior function */
|
||
int32 dp_time = 0; /* seek time */
|
||
|
||
t_stat dp_reset (DEVICE *dptr);
|
||
t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 wchk);
|
||
t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 wchk);
|
||
t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg);
|
||
t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg);
|
||
int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf);
|
||
t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf);
|
||
t_bool dp_zeroad (uint8 *ap);
|
||
t_bool dp_cmp_ad (uint8 *ap, int32 dcf);
|
||
int32 dp_trkop (int32 drv, int32 sec);
|
||
int32 dp_cvt_bcd (int32 ad, int32 len);
|
||
void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg);
|
||
int32 dp_get_cnt (int32 dcf);
|
||
void dp_fill (UNIT *uptr, uint32 da, int32 cnt);
|
||
|
||
/* DP data structures
|
||
|
||
dp_dev DSK device descriptor
|
||
dp_unit DSK unit list
|
||
dp_reg DSK register list
|
||
dp_mod DSK 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) },
|
||
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
|
||
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } };
|
||
|
||
REG dp_reg[] = {
|
||
{ FLDATA (ACC, ind[IN_ACC], 0) },
|
||
{ FLDATA (PWC, ind[IN_DPW], 0) },
|
||
{ FLDATA (WLR, ind[IN_LNG], 0) },
|
||
{ FLDATA (UNA, ind[IN_UNA], 0) },
|
||
{ FLDATA (ERR, ind[IN_DSK], 0) },
|
||
{ FLDATA (BSY, ind[IN_DBY], 0) },
|
||
{ DRDATA (LASTF, dp_lastf, 3) },
|
||
{ DRDATA (TIME, dp_time, 24), PV_LEFT },
|
||
{ 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, 8, 7,
|
||
NULL, NULL, &dp_reset,
|
||
NULL, NULL, NULL };
|
||
|
||
/* Disk IO routine
|
||
|
||
Inputs:
|
||
fnc = function character
|
||
flg = load vs move mode
|
||
mod = modifier character
|
||
Outputs:
|
||
status = status
|
||
*/
|
||
|
||
t_stat dp_io (int32 fnc, int32 flg, int32 mod)
|
||
{
|
||
int32 dcf, drv, sec, psec, cnt, qwc, qzr, diff;
|
||
UNIT *uptr;
|
||
t_stat r;
|
||
|
||
dcf = BS; /* save DCF addr */
|
||
qwc = 0; /* not wcheck */
|
||
ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
|
||
ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
|
||
if (sim_is_active (&dp_unit[0])) { /* ctlr busy? */
|
||
ind[IN_DBY] = ind[IN_DSK] = 1; /* set indicators */
|
||
return SCPE_OK; } /* done */
|
||
|
||
AS = dcf + 6; /* AS for most ops */
|
||
BS = dcf + DCF_CNT - 1; /* minimum DCF */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */
|
||
if (M[dcf] & BBIT) drv = M[dcf + DCF_SEC + 1] & 0xE; /* impl sel? cyl 8-4-2 */
|
||
else drv = M[dcf] & DIGIT; /* get drive sel */
|
||
if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */
|
||
return STOP_INVDSK;
|
||
drv = bcd_to_bin[drv] >> 1; /* convert */
|
||
uptr = dp_dev.units + drv; /* get unit ptr */
|
||
if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
|
||
ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */
|
||
CRETIOE (iochk, SCPE_UNATT); }
|
||
|
||
if ((fnc == FNC_SEEK) && /* seek and */
|
||
(M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */
|
||
diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */
|
||
if (diff < 0) return STOP_INVDSC; /* error? */
|
||
diff = diff >> 1; /* diff is *2 */
|
||
if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT)
|
||
diff = -diff; /* get sign */
|
||
uptr->CYL = uptr->CYL + diff; /* bound seek */
|
||
if (uptr->CYL < 0) uptr->CYL = 0;
|
||
else if (uptr->CYL >= DP_NUMCY) { /* too big? */
|
||
uptr->CYL = 0; /* system hangs */
|
||
return STOP_INVDCY; }
|
||
sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
|
||
return SCPE_OK; } /* done! */
|
||
|
||
sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */
|
||
if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */
|
||
return STOP_INVDSC;
|
||
if (fnc == FNC_SEEK) { /* seek? */
|
||
uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */
|
||
DP_NUMCY;
|
||
sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
|
||
return SCPE_OK; } /* done! */
|
||
|
||
BS = dcf + DCF_LEN; /* full DCF */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */
|
||
cnt = dp_get_cnt (dcf); /* get count */
|
||
if (cnt < 0) return STOP_INVDCN; /* bad count? */
|
||
|
||
if (fnc >= FNC_WOFF) return STOP_INVDFN; /* invalid func */
|
||
if (mod == BCD_W) { /* write? */
|
||
if (fnc == FNC_CHECK) { /* write check? */
|
||
qwc = 1; /* special read */
|
||
fnc = dp_lastf; } /* use last func */
|
||
else {
|
||
dp_lastf = fnc; /* save func */
|
||
fnc = fnc + FNC_WOFF; } } /* change to write */
|
||
else if (mod == BCD_R) dp_lastf = fnc; /* read? save func */
|
||
else return STOP_INVM; /* other? error */
|
||
|
||
switch (fnc) { /* case on function */
|
||
case FNC_RSCO: /* read sec cnt ov */
|
||
BS = dcf + DCF_CNT; /* set count back */
|
||
/* fall thru */
|
||
case FNC_READ: /* read */
|
||
psec = dp_fndsec (uptr, sec, dcf); /* find sector */
|
||
if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */
|
||
for (;;) { /* loop */
|
||
qzr = (--cnt == 0); /* set zero latch */
|
||
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
|
||
if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read sector */
|
||
break;
|
||
cnt = dp_get_cnt (dcf); /* get new count */
|
||
if (cnt < 0) return STOP_INVDCN; /* bad count? */
|
||
if (qzr) break; /* zero latch? done */
|
||
sec++; psec++; /* next sector */
|
||
dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
|
||
if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */
|
||
}
|
||
break; /* done, clean up */
|
||
|
||
case FNC_RTRK: /* read track */
|
||
AS = dcf + 9; /* special AS */
|
||
psec = dp_trkop (drv, sec); /* start of track */
|
||
for (;;) { /* loop */
|
||
qzr = (--cnt == 0); /* set zero latch */
|
||
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
|
||
if (r = dp_rdadr (uptr, psec, flg, qwc)) /* read addr */
|
||
break; /* error? */
|
||
if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read data */
|
||
break; /* error? */
|
||
cnt = dp_get_cnt (dcf); /* get new count */
|
||
if (cnt < 0) return STOP_INVDCN; /* bad count? */
|
||
if (qzr) break; /* zero latch? done */
|
||
psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); }
|
||
break; /* done, clean up */
|
||
|
||
case FNC_WRSCO: /* write sec cnt ov */
|
||
BS = dcf + DCF_CNT; /* set count back */
|
||
/* fall through */
|
||
case FNC_WRITE: /* read */
|
||
psec = dp_fndsec (uptr, sec, dcf); /* find sector */
|
||
if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */
|
||
for (;;) { /* loop */
|
||
qzr = (--cnt == 0); /* set zero latch */
|
||
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */
|
||
if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */
|
||
if (qzr) break; /* zero latch? done */
|
||
sec++; psec++; /* next sector */
|
||
dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
|
||
if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */
|
||
}
|
||
break; /* done, clean up */
|
||
|
||
case FNC_WRTRK: /* write track */
|
||
if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */
|
||
return STOP_WRADIS;
|
||
AS = dcf + 9; /* special AS */
|
||
psec = dp_trkop (drv, sec); /* start of track */
|
||
for (;;) { /* loop */
|
||
qzr = (--cnt == 0); /* set zero latch */
|
||
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
|
||
if (r = dp_wradr (uptr, psec, flg)) break; /* write addr */
|
||
if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */
|
||
if (qzr) break; /* zero latch? done */
|
||
psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); }
|
||
break; /* done, clean up */
|
||
|
||
default: /* unknown */
|
||
return STOP_INVDFN; }
|
||
|
||
if (r == SCPE_OK) { /* normal so far? */
|
||
BS++; /* advance BS */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; /* address error? */
|
||
if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */
|
||
ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */
|
||
r = STOP_INVDLN; } }
|
||
CRETIOE (iochk || !ind[IN_DSK], r); /* return status */
|
||
}
|
||
|
||
/* Read or compare address with memory */
|
||
|
||
t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
|
||
{
|
||
int32 i;
|
||
uint8 ac;
|
||
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 */
|
||
{ 100000, 10000, 1000, 100, 10, 1} ;
|
||
|
||
for (i = 0; i < DP_ADDR; i++) { /* copy address */
|
||
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
|
||
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
|
||
return STOP_INVDLN; }
|
||
if (zad) { /* addr zero? */
|
||
ac = sec / dec_tab[i]; /* get addr digit */
|
||
sec = sec % dec_tab[i]; /* get remainder */
|
||
ac = bcd_to_bin[ac]; } /* cvt to BCD */
|
||
else ac = *ap; /* addr char */
|
||
if (qwc) { /* wr chk? skip if zad */
|
||
if (!zad && (flg? (M[BS] != ac): /* L? cmp with WM */
|
||
((M[BS] & CHAR) != (ac & CHAR)))) { /* M? cmp w/o WM */
|
||
ind[IN_DPW] = ind[IN_DSK] = 1;
|
||
return STOP_WRCHKE; } }
|
||
else if (flg) M[BS] = ac & CHAR; /* load mode */
|
||
else M[BS] = (M[BS] & WM) | (ac & CHAR); /* move mode */
|
||
ap++; BS++; /* adv ptrs */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Read or compare data with memory */
|
||
|
||
t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
|
||
{
|
||
int32 i, lim;
|
||
int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */
|
||
|
||
lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
|
||
for (i = 0; i < lim; i++) { /* copy data */
|
||
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
|
||
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
|
||
return STOP_INVDLN; }
|
||
if (qwc) { /* write check? */
|
||
if (flg? (M[BS] != *ap): /* load mode cmp */
|
||
((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */
|
||
ind[IN_DPW] = ind[IN_DSK] = 1; /* error */
|
||
return STOP_WRCHKE; } }
|
||
else if (flg) M[BS] = *ap & (WM | CHAR); /* load mode */
|
||
else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */
|
||
ap++; BS++; /* adv ptrs */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Write address to disk */
|
||
|
||
t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg)
|
||
{
|
||
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 */
|
||
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
|
||
dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */
|
||
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
|
||
return STOP_INVDLN; }
|
||
if (flg) *ap = M[BS] & (WM | CHAR); /* L? copy WM */
|
||
else *ap = M[BS] & CHAR; /* M? strip WM */
|
||
if (da >= uptr->hwmark) uptr->hwmark = da + 1;
|
||
da++; ap++; BS++; /* adv ptrs */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Write data to disk */
|
||
|
||
t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg)
|
||
{
|
||
int32 i, lim;
|
||
uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */
|
||
uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */
|
||
|
||
lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
|
||
for (i = 0; i < lim; i++) { /* copy data */
|
||
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
|
||
dp_fill (uptr, da, DP_DATA - i); /* fill, set err */
|
||
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
|
||
return STOP_INVDLN; }
|
||
if (flg) *ap = M[BS] & (WM | CHAR); /* load, copy WM */
|
||
else *ap = M[BS] & CHAR; /* move, strip WM */
|
||
if (da >= uptr->hwmark) uptr->hwmark = da + 1;
|
||
da++; ap++; BS++; /* adv ptrs */
|
||
if (ADDR_ERR (BS)) return STOP_WRAP; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Find sector */
|
||
|
||
int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf)
|
||
{
|
||
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 i;
|
||
|
||
if (dp_zeroad (ap)) return psec; /* addr zero? ok */
|
||
if (dp_cmp_ad (ap, dcf)) return psec; /* addr comp? ok */
|
||
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 */
|
||
if (dp_cmp_ad (ap, dcf)) return psec; } /* match? */
|
||
ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */
|
||
return -1;
|
||
}
|
||
|
||
/* Find next sector - must be sequential, cannot cross cylinder boundary */
|
||
|
||
t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf)
|
||
{
|
||
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 */
|
||
|
||
if (ctrk) { /* not trk zero? */
|
||
if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */
|
||
if (dp_cmp_ad (ap, dcf)) return SCPE_OK; } /* addr comp? ok */
|
||
ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */
|
||
return STOP_INVDAD;
|
||
}
|
||
|
||
/* 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 & CHAR) return FALSE; } /* nonzero? lose */
|
||
return TRUE; /* all zeroes */
|
||
}
|
||
|
||
/* Compare disk address to memory sector address - always omit word marks */
|
||
|
||
t_bool dp_cmp_ad (uint8 *ap, int32 dcf)
|
||
{
|
||
int32 i;
|
||
uint8 c;
|
||
|
||
for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
|
||
c = M[dcf + DCF_SEC + i]; /* sector addr char */
|
||
if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */
|
||
return FALSE; }
|
||
return TRUE; /* compare ok */
|
||
}
|
||
|
||
/* 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 (int32 ad, int32 len)
|
||
{
|
||
uint8 c;
|
||
int32 r;
|
||
|
||
for (r = 0; len > 0; len--) { /* loop thru char */
|
||
c = M[ad] & DIGIT; /* get digit */
|
||
if ((c == 0) || (c > BCD_ZERO)) return -1; /* invalid? */
|
||
r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */
|
||
ad++; } /* next digit */
|
||
return r;
|
||
}
|
||
|
||
/* Convert binary to DCF BCD field */
|
||
|
||
void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg)
|
||
{
|
||
int32 r;
|
||
|
||
for ( ; len > 0; len--) { /* loop thru char */
|
||
r = val % 10; /* get digit */
|
||
if (flg) M[ad + len - 1] = bin_to_bcd[r]; /* load mode? */
|
||
else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r];
|
||
val = val / 10; }
|
||
return;
|
||
}
|
||
|
||
/* Get and validate count */
|
||
|
||
int32 dp_get_cnt (int32 dcf)
|
||
{
|
||
int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */
|
||
if (cnt < 0) return -1; /* bad count? */
|
||
if (cnt == 0) return 1000; /* 0 => 1000 */
|
||
return cnt;
|
||
}
|
||
|
||
/* Fill sector buffer with blanks */
|
||
|
||
void dp_fill (UNIT *uptr, uint32 da, int32 cnt)
|
||
{
|
||
while (cnt-- > 0) { /* fill with blanks */
|
||
*(((uint8 *) uptr->filebuf) + da) = BCD_BLANK;
|
||
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 */
|
||
dp_lastf = 0; /* clear state */
|
||
ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
|
||
ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
|
||
sim_cancel (&dp_unit[0]); /* cancel timer */
|
||
return SCPE_OK;
|
||
}
|