/* i1401_dp.c: IBM 1311 disk simulator | |
Copyright (c) 2002-2005, 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; | |
} |