/* i7094_dsk.c: 7631 file control (disk/drum) simulator | |
Copyright (c) 2005-2006, 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. | |
dsk 7631 file control | |
The 7631 is a controller for multiple serial bit stream devices such as | |
disks or drums. It supports the | |
1301 fixed disk | |
1302 fixed disk | |
2302 fixed disk | |
7320 drum | |
The 7631 supports variable record formatting, user-specified record numbering, | |
and other complex features. Each track has | |
home address 1: the track number, 4 BCD digits (implicit) | |
home address 2: user-specified track identifier, 6 BCD chars | |
records 1..n: variably formatted records, each consisting of | |
record address: user-specified record identifier, 4 BCD digits | |
and 2 BCD characters | |
record data: 36b words | |
To deal with this, the simulator provides a container of 500 (7320/1301) or | |
1000 (1302/2302) words per track. Each track starts with home address 2 | |
and then contains a variable number of records. Each record has a two-word | |
header followed by data: | |
word 0: record length without header | |
word 1: record address | |
word 2: start of data | |
word 2+n-1: end of data | |
word 2+n+2: start of next record | |
A record length of 0 indicates end of valid data on the track. | |
Orders to the 7631 are 10 BCD digits (60b), consisting of two words: | |
word 0: op-op-access-module-d1-d2 | |
word 1: d3-d4-d5-d6-x-x | |
Depending on the opcode, d1:d6 can be a track number plus home address 2, | |
or a record number. | |
Status from the 7631 is also 10 BCD digits (60b), with 36b in the first | |
word, and 24b (plus 12b of zeroes) in the second. | |
Because modules can have two access arms that seek independently, each | |
module m is represented by two units: unit m for access 0 and unit m+10 | |
for access 1. This requires tricky bookkeeping to be sure that the | |
service routine is using the 'right' unit. | |
Limitations of the simulation: | |
- HA2 and record address must be exactly 6 characters (one word) | |
- Record lengths must be exact multiples of 6 characters | |
- Seek timing is fixed rather than based on seek length | |
*/ | |
/* Definitions */ | |
#include "i7094_defs.h" | |
#include <math.h> | |
#define DSK_NUMDR 10 /* modules/controller */ | |
#define DSK_SNS (2 * DSK_NUMDR) /* dummy unit for sense */ | |
/* Drive geometry */ | |
#define DSK_WDSPT_7320 500 /* words/track */ | |
#define DSK_WDSPT_1301 500 | |
#define DSK_WDSPT_1302 1000 | |
#define DSK_WDSPT_2302 1000 | |
#define DSK_TRKPC_7320 400 /* tracks/cylinder */ | |
#define DSK_TRKPC_1301 40 | |
#define DSK_TRKPC_1302 40 | |
#define DSK_TRKPC_2302 40 | |
#define DSK_CYLPA_7320 1 /* cylinders/access */ | |
#define DSK_CYLPA_1301 250 | |
#define DSK_CYLPA_1302 250 | |
#define DSK_CYLPA_2302 250 | |
#define DSK_TRKPA_7320 (DSK_TRKPC_7320*DSK_CYLPA_7320) /* tracks/access */ | |
#define DSK_TRKPA_1301 (DSK_TRKPC_1301*DSK_CYLPA_1301) | |
#define DSK_TRKPA_1302 (DSK_TRKPC_1302*DSK_CYLPA_1302) | |
#define DSK_TRKPA_2302 (DSK_TRKPC_2302*DSK_CYLPA_2302) | |
#define DSK_ACCPM_7320 1 /* access/module */ | |
#define DSK_ACCPM_1301 1 | |
#define DSK_ACCPM_1302 2 | |
#define DSK_ACCPM_2302 2 | |
#define DSK_FMCPT_7320 2868 /* format chars/track */ | |
#define DSK_FMCPT_1301 2868 | |
#define DSK_FMCPT_1302 5942 | |
#define DSK_FMCPT_2302 5942 | |
#define SIZE_7320 (DSK_WDSPT_7320*DSK_TRKPA_7320*DSK_ACCPM_7320) | |
#define SIZE_1301 (DSK_WDSPT_1301*DSK_TRKPA_1301*DSK_ACCPM_1301) | |
#define SIZE_1302 (DSK_WDSPT_1302*DSK_TRKPA_1302*DSK_ACCPM_1302) | |
#define SIZE_2302 (DSK_WDSPT_2302*DSK_TRKPA_2302*DSK_ACCPM_2302) | |
#define DSK_BUFSIZ (DSK_WDSPT_2302) | |
#define DSK_DA(a,t,d) (((((a) * dsk_tab[d].trkpa) + (t)) * dsk_tab[d].wdspt) *\ | |
sizeof (t_uint64)) | |
/* Unit flags */ | |
#define UNIT_V_INOP0 (UNIT_V_UF + 0) /* acc 0 inoperative */ | |
#define UNIT_V_INOP1 (UNIT_V_UF + 1) /* acc 1 inoperative */ | |
#define UNIT_V_FMTE (UNIT_V_UF + 2) /* format enabled */ | |
#define UNIT_V_TYPE (UNIT_V_UF + 3) /* drive type */ | |
#define UNIT_M_TYPE 03 | |
#define UNIT_INOP0 (1 << UNIT_V_INOP0) | |
#define UNIT_INOP1 (1 << UNIT_V_INOP1) | |
#define UNIT_FMTE (1 << UNIT_V_FMTE) | |
#define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE) | |
#define TYPE_7320 (0 << UNIT_V_TYPE) | |
#define TYPE_1301 (1 << UNIT_V_TYPE) | |
#define TYPE_1302 (2 << UNIT_V_TYPE) | |
#define TYPE_2302 (3 << UNIT_V_TYPE) | |
#define GET_DTYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE) | |
#define TRK u3 /* track */ | |
#define SKF u4 /* seek in progress */ | |
/* Track/record structure */ | |
#define THA2 0 /* home address 2 */ | |
#define HA2_MASK 0777700000000 /* two chars checked */ | |
#define T1STREC 1 /* start of records */ | |
#define RLNT 0 /* record length */ | |
#define RADDR 1 /* record address */ | |
#define RDATA 2 /* start of data */ | |
#define REC_MASK 0171717177777 /* 4 digits, 2 chars */ | |
/* Command word (60b) - 10 BCD digits */ | |
#define OP1 0 /* opcode */ | |
#define OP2 1 | |
#define ACC 2 /* access */ | |
#define MOD 3 /* module */ | |
#define T1 4 /* track */ | |
#define T2 5 | |
#define T3 6 | |
#define T4 7 | |
/* Disk states */ | |
#define DSK_IDLE 0 | |
/* Status word (60b) */ | |
#define DSKS_PCHK 004000000000000000000 /* prog check */ | |
#define DSKS_DCHK 002000000000000000000 /* data check */ | |
#define DSKS_EXCC 001000000000000000000 /* exc cond */ | |
#define DSKS_INVS 000200000000000000000 /* invalid seq */ | |
#define DSKS_INVC 000040000000000000000 /* invalid opcode */ | |
#define DSKS_FMTC 000020000000000000000 /* format check */ | |
#define DSKS_NRCF 000010000000000000000 /* no record found */ | |
#define DSKS_INVA 000002000000000000000 /* invalid address */ | |
#define DSKS_RSPC 000000400000000000000 /* response check */ | |
#define DSKS_CMPC 000000200000000000000 /* compare check */ | |
#define DSKS_PARC 000000100000000000000 /* parity check */ | |
#define DSKS_ACCI 000000020000000000000 /* access inoperative */ | |
#define DSKS_ACCN 000000004000000000000 /* access not ready */ | |
#define DSKS_DSKE 000000002000000000000 /* disk error */ | |
#define DSKS_FILE 000000001000000000000 /* file error */ | |
#define DSKS_6B 000000000040000000000 /* six bit mode */ | |
#define DSKS_ATN0 000000000002000000000 /* attention start */ | |
#define DSKS_PALL 000777000000000000000 | |
#define DSKS_DALL 000000740000000000000 | |
#define DSKS_EALL 000000037000000000000 | |
#define DSKS_ALLERR 007777777000000000000 | |
/* Commands - opcode 0 */ | |
#define DSKC_NOP 0x00 | |
#define DSKC_RLS 0x04 | |
#define DSKC_8B 0x08 | |
#define DSKC_6B 0x09 | |
/* Commands - opcode 8 */ | |
#define DSKC_SEEK 0x0 /* seek */ | |
#define DSKC_SREC 0x2 /* single record */ | |
#define DSKC_WFMT 0x3 /* write format */ | |
#define DSKC_TNOA 0x4 /* track no addr */ | |
#define DSKC_CYL 0x5 /* cyl no addr */ | |
#define DSKC_WCHK 0x6 /* write check */ | |
#define DSKC_ACCI 0x7 /* set acc inoperative */ | |
#define DSKC_TWIA 0x8 /* track with addr */ | |
#define DSKC_THA 0x9 /* track home addr */ | |
/* CTSS record structure */ | |
#define CTSS_HA2 0676767676767 /* =HXXXXXX */ | |
#define CTSS_RLNT 435 /* data record */ | |
#define CTSS_D1LNT 31 /* padding */ | |
#define CTSS_D2LNT 14 | |
#define CTSS_D3LNT 16 | |
#define CTSS_DLLNT 1 | |
#define CTSS_RA1 2 | |
#define CTSS_RA2 8 | |
/* Data and declarations */ | |
typedef struct { | |
char *name; | |
uint32 accpm; /* acc/module: 1 or 2 */ | |
uint32 wdspt; /* wds/track: 500 or 1000 */ | |
uint32 trkpc; /* trks/cyl: 1 or 40 */ | |
uint32 trkpa; /* trks/acc: 400 or 10000 */ | |
uint32 fchpt; /* format ch/track */ | |
uint32 size; | |
} DISK_TYPE; | |
const DISK_TYPE dsk_tab[4] = { | |
{ "7320", DSK_ACCPM_7320, DSK_WDSPT_7320, | |
DSK_TRKPC_7320, DSK_TRKPA_7320, DSK_FMCPT_7320, SIZE_7320 }, | |
{ "1301", DSK_ACCPM_1301, DSK_WDSPT_1301, | |
DSK_TRKPC_1301, DSK_TRKPA_1301, DSK_FMCPT_1301, SIZE_1301 }, | |
{ "1302", DSK_ACCPM_1302, DSK_WDSPT_1302, | |
DSK_TRKPC_1302, DSK_TRKPA_1302, DSK_FMCPT_1302, SIZE_1302 }, | |
{ "2302", DSK_ACCPM_2302, DSK_WDSPT_2302, | |
DSK_TRKPC_2302, DSK_TRKPA_2302, DSK_FMCPT_2302, SIZE_2302 } | |
}; | |
/* 7320/1301 format track characters */ | |
uint8 fmt_thdr_7320[] = { | |
4, 4, 4, /* gap 1 */ | |
3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */ | |
4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */ | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */ | |
}; | |
uint8 fmt_rhdr_7320[] = { | |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */ | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */ | |
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */ | |
1, 1, 1, 1, 0 /* record ovhd */ | |
}; | |
/* 1302/2302 format track characters */ | |
uint8 fmt_thdr_1302[] = { | |
4, 4, 4, 4, 4, 4, /* gap 1 */ | |
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */ | |
4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */ | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */ | |
}; | |
uint8 fmt_rhdr_1302[] = { | |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */ | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */ | |
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */ | |
1, 1, 1, 1, 1, 1, 1, 0 /* record ovhd */ | |
}; | |
/* CTSS 7320/1301 track format table */ | |
uint32 ctss_fmt_7320[] = { | |
CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0 | |
}; | |
/* CTSS 1302/2302 track format table */ | |
uint32 ctss_fmt_1302[] = { | |
CTSS_RLNT, CTSS_D1LNT, CTSS_D2LNT, | |
CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0 | |
}; | |
uint32 dsk_ch = CH_C; /* disk channel */ | |
uint32 dsk_acc = 0; /* access */ | |
uint32 dsk_mod = 0; /* module */ | |
uint32 dsk_sta = 0; /* disk state */ | |
uint32 dsk_mode = 0; /* I/O mode */ | |
uint32 dsk_wchk = 0; /* write check flag */ | |
uint32 dsk_ctime = 10; /* command time */ | |
uint32 dsk_stime = 1000; /* seek time */ | |
uint32 dsk_rtime = 100; /* rotational latency */ | |
uint32 dsk_wtime = 2; /* word time */ | |
uint32 dsk_gtime = 5; /* gap time */ | |
uint32 dsk_rbase = 0; /* record tracking */ | |
uint32 dsk_rptr = 0; | |
uint32 dsk_rlim = 0; | |
uint32 dsk_stop = 0; | |
uint32 dsk_fmt_cntr = 0; /* format counter */ | |
t_uint64 dsk_rec = 0; /* rec/home addr (36b) */ | |
t_uint64 dsk_sns = 0; /* sense data (60b) */ | |
t_uint64 dsk_cmd = 0; /* BCD command (60b) */ | |
t_uint64 dsk_chob = 0; /* chan output buffer */ | |
uint32 dsk_chob_v = 0; /* valid */ | |
t_uint64 dsk_buf[DSK_BUFSIZ]; /* transfer buffer */ | |
extern uint32 ch_req; | |
t_stat dsk_svc (UNIT *uptr); | |
t_stat dsk_svc_sns (UNIT *uptr); | |
t_stat dsk_reset (DEVICE *dptr); | |
t_stat dsk_attach (UNIT *uptr, char *cptr); | |
t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit); | |
t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 flags); | |
t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd); | |
t_stat dsk_uend (uint32 ch, t_uint64 stat); | |
t_stat dsk_recad (uint32 trk, uint32 rec, uint32 acc, uint32 mod, t_uint64 *res); | |
t_uint64 dsk_acc_atn (uint32 u); | |
t_stat dsk_init_trk (UNIT *udptr, uint32 trk); | |
t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp); | |
t_stat dsk_wr_trk (UNIT *uptr, uint32 trk); | |
t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc); | |
t_bool dsk_qdone (uint32 ch); | |
t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc); | |
/* DSK data structures | |
dsk_dev DSK device descriptor | |
dsk_unit DSK unit descriptor | |
dsk_reg DSK register list | |
*/ | |
DIB dsk_dib = { &dsk_chsel, &dsk_chwr }; | |
UNIT dsk_unit[] = { | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
TYPE_7320, SIZE_7320) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
UNIT_DIS+TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
UNIT_DIS+TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
UNIT_DIS+TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
UNIT_DIS+TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ | |
UNIT_DIS+TYPE_2302, SIZE_2302) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc, UNIT_DIS, 0) }, | |
{ UDATA (&dsk_svc_sns, UNIT_DIS, 0) } | |
}; | |
REG dsk_reg[] = { | |
{ ORDATA (STATE, dsk_sta, 6) }, | |
{ ORDATA (ACCESS, dsk_acc, 1) }, | |
{ ORDATA (MODULE, dsk_mod, 4) }, | |
{ ORDATA (RECORD, dsk_rec, 36) }, | |
{ ORDATA (MODE, dsk_mode, 4) }, | |
{ ORDATA (SENSE, dsk_sns, 60) }, | |
{ ORDATA (BCDCMD, dsk_cmd, 60) }, | |
{ ORDATA (CHOB, dsk_chob, 36) }, | |
{ FLDATA (CHOBV, dsk_chob_v, 0) }, | |
{ FLDATA (STOP, dsk_stop, 0) }, | |
{ DRDATA (FCNTR, dsk_fmt_cntr, 13) }, | |
{ BRDATA (BUF, dsk_buf, 8, 36, DSK_BUFSIZ) }, | |
{ DRDATA (RBASE, dsk_rbase, 10), REG_RO }, | |
{ DRDATA (RPTR, dsk_rptr, 10), REG_RO }, | |
{ DRDATA (RLIM, dsk_rlim, 10), REG_RO }, | |
{ DRDATA (CHAN, dsk_ch, 3), REG_HRO }, | |
{ DRDATA (STIME, dsk_stime, 24), REG_NZ + PV_LEFT }, | |
{ DRDATA (RTIME, dsk_rtime, 24), REG_NZ + PV_LEFT }, | |
{ DRDATA (WTIME, dsk_wtime, 24), REG_NZ + PV_LEFT }, | |
{ DRDATA (GTIME, dsk_gtime, 24), REG_NZ + PV_LEFT }, | |
{ DRDATA (CTIME, dsk_ctime, 24), REG_NZ + PV_LEFT }, | |
{ URDATA (TRACK, dsk_unit[0].TRK, 10, 14, 0, | |
2 * DSK_NUMDR, PV_LEFT) }, | |
{ URDATA (SEEKF, dsk_unit[0].SKF, 10, 1, 0, | |
2 * DSK_NUMDR, PV_LEFT | REG_HRO) }, | |
{ URDATA (CAPAC, dsk_unit[0].capac, 10, T_ADDR_W, 0, | |
DSK_NUMDR, PV_LEFT | REG_HRO) }, | |
{ NULL } | |
}; | |
MTAB dsk_mtab[] = { | |
{ UNIT_INOP0 + UNIT_INOP1, 0, "operational", "OPERATIONAL" }, | |
{ UNIT_INOP0 + UNIT_INOP1, UNIT_INOP0, "access 0 inoperative", NULL }, | |
{ UNIT_INOP0 + UNIT_INOP1, UNIT_INOP1, "access 1 inoperative", NULL }, | |
{ UNIT_FMTE, UNIT_FMTE, "formating enabled", "FORMAT" }, | |
{ UNIT_FMTE, 0, "formating disabled", "NOFORMAT" }, | |
{ UNIT_TYPE, TYPE_7320, "7320", "7320", &dsk_set_size }, | |
{ UNIT_TYPE, TYPE_1301, "1301", "1301", &dsk_set_size }, | |
{ UNIT_TYPE, TYPE_1302, "1302", "1302", &dsk_set_size }, | |
{ UNIT_TYPE, TYPE_2302, "2302", "2302", &dsk_set_size }, | |
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, | |
NULL, &ch_show_chan, NULL }, | |
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "FORMAT", NULL, | |
NULL, &dsk_show_format, NULL }, | |
{ 0 } | |
}; | |
DEVICE dsk_dev = { | |
"DSK", dsk_unit, dsk_reg, dsk_mtab, | |
(DSK_NUMDR * 2) + 1, 10, 24, 1, 8, 36, | |
NULL, NULL, &dsk_reset, | |
NULL, &dsk_attach, NULL, | |
&dsk_dib, DEV_DIS | |
}; | |
/* Disk channel select, from 7909 channel program */ | |
t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit) | |
{ | |
uint32 u; | |
dsk_ch = ch; | |
if (dsk_sta != DSK_IDLE) dsk_uend (ch, DSKS_INVS); /* not idle? seq check */ | |
switch (sel) { | |
case CHSL_CTL: /* control */ | |
ch_req |= REQ_CH (ch); /* request channel */ | |
break; | |
case CHSL_SNS: /* sense */ | |
if (sim_is_active (&dsk_unit[DSK_SNS])) /* already sensing? */ | |
return dsk_uend (ch, DSKS_INVS); /* sequence error */ | |
sim_activate (&dsk_unit[DSK_SNS], dsk_ctime); /* set timer */ | |
dsk_stop = 0; | |
break; | |
case CHSL_RDS: /* read */ | |
if (dsk_mode == DSKC_WFMT) /* write format? */ | |
return dsk_uend (ch, DSKS_INVS); /* sequence error */ | |
case CHSL_WRS: /* write */ | |
if (dsk_mode == 0) dsk_uend (ch, DSKS_INVS); /* no mode? seq check */ | |
if (dsk_mode == DSKC_WFMT) sel = CHSL_FMT; /* format? fake sel */ | |
u = (dsk_acc * DSK_NUMDR) + dsk_mod; /* access unit number */ | |
if (sim_is_active (&dsk_unit[u])) /* access in use? */ | |
return dsk_uend (ch, DSKS_ACCN); /* access not ready */ | |
sim_activate (&dsk_unit[u], dsk_rtime); /* rotational time */ | |
break; | |
default: /* other */ | |
return STOP_ILLIOP; | |
} | |
dsk_sta = sel; /* set new state */ | |
return SCPE_OK; | |
} | |
/* Disk channel write, from 7909 channel program */ | |
t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 stopf) | |
{ | |
if (stopf) dsk_stop = 1; /* stop? */ | |
else { | |
val = val & DMASK; | |
switch (dsk_sta) { /* case on state */ | |
case CHSL_CTL: /* control */ | |
dsk_cmd = val << 24; | |
if (val & 0100000000000) { /* need 2nd word? */ | |
ch_req |= REQ_CH (ch); /* req ch for 2nd */ | |
dsk_sta = CHSL_CTL|CHSL_2ND; /* next state */ | |
return SCPE_OK; | |
} | |
return dsk_new_cmd (ch, dsk_cmd); /* no, do cmd */ | |
case CHSL_CTL|CHSL_2ND: /* 2nd control */ | |
dsk_cmd |= (val >> 12); | |
return dsk_new_cmd (ch, dsk_cmd); /* do cmd */ | |
default: | |
dsk_chob = val; /* store data */ | |
dsk_chob_v = 1; /* set valid */ | |
} | |
} | |
return SCPE_OK; | |
} | |
/* New command - end of CTL sequence */ | |
t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd) | |
{ | |
uint32 i, d, a, m, u, trk, dtyp, bcd[8]; | |
ch_req |= REQ_CH (ch); /* req ch for end */ | |
ch9_set_end (ch, 0); /* set end flag */ | |
dsk_sta = DSK_IDLE; /* ctrl is idle */ | |
for (i = 0; i < 8; i++) { /* get chars from cmd */ | |
d = (uint32) (cmd >> (6 * (9 - i))) & BCD_MASK; | |
if (d == BCD_ZERO) d = 0; | |
else if (d == 0) d = BCD_ZERO; /* BCD zero cvt */ | |
bcd[i] = d; | |
} | |
if (bcd[OP1] == 0) { /* cmd = 0x? */ | |
switch (bcd[OP2]) { /* case on x */ | |
case DSKC_NOP: /* nop */ | |
case DSKC_RLS: /* release */ | |
break; | |
case DSKC_8B: /* 8b mode */ | |
dsk_sns &= ~DSKS_6B; | |
break; | |
case DSKC_6B: /* 6b mode */ | |
dsk_sns |= DSKS_6B; | |
break; | |
default: /* unknown */ | |
return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ | |
} /* end case op2 */ | |
return SCPE_OK; | |
} /* end if */ | |
else if (bcd[OP1] == 8) { /* cmd = 8x? */ | |
a = bcd[ACC]; /* get access, */ | |
m = bcd[MOD]; /* module */ | |
u = (a * DSK_NUMDR) + m; /* unit for access */ | |
if ((m > DSK_NUMDR) || /* invalid module? */ | |
(dsk_unit[m].flags & UNIT_DIS)) /* disabled module? */ | |
return dsk_uend (ch, DSKS_ACCI); | |
dtyp = GET_DTYPE (dsk_unit[m].flags); /* get drive type */ | |
if ((a >= dsk_tab[dtyp].accpm) || /* invalid access? */ | |
(dsk_unit[m].flags & (UNIT_INOP0 << a))) /* access inop? */ | |
return dsk_uend (ch, DSKS_ACCI); | |
if ((bcd[T1] > 9) || (bcd[T2] > 9) || (bcd[T3] > 9) || (bcd[T4] > 9)) | |
trk = dsk_tab[dtyp].trkpa + 1; /* bad track */ | |
else trk = (((((bcd[T1] * 10) + bcd[T2]) * 10) + bcd[T3]) * 10) + bcd[T4]; | |
if (bcd[OP2] == DSKC_WCHK) { /* write check */ | |
if (dsk_mode == 0) /* no prior operation? */ | |
return dsk_uend (ch, DSKS_INVS); | |
bcd[OP2] = dsk_mode; /* use prior mode */ | |
dsk_wchk = 1; /* set write check */ | |
} | |
else dsk_wchk = 0; | |
dsk_sns &= ~(DSKS_ALLERR | dsk_acc_atn (u)); /* clear err, atn */ | |
dsk_stop = 0; /* clear stop */ | |
switch (bcd[OP2]) { | |
case DSKC_SEEK: /* seek */ | |
if ((trk >= dsk_tab[dtyp].trkpa) && /* inv track? */ | |
((dtyp == TYPE_7320) || /* drum or not CE? */ | |
(bcd[T1] > 9) || (bcd[T2] != BCD_AT) || | |
(bcd[T3] > 9) || (bcd[T4] > 9))) | |
return dsk_uend (ch, DSKS_INVA); | |
if (sim_is_active (&dsk_unit[u])) /* selected acc busy? */ | |
return dsk_uend (ch, DSKS_ACCN); | |
dsk_unit[u].SKF = 1; /* set seeking flag */ | |
dsk_unit[u].TRK = trk; /* sel acc on cyl */ | |
sim_activate (&dsk_unit[u], dsk_stime); /* seek */ | |
dsk_mode = 0; /* clear I/O mode */ | |
return SCPE_OK; | |
case DSKC_ACCI: /* access inoperative */ | |
dsk_unit[m].flags |= (UNIT_INOP0 << a); /* set correct flag */ | |
dsk_mode = 0; /* clear I/O mode */ | |
return SCPE_OK; | |
case DSKC_SREC: /* single record */ | |
break; /* no verification */ | |
case DSKC_WFMT: /* format */ | |
if (!(dsk_unit[m].flags & UNIT_FMTE)) /* format enabled? */ | |
return dsk_uend (ch, DSKS_FMTC); /* no, error */ | |
case DSKC_TNOA: /* track no addr */ | |
case DSKC_CYL: /* cyl no addr */ | |
case DSKC_TWIA: /* track with addr */ | |
case DSKC_THA: /* track home addr */ | |
if (trk != (uint32) dsk_unit[u].TRK) /* on track? */ | |
return dsk_uend (ch, DSKS_NRCF); | |
break; | |
default: | |
return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ | |
} | |
dsk_acc = a; /* save access */ | |
dsk_mod = m; /* save module */ | |
dsk_rec = cmd & DMASK; /* save rec/home addr */ | |
dsk_mode = bcd[OP2]; /* save mode */ | |
return SCPE_OK; | |
} | |
return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ | |
} | |
/* Sense unit service */ | |
t_stat dsk_svc_sns (UNIT *uptr) | |
{ | |
t_uint64 dat; | |
switch (dsk_sta) { /* case on state */ | |
case CHSL_SNS: /* prepare data */ | |
dsk_buf[0] = (dsk_sns >> 24) & DMASK; /* buffer is 2 words */ | |
dsk_buf[1] = (dsk_sns << 12) & DMASK; | |
dsk_rptr = 0; | |
dsk_rlim = 2; | |
dsk_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */ | |
break; | |
case CHSL_SNS|CHSL_2ND: /* second state */ | |
if (dsk_rptr >= dsk_rlim) { /* end of buffer? */ | |
ch9_set_end (dsk_ch, 0); /* set end */ | |
ch_req |= REQ_CH (dsk_ch); /* request channel */ | |
dsk_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */ | |
sim_activate (uptr, dsk_ctime); /* longer wait */ | |
return SCPE_OK; | |
} | |
dat = dsk_buf[dsk_rptr++]; /* get word */ | |
if (!dsk_stop) ch9_req_rd (dsk_ch, dat); /* send wd to chan */ | |
break; | |
case CHSL_SNS|CHSL_3RD: /* 3rd state */ | |
if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */ | |
dsk_sta = CHSL_SNS; /* repeat sequence */ | |
break; | |
} | |
sim_activate (uptr, dsk_wtime); /* sched next */ | |
return SCPE_OK; | |
} | |
/* Seek, read, write unit service */ | |
t_stat dsk_svc (UNIT *uaptr) | |
{ | |
uint32 i, dtyp, trk; | |
uint8 fc, *format; | |
t_uint64 rdat; | |
UNIT *udptr; | |
t_stat r; | |
if (uaptr->SKF) { /* seeking? */ | |
uint32 u = uaptr - dsk_dev.units; /* get unit */ | |
uaptr->SKF = 0; /* seek done */ | |
dsk_sns |= dsk_acc_atn (u); /* set atn bit */ | |
ch9_set_atn (dsk_ch); /* set atn flag */ | |
return SCPE_OK; | |
} | |
udptr = dsk_dev.units + dsk_mod; /* data unit */ | |
if (udptr->flags & (UNIT_INOP0 << dsk_acc)) /* acc inoperative? */ | |
return dsk_uend (dsk_ch, DSKS_ACCI); /* error */ | |
if ((udptr->flags & UNIT_ATT) == 0) { /* not attached? */ | |
dsk_uend (dsk_ch, DSKS_ACCI); /* error */ | |
return SCPE_UNATT; | |
} | |
dtyp = GET_DTYPE (udptr->flags); /* get data drive type */ | |
trk = uaptr->TRK; /* get access track */ | |
switch (dsk_sta) { /* case on state */ | |
case CHSL_RDS: /* read start */ | |
if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */ | |
return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */ | |
} | |
dsk_sta = CHSL_RDS|CHSL_2ND; /* next state */ | |
break; | |
case CHSL_RDS|CHSL_2ND: /* read data transmit */ | |
if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */ | |
if (r != ERR_ENDRC) return r; /* error? */ | |
dsk_sta = CHSL_RDS|CHSL_3RD; /* next state */ | |
sim_activate (uaptr, dsk_gtime); /* gap time */ | |
return SCPE_OK; | |
} | |
rdat = dsk_buf[dsk_rptr++]; /* get word */ | |
if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */ | |
if (!dsk_stop) ch9_req_rd (dsk_ch, rdat); /* give to channel */ | |
break; | |
case CHSL_RDS|CHSL_3RD: /* read end rec/trk */ | |
if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */ | |
dsk_sta = CHSL_RDS; /* repeat sequence */ | |
break; | |
case CHSL_WRS: /* write start */ | |
if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */ | |
return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */ | |
} | |
ch_req |= REQ_CH (dsk_ch); /* first request */ | |
dsk_sta = CHSL_WRS|CHSL_2ND; /* next state */ | |
dsk_chob = 0; /* clr, inval buffer */ | |
dsk_chob_v = 0; | |
break; | |
case CHSL_WRS|CHSL_2ND: /* write data transmit */ | |
if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */ | |
else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */ | |
if (dsk_wchk) { /* write check? */ | |
if (dsk_buf[dsk_rptr++] != dsk_chob) /* data mismatch? */ | |
return dsk_uend (dsk_ch, DSKS_CMPC); /* error */ | |
} | |
else dsk_buf[dsk_rptr++] = dsk_chob; /* write, store word */ | |
if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */ | |
if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */ | |
if (r != ERR_ENDRC) return r; /* error? */ | |
dsk_sta = CHSL_WRS|CHSL_3RD; /* next state */ | |
sim_activate (uaptr, dsk_gtime); /* gap time */ | |
return SCPE_OK; | |
} | |
if (!dsk_stop) ch_req |= REQ_CH (dsk_ch); /* more to do */ | |
break; | |
case CHSL_WRS|CHSL_3RD: /* write done */ | |
if (!dsk_wchk) { /* if write */ | |
if (r = dsk_wr_trk (udptr, trk)) return r; /* write track; err? */ | |
} | |
if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */ | |
dsk_sta = CHSL_WRS; /* repeat sequence */ | |
break; | |
/* Formatting takes place in five stages | |
1. Clear the track buffer, request the first word from the channel | |
2. Match characters against the fixed overhead (HA1, HA2, and gaps) | |
3. Match characters against the per-record overhead (RA and gaps) | |
4. Count the characters defining the record length | |
5. See if the next character is end or gap; if gap, return to stage 3 | |
This formating routine is not exact. It checks whether the format | |
will fit in the container, not whether the format would fit on a | |
real 7320, 1301, 1302, or 2302. */ | |
case CHSL_FMT: /* initialization */ | |
for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;/* clear track buf */ | |
dsk_rbase = T1STREC; /* init record ptr */ | |
dsk_rptr = 0; /* init format ptr */ | |
dsk_fmt_cntr = 0; /* init counter */ | |
ch_req |= REQ_CH (dsk_ch); /* request channel */ | |
dsk_sta = CHSL_FMT|CHSL_2ND; /* next state */ | |
dsk_chob = 0; /* clr, inval buffer */ | |
dsk_chob_v = 0; | |
break; | |
case CHSL_FMT|CHSL_2ND: /* match track header */ | |
if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) | |
format = fmt_thdr_7320; | |
else format = fmt_thdr_1302; | |
if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ | |
if (fc != format[dsk_rptr++]) /* mismatch? */ | |
return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ | |
if (format[dsk_rptr] == 0) { /* end format? */ | |
dsk_sta = CHSL_FMT|CHSL_3RD; /* next state */ | |
dsk_rptr = 0; /* reset format ptr */ | |
} | |
break; | |
case CHSL_FMT|CHSL_3RD: /* match record header */ | |
if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) | |
format = fmt_rhdr_7320; | |
else format = fmt_rhdr_1302; | |
if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ | |
if (fc != format[dsk_rptr++]) /* mismatch? */ | |
return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ | |
if (format[dsk_rptr] == 0) { /* end format? */ | |
dsk_sta = CHSL_FMT|CHSL_4TH; /* next state */ | |
dsk_rlim = 0; /* reset record ctr */ | |
} | |
break; | |
case CHSL_FMT|CHSL_4TH: /* count record size */ | |
if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ | |
if (fc == BCD_ONE) dsk_rlim++; /* more record? */ | |
else { | |
uint32 rsiz = dsk_rlim / 6; /* rec size words */ | |
if ((fc != BCD_TWO) || /* improper end? */ | |
(rsiz == 0) || /* smaller than min? */ | |
((dsk_rlim % 6) != 0) || /* not multiple of 6? */ | |
((dsk_rbase + rsiz + RDATA) >= dsk_tab[dtyp].wdspt)) | |
return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ | |
dsk_buf[dsk_rbase + RLNT] = rsiz; /* record rec lnt */ | |
dsk_rbase = dsk_rbase + rsiz + RDATA; /* new rec start */ | |
dsk_sta = CHSL_FMT|CHSL_5TH; /* next state */ | |
} | |
break; | |
case CHSL_FMT|CHSL_5TH: /* record or track end */ | |
if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ | |
if (fc == BCD_TWO) { /* back to record header? */ | |
dsk_rptr = 2; /* already done 2 chars */ | |
dsk_sta = CHSL_FMT|CHSL_3RD; /* record header state */ | |
} | |
else if (fc != BCD_ONE) dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ | |
else { | |
if (!dsk_wchk) { /* actual write? */ | |
trk = trk - (trk % dsk_tab[dtyp].trkpc); /* cyl start */ | |
for (i = 0; i < dsk_tab[dtyp].trkpc; i++) { /* do all tracks */ | |
if (r = dsk_wr_trk (udptr, trk + i)) /* wr track; err? */ | |
return r; | |
} | |
} | |
ch9_set_end (dsk_ch, 0); /* set end */ | |
ch_req |= REQ_CH (dsk_ch); /* request channel */ | |
dsk_sta = DSK_IDLE; /* disk is idle */ | |
return SCPE_OK; /* done */ | |
} | |
break; | |
default: | |
return SCPE_IERR; | |
} | |
sim_activate (uaptr, dsk_wtime); | |
return SCPE_OK; | |
} | |
/* Initialize data transfer | |
Inputs: | |
udptr = pointer to data unit | |
trk = track to read | |
Outputs: | |
dsk_buf contains track specified by trk | |
dsk_rbase, dsk_rptr, dsk_rlim are initialized | |
Errors: | |
SCPE_IOERR = I/O error (fatal, uend) | |
ERR_NRCF = no record found (HA2 or record number mismatch, uend) | |
STOP_INVFMT = invalid format (fatal, uend) | |
*/ | |
t_stat dsk_init_trk (UNIT *udptr, uint32 trk) | |
{ | |
uint32 k, da, dtyp, rlnt; | |
dtyp = GET_DTYPE (udptr->flags); /* get drive type */ | |
da = DSK_DA (dsk_acc, trk, dtyp); /* get disk address */ | |
sim_fseek (udptr->fileref, da, SEEK_SET); /* read track */ | |
k = sim_fread (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref); | |
if (ferror (udptr->fileref)) { /* error? */ | |
perror ("DSK I/O error"); | |
clearerr (udptr->fileref); | |
dsk_uend (dsk_ch, DSKS_DSKE); | |
return SCPE_IOERR; | |
} | |
for ( ; k < dsk_tab[dtyp].wdspt; k++) dsk_buf[k] = 0; /* zero fill */ | |
dsk_rbase = T1STREC; /* record base */ | |
rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ | |
dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */ | |
if ((rlnt == 0) || (dsk_rlim >= dsk_tab[dtyp].wdspt)) { /* invalid record? */ | |
dsk_uend (dsk_ch, DSKS_FMTC); | |
return STOP_INVFMT; | |
} | |
if (dsk_mode != DSKC_SREC) { /* not single record? */ | |
if (dsk_mode == DSKC_THA) dsk_rptr = 0; /* trk home addr? */ | |
else { | |
if (((dsk_rec << 24) ^ dsk_buf[THA2]) & HA2_MASK) { | |
dsk_uend (dsk_ch, DSKS_NRCF); /* invalid HA2 */ | |
return ERR_NRCF; | |
} | |
if (dsk_mode == DSKC_TWIA) /* track with addr? */ | |
dsk_rptr = dsk_rbase + RADDR; /* start at addr */ | |
else dsk_rptr = dsk_rbase + RDATA; /* else, at data */ | |
} | |
return SCPE_OK; | |
} | |
while (rlnt != 0) { /* until end track */ | |
dsk_rptr = dsk_rbase + RDATA; | |
if (((dsk_rec ^ dsk_buf[dsk_rbase + RADDR]) & REC_MASK) == 0) | |
return SCPE_OK; /* rec found? done */ | |
dsk_rbase = dsk_rlim; /* next record */ | |
rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ | |
dsk_rlim = dsk_rbase + rlnt + RDATA; /* limit */ | |
if (dsk_rlim >= dsk_tab[dtyp].wdspt) { /* invalid format? */ | |
dsk_uend (dsk_ch, DSKS_FMTC); | |
return STOP_INVFMT; | |
} | |
} | |
dsk_uend (dsk_ch, DSKS_NRCF); /* not found */ | |
return ERR_NRCF; | |
} | |
/* Check end of transfer | |
Inputs: | |
uptr = pointer to access unit | |
dtyp = drive type | |
Outputs: | |
ERR_ENDRC = end of record/track/cylinder, end sent, ch req if required | |
SCPE_OK = more to do, dsk_rbase, dsk_rptr, dsk_rlim may be updated | |
STOP_INVFMT = invalid format (fatal, uend sent) | |
*/ | |
t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp) | |
{ | |
uint32 rlnt; | |
if (dsk_rptr < dsk_rlim) return SCPE_OK; /* record done? */ | |
if (dsk_stop || !ch9_qconn (dsk_ch) || /* stop or err disc or */ | |
(dsk_mode == DSKC_SREC)) { /* single record? */ | |
ch9_set_end (dsk_ch, 0); /* set end */ | |
ch_req |= REQ_CH (dsk_ch); /* request channel */ | |
return ERR_ENDRC; | |
} | |
dsk_rbase = dsk_rlim; /* next record */ | |
rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ | |
dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */ | |
if ((dsk_rbase >= dsk_tab[dtyp].wdspt) || /* invalid format? */ | |
(dsk_rlim >= dsk_tab[dtyp].wdspt)) { | |
dsk_uend (dsk_ch, DSKS_FMTC); | |
return STOP_INVFMT; | |
} | |
if (rlnt) { /* more on track? */ | |
if ((dsk_mode == DSKC_THA) || (dsk_mode == DSKC_TWIA)) | |
dsk_rptr = dsk_rbase + RADDR; /* start with addr */ | |
else dsk_rptr = dsk_rbase + RDATA; /* or data */ | |
return SCPE_OK; | |
} | |
if (dsk_mode == DSKC_CYL) { /* cylinder mode? */ | |
uaptr->TRK = (uaptr->TRK + 1) % dsk_tab[dtyp].trkpa; /* incr track */ | |
if (uaptr->TRK % dsk_tab[dtyp].trkpc) /* not cyl end? */ | |
return ERR_ENDRC; /* don't set end */ | |
} | |
ch9_set_end (dsk_ch, 0); /* set end */ | |
ch_req |= REQ_CH (dsk_ch); /* request channel */ | |
return ERR_ENDRC; | |
} | |
/* Write track back to file */ | |
t_stat dsk_wr_trk (UNIT *udptr, uint32 trk) | |
{ | |
uint32 dtyp = GET_DTYPE (udptr->flags); | |
uint32 da = DSK_DA (dsk_acc, trk, dtyp); | |
sim_fseek (udptr->fileref, da, SEEK_SET); | |
sim_fwrite (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref); | |
if (ferror (udptr->fileref)) { | |
perror ("DSK I/O error"); | |
clearerr (udptr->fileref); | |
dsk_uend (dsk_ch, DSKS_DSKE); | |
return SCPE_IOERR; | |
} | |
return SCPE_OK; | |
} | |
/* Synthesize right attention bit from (access * 10 + module) */ | |
t_uint64 dsk_acc_atn (uint32 u) | |
{ | |
uint32 g, b; | |
g = u / 4; /* bit group */ | |
b = u % 4; /* bit within group */ | |
return (DSKS_ATN0 >> ((g * 6) + (b? b + 1: 0))); | |
} | |
/* Get next format character */ | |
t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc) | |
{ | |
uint32 cc = dsk_fmt_cntr % 6; | |
if (cc == 0) { /* start of word? */ | |
if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */ | |
else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */ | |
} | |
*fc = ((uint8) (dsk_chob >> ((5 - cc) * 6))) & 077; /* get character */ | |
if ((cc == 5) && !dsk_stop) ch_req |= REQ_CH (dsk_ch); /* end of word? */ | |
if (dsk_fmt_cntr++ >= dsk_tab[dtyp].fchpt) { /* track overflow? */ | |
dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/* Unusual end (set status and stop) */ | |
t_stat dsk_uend (uint32 ch, t_uint64 stat) | |
{ | |
dsk_sns |= stat; | |
dsk_sns &= ~(DSKS_PCHK|DSKS_DCHK|DSKS_EXCC); | |
if (dsk_sns & DSKS_PALL) dsk_sns |= DSKS_PCHK; | |
if (dsk_sns & DSKS_DALL) dsk_sns |= DSKS_DCHK; | |
if (dsk_sns & DSKS_EALL) dsk_sns |= DSKS_EXCC; | |
ch9_set_end (ch, CHINT_UEND); | |
ch_req |= REQ_CH (ch); | |
dsk_sta = DSK_IDLE; | |
return SCPE_OK; | |
} | |
/* Test for done */ | |
t_bool dsk_qdone (uint32 ch) | |
{ | |
if (dsk_stop || !ch9_qconn (ch)) { /* stop or err disc? */ | |
dsk_sta = DSK_IDLE; /* disk is idle */ | |
return TRUE; | |
} | |
return FALSE; | |
} | |
/* Reset */ | |
t_stat dsk_reset (DEVICE *dptr) | |
{ | |
uint32 i; | |
UNIT *uptr; | |
dsk_acc = 0; | |
dsk_mod = 0; | |
dsk_rec = 0; | |
dsk_mode = 0; | |
dsk_wchk = 0; | |
dsk_sns = 0; | |
dsk_cmd = 0; | |
dsk_sta = DSK_IDLE; | |
dsk_rbase = 0; | |
dsk_rptr = 0; | |
dsk_rlim = 0; | |
dsk_stop = 0; | |
dsk_fmt_cntr = 0; | |
dsk_chob = 0; | |
dsk_chob_v = 0; | |
for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0; | |
for (i = 0; i <= (2 * DSK_NUMDR); i++) { | |
uptr = dsk_dev.units + i; | |
sim_cancel (uptr); | |
uptr->TRK = 0; | |
uptr->SKF = 0; | |
} | |
return SCPE_OK; | |
} | |
/* Attach routine, test formating */ | |
t_stat dsk_attach (UNIT *uptr, char *cptr) | |
{ | |
uint32 dtyp = GET_DTYPE (uptr->flags); | |
t_stat r; | |
uptr->capac = dsk_tab[dtyp].size; | |
r = attach_unit (uptr, cptr); | |
if (r != SCPE_OK) return r; | |
uptr->TRK = 0; | |
uptr->SKF = 0; | |
uptr->flags &= ~(UNIT_INOP0|UNIT_INOP1); | |
return dsk_show_format (stdout, uptr, 0, NULL); | |
} | |
/* Set disk size */ | |
t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
uint32 dtyp = GET_DTYPE (val); | |
uint32 u = uptr - dsk_dev.units; | |
UNIT *u1; | |
if (u & 1) return SCPE_ARG; | |
u1 = dsk_dev.units + u + 1; | |
if ((uptr->flags & UNIT_ATT) || (u1->flags & UNIT_ATT)) | |
return SCPE_ALATT; | |
if (val == TYPE_7320) u1->flags = (u1->flags & ~UNIT_DISABLE) | UNIT_DIS; | |
else { | |
u1->flags = (u1->flags & ~UNIT_TYPE) | val | UNIT_DISABLE; | |
u1->capac = dsk_tab[dtyp].size; | |
} | |
uptr->capac = dsk_tab[dtyp].size; | |
return SCPE_OK; | |
} | |
/* Show format */ | |
t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
uint32 a, t, k, u, tlim, dtyp, da; | |
uint32 rptr, rlnt, rlim, rec, ctptr, *format; | |
uint32 minrsz = DSK_BUFSIZ; | |
uint32 maxrsz = 0; | |
uint32 minrno = DSK_BUFSIZ; | |
uint32 maxrno = 0; | |
t_bool ctss; | |
t_uint64 dbuf[DSK_BUFSIZ]; | |
DEVICE *dptr; | |
if (uptr == NULL) return SCPE_IERR; | |
if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; | |
dptr = find_dev_from_unit (uptr); | |
u = uptr - dptr->units; | |
if (dptr == NULL) return SCPE_IERR; | |
dtyp = GET_DTYPE (uptr->flags); | |
if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) | |
format = ctss_fmt_7320; | |
else format = ctss_fmt_1302; | |
for (a = 0, ctss = TRUE; a < dsk_tab[dtyp].accpm; a++) { | |
if (val) tlim = dsk_tab[dtyp].trkpa; | |
else tlim = 1; | |
for (t = 0; t < tlim; t++) { | |
da = DSK_DA (a, t, dtyp); /* get disk address */ | |
sim_fseek (uptr->fileref, da, SEEK_SET); /* read track */ | |
k = sim_fread (dbuf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, uptr->fileref); | |
if (ferror (uptr->fileref)) return SCPE_IOERR; /* error? */ | |
for ( ; k < dsk_tab[dtyp].wdspt; k++) dbuf[k] = 0; | |
rptr = T1STREC; | |
rlnt = (uint32) dbuf[rptr + RLNT]; | |
if (dbuf[THA2] != CTSS_HA2) ctss = FALSE; | |
if (rlnt == 0) { | |
if (a || t) fprintf (st, | |
"Unformatted track, unit = %d, access = %d, track = %d\n", u, a, t); | |
else fprintf (st, "Unit %d is unformatted\n", u); | |
return SCPE_OK; | |
} | |
for (rec = 0, ctptr = 0; rlnt != 0; rec++) { | |
if ((format[ctptr] == 0) || format[ctptr++] != rlnt) | |
ctss = FALSE; | |
rlim = rptr + rlnt + RDATA; | |
if (rlim >= dsk_tab[dtyp].wdspt) { | |
fprintf (st, "Invalid record length %d, unit = %d, access = %d, track = %d, record = %d\n", | |
rlnt, u, a, t, rec); | |
return SCPE_OK; | |
} | |
if (rlnt > maxrsz) maxrsz = rlnt; | |
if (rlnt < minrsz) minrsz = rlnt; | |
rptr = rlim; | |
rlnt = (uint32) dbuf[rptr + RLNT]; | |
} | |
if (format[ctptr] != 0) ctss = FALSE; | |
if (rec > maxrno) maxrno = rec; | |
if (rec < minrno) minrno = rec; | |
} | |
} | |
if (val == 0) return SCPE_OK; | |
if (ctss) fprintf (st, "CTSS format\n"); | |
else if ((minrno == maxrno) && (minrsz == maxrsz)) fprintf (st, | |
"Valid fixed format, records/track = %d, record size = %d\n", | |
minrno, minrsz); | |
else if (minrsz == maxrsz) fprintf (st, | |
"Valid variable format, records/track = %d-%d, record size = %d\n", | |
minrno, maxrno, minrsz); | |
else if (minrno == maxrno) fprintf (st, | |
"Valid variable format, records/track = %d, record sizes = %d-%d\n", | |
minrno, minrsz, maxrsz); | |
else fprintf (st, | |
"Valid variable format, records/track = %d-%d, record sizes = %d-%d\n", | |
minrno, maxrno, minrsz, maxrsz); | |
return SCPE_OK; | |
} |