| /* i7094_dsk.c: 7631 file control (disk/drum) simulator | |
| Copyright (c) 2005-2008, 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) /* not idle? seq check */ | |
| dsk_uend (ch, DSKS_INVS); | |
| 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) /* no mode? seq check */ | |
| dsk_uend (ch, DSKS_INVS); | |
| if (dsk_mode == DSKC_WFMT) /* format? fake sel */ | |
| sel = CHSL_FMT; | |
| 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) /* stop? */ | |
| dsk_stop = 1; | |
| 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) /* BCD zero cvt */ | |
| d = BCD_ZERO; | |
| 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) /* send wd to chan */ | |
| ch9_req_rd (dsk_ch, dat); | |
| break; | |
| case CHSL_SNS|CHSL_3RD: /* 3rd state */ | |
| if (dsk_qdone (dsk_ch)) /* done? exit */ | |
| return SCPE_OK; | |
| 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) /* error? */ | |
| return r; | |
| 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) /* if THA, skip after HA */ | |
| dsk_rptr++; | |
| if (!dsk_stop) /* give to channel */ | |
| ch9_req_rd (dsk_ch, rdat); | |
| break; | |
| case CHSL_RDS|CHSL_3RD: /* read end rec/trk */ | |
| if (dsk_qdone (dsk_ch)) /* done? exit */ | |
| return SCPE_OK; | |
| 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) /* valid? clear */ | |
| dsk_chob_v = 0; | |
| else if (!dsk_stop) /* no, no stop? io chk */ | |
| ch9_set_ioc (dsk_ch); | |
| 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) /* if THA, skip after HA */ | |
| dsk_rptr++; | |
| if ((r = dsk_xfer_done (uaptr, dtyp))) { /* transfer done? */ | |
| if (r != ERR_ENDRC) /* error? */ | |
| return r; | |
| dsk_sta = CHSL_WRS|CHSL_3RD; /* next state */ | |
| sim_activate (uaptr, dsk_gtime); /* gap time */ | |
| return SCPE_OK; | |
| } | |
| if (!dsk_stop) /* more to do */ | |
| ch_req |= REQ_CH (dsk_ch); | |
| break; | |
| case CHSL_WRS|CHSL_3RD: /* write done */ | |
| if (!dsk_wchk) { /* if write */ | |
| if ((r = dsk_wr_trk (udptr, trk))) /* write track; err? */ | |
| return r; | |
| } | |
| if (dsk_qdone (dsk_ch)) /* done? exit */ | |
| return SCPE_OK; | |
| 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++) /* clear track buf */ | |
| dsk_buf[i] = 0; | |
| 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)) /* get fmt char; err? */ | |
| return SCPE_OK; | |
| 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)) /* get fmt char; err? */ | |
| return SCPE_OK; | |
| 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)) /* get fmt char; err? */ | |
| return SCPE_OK; | |
| if (fc == BCD_ONE) /* more record? */ | |
| dsk_rlim++; | |
| 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)) /* get fmt char; err? */ | |
| return SCPE_OK; | |
| 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) /* format check */ | |
| dsk_uend (dsk_ch, DSKS_FMTC); | |
| 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++) /* zero fill */ | |
| dsk_buf[k] = 0; | |
| 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) /* trk home addr? */ | |
| dsk_rptr = 0; | |
| 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) /* record done? */ | |
| return SCPE_OK; | |
| 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) /* valid? clear */ | |
| dsk_chob_v = 0; | |
| else if (!dsk_stop) /* no, no stop? io chk */ | |
| ch9_set_ioc (dsk_ch); | |
| } | |
| *fc = ((uint8) (dsk_chob >> ((5 - cc) * 6))) & 077; /* get character */ | |
| if ((cc == 5) && !dsk_stop) /* end of word? */ | |
| ch_req |= REQ_CH (dsk_ch); | |
| 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); | |
| if (dptr == NULL) | |
| return SCPE_IERR; | |
| u = uptr - dptr->units; | |
| 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)) /* error? */ | |
| return SCPE_IOERR; | |
| 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; | |
| } |