| 1 | /* i7094_dsk.c: 7631 file control (disk/drum) simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2005-2006, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | dsk 7631 file control\r |
| 27 | \r |
| 28 | The 7631 is a controller for multiple serial bit stream devices such as\r |
| 29 | disks or drums. It supports the\r |
| 30 | \r |
| 31 | 1301 fixed disk\r |
| 32 | 1302 fixed disk\r |
| 33 | 2302 fixed disk\r |
| 34 | 7320 drum\r |
| 35 | \r |
| 36 | The 7631 supports variable record formatting, user-specified record numbering,\r |
| 37 | and other complex features. Each track has\r |
| 38 | \r |
| 39 | home address 1: the track number, 4 BCD digits (implicit)\r |
| 40 | home address 2: user-specified track identifier, 6 BCD chars\r |
| 41 | records 1..n: variably formatted records, each consisting of\r |
| 42 | record address: user-specified record identifier, 4 BCD digits\r |
| 43 | and 2 BCD characters\r |
| 44 | record data: 36b words\r |
| 45 | \r |
| 46 | To deal with this, the simulator provides a container of 500 (7320/1301) or\r |
| 47 | 1000 (1302/2302) words per track. Each track starts with home address 2\r |
| 48 | and then contains a variable number of records. Each record has a two-word\r |
| 49 | header followed by data:\r |
| 50 | \r |
| 51 | word 0: record length without header\r |
| 52 | word 1: record address\r |
| 53 | word 2: start of data\r |
| 54 | word 2+n-1: end of data\r |
| 55 | word 2+n+2: start of next record\r |
| 56 | \r |
| 57 | A record length of 0 indicates end of valid data on the track.\r |
| 58 | \r |
| 59 | Orders to the 7631 are 10 BCD digits (60b), consisting of two words:\r |
| 60 | \r |
| 61 | word 0: op-op-access-module-d1-d2\r |
| 62 | word 1: d3-d4-d5-d6-x-x\r |
| 63 | \r |
| 64 | Depending on the opcode, d1:d6 can be a track number plus home address 2,\r |
| 65 | or a record number.\r |
| 66 | \r |
| 67 | Status from the 7631 is also 10 BCD digits (60b), with 36b in the first\r |
| 68 | word, and 24b (plus 12b of zeroes) in the second.\r |
| 69 | \r |
| 70 | Because modules can have two access arms that seek independently, each\r |
| 71 | module m is represented by two units: unit m for access 0 and unit m+10\r |
| 72 | for access 1. This requires tricky bookkeeping to be sure that the\r |
| 73 | service routine is using the 'right' unit.\r |
| 74 | \r |
| 75 | Limitations of the simulation:\r |
| 76 | \r |
| 77 | - HA2 and record address must be exactly 6 characters (one word)\r |
| 78 | - Record lengths must be exact multiples of 6 characters\r |
| 79 | - Seek timing is fixed rather than based on seek length\r |
| 80 | */\r |
| 81 | \r |
| 82 | /* Definitions */\r |
| 83 | \r |
| 84 | #include "i7094_defs.h"\r |
| 85 | #include <math.h>\r |
| 86 | \r |
| 87 | #define DSK_NUMDR 10 /* modules/controller */\r |
| 88 | #define DSK_SNS (2 * DSK_NUMDR) /* dummy unit for sense */\r |
| 89 | \r |
| 90 | /* Drive geometry */\r |
| 91 | \r |
| 92 | #define DSK_WDSPT_7320 500 /* words/track */\r |
| 93 | #define DSK_WDSPT_1301 500\r |
| 94 | #define DSK_WDSPT_1302 1000\r |
| 95 | #define DSK_WDSPT_2302 1000\r |
| 96 | #define DSK_TRKPC_7320 400 /* tracks/cylinder */\r |
| 97 | #define DSK_TRKPC_1301 40\r |
| 98 | #define DSK_TRKPC_1302 40\r |
| 99 | #define DSK_TRKPC_2302 40\r |
| 100 | #define DSK_CYLPA_7320 1 /* cylinders/access */\r |
| 101 | #define DSK_CYLPA_1301 250\r |
| 102 | #define DSK_CYLPA_1302 250\r |
| 103 | #define DSK_CYLPA_2302 250\r |
| 104 | #define DSK_TRKPA_7320 (DSK_TRKPC_7320*DSK_CYLPA_7320) /* tracks/access */\r |
| 105 | #define DSK_TRKPA_1301 (DSK_TRKPC_1301*DSK_CYLPA_1301)\r |
| 106 | #define DSK_TRKPA_1302 (DSK_TRKPC_1302*DSK_CYLPA_1302)\r |
| 107 | #define DSK_TRKPA_2302 (DSK_TRKPC_2302*DSK_CYLPA_2302)\r |
| 108 | #define DSK_ACCPM_7320 1 /* access/module */\r |
| 109 | #define DSK_ACCPM_1301 1\r |
| 110 | #define DSK_ACCPM_1302 2\r |
| 111 | #define DSK_ACCPM_2302 2\r |
| 112 | #define DSK_FMCPT_7320 2868 /* format chars/track */\r |
| 113 | #define DSK_FMCPT_1301 2868\r |
| 114 | #define DSK_FMCPT_1302 5942\r |
| 115 | #define DSK_FMCPT_2302 5942\r |
| 116 | #define SIZE_7320 (DSK_WDSPT_7320*DSK_TRKPA_7320*DSK_ACCPM_7320)\r |
| 117 | #define SIZE_1301 (DSK_WDSPT_1301*DSK_TRKPA_1301*DSK_ACCPM_1301)\r |
| 118 | #define SIZE_1302 (DSK_WDSPT_1302*DSK_TRKPA_1302*DSK_ACCPM_1302)\r |
| 119 | #define SIZE_2302 (DSK_WDSPT_2302*DSK_TRKPA_2302*DSK_ACCPM_2302)\r |
| 120 | #define DSK_BUFSIZ (DSK_WDSPT_2302)\r |
| 121 | #define DSK_DA(a,t,d) (((((a) * dsk_tab[d].trkpa) + (t)) * dsk_tab[d].wdspt) *\\r |
| 122 | sizeof (t_uint64))\r |
| 123 | \r |
| 124 | /* Unit flags */\r |
| 125 | \r |
| 126 | #define UNIT_V_INOP0 (UNIT_V_UF + 0) /* acc 0 inoperative */\r |
| 127 | #define UNIT_V_INOP1 (UNIT_V_UF + 1) /* acc 1 inoperative */\r |
| 128 | #define UNIT_V_FMTE (UNIT_V_UF + 2) /* format enabled */\r |
| 129 | #define UNIT_V_TYPE (UNIT_V_UF + 3) /* drive type */\r |
| 130 | #define UNIT_M_TYPE 03\r |
| 131 | #define UNIT_INOP0 (1 << UNIT_V_INOP0)\r |
| 132 | #define UNIT_INOP1 (1 << UNIT_V_INOP1)\r |
| 133 | #define UNIT_FMTE (1 << UNIT_V_FMTE)\r |
| 134 | #define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE)\r |
| 135 | #define TYPE_7320 (0 << UNIT_V_TYPE)\r |
| 136 | #define TYPE_1301 (1 << UNIT_V_TYPE)\r |
| 137 | #define TYPE_1302 (2 << UNIT_V_TYPE)\r |
| 138 | #define TYPE_2302 (3 << UNIT_V_TYPE)\r |
| 139 | #define GET_DTYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE)\r |
| 140 | #define TRK u3 /* track */\r |
| 141 | #define SKF u4 /* seek in progress */\r |
| 142 | \r |
| 143 | /* Track/record structure */\r |
| 144 | \r |
| 145 | #define THA2 0 /* home address 2 */\r |
| 146 | #define HA2_MASK 0777700000000 /* two chars checked */\r |
| 147 | #define T1STREC 1 /* start of records */\r |
| 148 | #define RLNT 0 /* record length */\r |
| 149 | #define RADDR 1 /* record address */\r |
| 150 | #define RDATA 2 /* start of data */\r |
| 151 | #define REC_MASK 0171717177777 /* 4 digits, 2 chars */\r |
| 152 | \r |
| 153 | /* Command word (60b) - 10 BCD digits */\r |
| 154 | \r |
| 155 | #define OP1 0 /* opcode */\r |
| 156 | #define OP2 1\r |
| 157 | #define ACC 2 /* access */\r |
| 158 | #define MOD 3 /* module */\r |
| 159 | #define T1 4 /* track */\r |
| 160 | #define T2 5\r |
| 161 | #define T3 6\r |
| 162 | #define T4 7\r |
| 163 | \r |
| 164 | /* Disk states */\r |
| 165 | \r |
| 166 | #define DSK_IDLE 0\r |
| 167 | \r |
| 168 | /* Status word (60b) */\r |
| 169 | \r |
| 170 | #define DSKS_PCHK 004000000000000000000 /* prog check */\r |
| 171 | #define DSKS_DCHK 002000000000000000000 /* data check */\r |
| 172 | #define DSKS_EXCC 001000000000000000000 /* exc cond */\r |
| 173 | #define DSKS_INVS 000200000000000000000 /* invalid seq */\r |
| 174 | #define DSKS_INVC 000040000000000000000 /* invalid opcode */\r |
| 175 | #define DSKS_FMTC 000020000000000000000 /* format check */\r |
| 176 | #define DSKS_NRCF 000010000000000000000 /* no record found */\r |
| 177 | #define DSKS_INVA 000002000000000000000 /* invalid address */\r |
| 178 | #define DSKS_RSPC 000000400000000000000 /* response check */\r |
| 179 | #define DSKS_CMPC 000000200000000000000 /* compare check */\r |
| 180 | #define DSKS_PARC 000000100000000000000 /* parity check */\r |
| 181 | #define DSKS_ACCI 000000020000000000000 /* access inoperative */\r |
| 182 | #define DSKS_ACCN 000000004000000000000 /* access not ready */\r |
| 183 | #define DSKS_DSKE 000000002000000000000 /* disk error */\r |
| 184 | #define DSKS_FILE 000000001000000000000 /* file error */\r |
| 185 | #define DSKS_6B 000000000040000000000 /* six bit mode */\r |
| 186 | #define DSKS_ATN0 000000000002000000000 /* attention start */\r |
| 187 | #define DSKS_PALL 000777000000000000000\r |
| 188 | #define DSKS_DALL 000000740000000000000\r |
| 189 | #define DSKS_EALL 000000037000000000000\r |
| 190 | #define DSKS_ALLERR 007777777000000000000\r |
| 191 | \r |
| 192 | /* Commands - opcode 0 */\r |
| 193 | \r |
| 194 | #define DSKC_NOP 0x00\r |
| 195 | #define DSKC_RLS 0x04\r |
| 196 | #define DSKC_8B 0x08\r |
| 197 | #define DSKC_6B 0x09\r |
| 198 | \r |
| 199 | /* Commands - opcode 8 */\r |
| 200 | \r |
| 201 | #define DSKC_SEEK 0x0 /* seek */\r |
| 202 | #define DSKC_SREC 0x2 /* single record */\r |
| 203 | #define DSKC_WFMT 0x3 /* write format */\r |
| 204 | #define DSKC_TNOA 0x4 /* track no addr */\r |
| 205 | #define DSKC_CYL 0x5 /* cyl no addr */\r |
| 206 | #define DSKC_WCHK 0x6 /* write check */\r |
| 207 | #define DSKC_ACCI 0x7 /* set acc inoperative */\r |
| 208 | #define DSKC_TWIA 0x8 /* track with addr */\r |
| 209 | #define DSKC_THA 0x9 /* track home addr */\r |
| 210 | \r |
| 211 | /* CTSS record structure */\r |
| 212 | \r |
| 213 | #define CTSS_HA2 0676767676767 /* =HXXXXXX */\r |
| 214 | #define CTSS_RLNT 435 /* data record */\r |
| 215 | #define CTSS_D1LNT 31 /* padding */\r |
| 216 | #define CTSS_D2LNT 14\r |
| 217 | #define CTSS_D3LNT 16\r |
| 218 | #define CTSS_DLLNT 1\r |
| 219 | #define CTSS_RA1 2\r |
| 220 | #define CTSS_RA2 8\r |
| 221 | \r |
| 222 | /* Data and declarations */\r |
| 223 | \r |
| 224 | typedef struct {\r |
| 225 | char *name;\r |
| 226 | uint32 accpm; /* acc/module: 1 or 2 */\r |
| 227 | uint32 wdspt; /* wds/track: 500 or 1000 */\r |
| 228 | uint32 trkpc; /* trks/cyl: 1 or 40 */\r |
| 229 | uint32 trkpa; /* trks/acc: 400 or 10000 */\r |
| 230 | uint32 fchpt; /* format ch/track */\r |
| 231 | uint32 size;\r |
| 232 | } DISK_TYPE;\r |
| 233 | \r |
| 234 | const DISK_TYPE dsk_tab[4] = {\r |
| 235 | { "7320", DSK_ACCPM_7320, DSK_WDSPT_7320,\r |
| 236 | DSK_TRKPC_7320, DSK_TRKPA_7320, DSK_FMCPT_7320, SIZE_7320 },\r |
| 237 | { "1301", DSK_ACCPM_1301, DSK_WDSPT_1301,\r |
| 238 | DSK_TRKPC_1301, DSK_TRKPA_1301, DSK_FMCPT_1301, SIZE_1301 },\r |
| 239 | { "1302", DSK_ACCPM_1302, DSK_WDSPT_1302,\r |
| 240 | DSK_TRKPC_1302, DSK_TRKPA_1302, DSK_FMCPT_1302, SIZE_1302 },\r |
| 241 | { "2302", DSK_ACCPM_2302, DSK_WDSPT_2302,\r |
| 242 | DSK_TRKPC_2302, DSK_TRKPA_2302, DSK_FMCPT_2302, SIZE_2302 }\r |
| 243 | };\r |
| 244 | \r |
| 245 | /* 7320/1301 format track characters */\r |
| 246 | \r |
| 247 | uint8 fmt_thdr_7320[] = {\r |
| 248 | 4, 4, 4, /* gap 1 */\r |
| 249 | 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */\r |
| 250 | 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */\r |
| 251 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */\r |
| 252 | };\r |
| 253 | uint8 fmt_rhdr_7320[] = {\r |
| 254 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */\r |
| 255 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */\r |
| 256 | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */\r |
| 257 | 1, 1, 1, 1, 0 /* record ovhd */\r |
| 258 | };\r |
| 259 | \r |
| 260 | /* 1302/2302 format track characters */\r |
| 261 | \r |
| 262 | uint8 fmt_thdr_1302[] = {\r |
| 263 | 4, 4, 4, 4, 4, 4, /* gap 1 */\r |
| 264 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */\r |
| 265 | 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */\r |
| 266 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */\r |
| 267 | };\r |
| 268 | uint8 fmt_rhdr_1302[] = {\r |
| 269 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */\r |
| 270 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */\r |
| 271 | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */\r |
| 272 | 1, 1, 1, 1, 1, 1, 1, 0 /* record ovhd */\r |
| 273 | };\r |
| 274 | \r |
| 275 | /* CTSS 7320/1301 track format table */\r |
| 276 | \r |
| 277 | uint32 ctss_fmt_7320[] = {\r |
| 278 | CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0\r |
| 279 | };\r |
| 280 | \r |
| 281 | /* CTSS 1302/2302 track format table */\r |
| 282 | \r |
| 283 | uint32 ctss_fmt_1302[] = {\r |
| 284 | CTSS_RLNT, CTSS_D1LNT, CTSS_D2LNT,\r |
| 285 | CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0\r |
| 286 | };\r |
| 287 | \r |
| 288 | uint32 dsk_ch = CH_C; /* disk channel */\r |
| 289 | uint32 dsk_acc = 0; /* access */\r |
| 290 | uint32 dsk_mod = 0; /* module */\r |
| 291 | uint32 dsk_sta = 0; /* disk state */\r |
| 292 | uint32 dsk_mode = 0; /* I/O mode */\r |
| 293 | uint32 dsk_wchk = 0; /* write check flag */\r |
| 294 | uint32 dsk_ctime = 10; /* command time */\r |
| 295 | uint32 dsk_stime = 1000; /* seek time */\r |
| 296 | uint32 dsk_rtime = 100; /* rotational latency */\r |
| 297 | uint32 dsk_wtime = 2; /* word time */\r |
| 298 | uint32 dsk_gtime = 5; /* gap time */\r |
| 299 | uint32 dsk_rbase = 0; /* record tracking */\r |
| 300 | uint32 dsk_rptr = 0;\r |
| 301 | uint32 dsk_rlim = 0;\r |
| 302 | uint32 dsk_stop = 0;\r |
| 303 | uint32 dsk_fmt_cntr = 0; /* format counter */\r |
| 304 | t_uint64 dsk_rec = 0; /* rec/home addr (36b) */\r |
| 305 | t_uint64 dsk_sns = 0; /* sense data (60b) */\r |
| 306 | t_uint64 dsk_cmd = 0; /* BCD command (60b) */\r |
| 307 | t_uint64 dsk_chob = 0; /* chan output buffer */\r |
| 308 | uint32 dsk_chob_v = 0; /* valid */\r |
| 309 | t_uint64 dsk_buf[DSK_BUFSIZ]; /* transfer buffer */\r |
| 310 | \r |
| 311 | extern uint32 ch_req;\r |
| 312 | \r |
| 313 | t_stat dsk_svc (UNIT *uptr);\r |
| 314 | t_stat dsk_svc_sns (UNIT *uptr);\r |
| 315 | t_stat dsk_reset (DEVICE *dptr);\r |
| 316 | t_stat dsk_attach (UNIT *uptr, char *cptr);\r |
| 317 | t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 318 | t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit);\r |
| 319 | t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 flags);\r |
| 320 | t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd);\r |
| 321 | t_stat dsk_uend (uint32 ch, t_uint64 stat);\r |
| 322 | t_stat dsk_recad (uint32 trk, uint32 rec, uint32 acc, uint32 mod, t_uint64 *res);\r |
| 323 | t_uint64 dsk_acc_atn (uint32 u);\r |
| 324 | t_stat dsk_init_trk (UNIT *udptr, uint32 trk);\r |
| 325 | t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp);\r |
| 326 | t_stat dsk_wr_trk (UNIT *uptr, uint32 trk);\r |
| 327 | t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc);\r |
| 328 | t_bool dsk_qdone (uint32 ch);\r |
| 329 | t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc);\r |
| 330 | \r |
| 331 | /* DSK data structures\r |
| 332 | \r |
| 333 | dsk_dev DSK device descriptor\r |
| 334 | dsk_unit DSK unit descriptor\r |
| 335 | dsk_reg DSK register list\r |
| 336 | */\r |
| 337 | \r |
| 338 | DIB dsk_dib = { &dsk_chsel, &dsk_chwr };\r |
| 339 | \r |
| 340 | UNIT dsk_unit[] = {\r |
| 341 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 342 | TYPE_2302, SIZE_2302) },\r |
| 343 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 344 | TYPE_2302, SIZE_2302) },\r |
| 345 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 346 | TYPE_7320, SIZE_7320) },\r |
| 347 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 348 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r |
| 349 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 350 | TYPE_2302, SIZE_2302) },\r |
| 351 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 352 | TYPE_2302, SIZE_2302) },\r |
| 353 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 354 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r |
| 355 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 356 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r |
| 357 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 358 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r |
| 359 | { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+\r |
| 360 | UNIT_DIS+TYPE_2302, SIZE_2302) },\r |
| 361 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 362 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 363 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 364 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 365 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 366 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 367 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 368 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 369 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 370 | { UDATA (&dsk_svc, UNIT_DIS, 0) },\r |
| 371 | { UDATA (&dsk_svc_sns, UNIT_DIS, 0) }\r |
| 372 | };\r |
| 373 | \r |
| 374 | REG dsk_reg[] = {\r |
| 375 | { ORDATA (STATE, dsk_sta, 6) },\r |
| 376 | { ORDATA (ACCESS, dsk_acc, 1) },\r |
| 377 | { ORDATA (MODULE, dsk_mod, 4) },\r |
| 378 | { ORDATA (RECORD, dsk_rec, 36) },\r |
| 379 | { ORDATA (MODE, dsk_mode, 4) },\r |
| 380 | { ORDATA (SENSE, dsk_sns, 60) },\r |
| 381 | { ORDATA (BCDCMD, dsk_cmd, 60) },\r |
| 382 | { ORDATA (CHOB, dsk_chob, 36) },\r |
| 383 | { FLDATA (CHOBV, dsk_chob_v, 0) },\r |
| 384 | { FLDATA (STOP, dsk_stop, 0) },\r |
| 385 | { DRDATA (FCNTR, dsk_fmt_cntr, 13) },\r |
| 386 | { BRDATA (BUF, dsk_buf, 8, 36, DSK_BUFSIZ) },\r |
| 387 | { DRDATA (RBASE, dsk_rbase, 10), REG_RO },\r |
| 388 | { DRDATA (RPTR, dsk_rptr, 10), REG_RO },\r |
| 389 | { DRDATA (RLIM, dsk_rlim, 10), REG_RO },\r |
| 390 | { DRDATA (CHAN, dsk_ch, 3), REG_HRO },\r |
| 391 | { DRDATA (STIME, dsk_stime, 24), REG_NZ + PV_LEFT },\r |
| 392 | { DRDATA (RTIME, dsk_rtime, 24), REG_NZ + PV_LEFT },\r |
| 393 | { DRDATA (WTIME, dsk_wtime, 24), REG_NZ + PV_LEFT },\r |
| 394 | { DRDATA (GTIME, dsk_gtime, 24), REG_NZ + PV_LEFT },\r |
| 395 | { DRDATA (CTIME, dsk_ctime, 24), REG_NZ + PV_LEFT },\r |
| 396 | { URDATA (TRACK, dsk_unit[0].TRK, 10, 14, 0,\r |
| 397 | 2 * DSK_NUMDR, PV_LEFT) },\r |
| 398 | { URDATA (SEEKF, dsk_unit[0].SKF, 10, 1, 0,\r |
| 399 | 2 * DSK_NUMDR, PV_LEFT | REG_HRO) },\r |
| 400 | { URDATA (CAPAC, dsk_unit[0].capac, 10, T_ADDR_W, 0,\r |
| 401 | DSK_NUMDR, PV_LEFT | REG_HRO) },\r |
| 402 | { NULL }\r |
| 403 | };\r |
| 404 | \r |
| 405 | MTAB dsk_mtab[] = {\r |
| 406 | { UNIT_INOP0 + UNIT_INOP1, 0, "operational", "OPERATIONAL" },\r |
| 407 | { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP0, "access 0 inoperative", NULL },\r |
| 408 | { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP1, "access 1 inoperative", NULL },\r |
| 409 | { UNIT_FMTE, UNIT_FMTE, "formating enabled", "FORMAT" },\r |
| 410 | { UNIT_FMTE, 0, "formating disabled", "NOFORMAT" },\r |
| 411 | { UNIT_TYPE, TYPE_7320, "7320", "7320", &dsk_set_size },\r |
| 412 | { UNIT_TYPE, TYPE_1301, "1301", "1301", &dsk_set_size },\r |
| 413 | { UNIT_TYPE, TYPE_1302, "1302", "1302", &dsk_set_size },\r |
| 414 | { UNIT_TYPE, TYPE_2302, "2302", "2302", &dsk_set_size },\r |
| 415 | { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL,\r |
| 416 | NULL, &ch_show_chan, NULL },\r |
| 417 | { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "FORMAT", NULL,\r |
| 418 | NULL, &dsk_show_format, NULL },\r |
| 419 | { 0 }\r |
| 420 | };\r |
| 421 | \r |
| 422 | DEVICE dsk_dev = {\r |
| 423 | "DSK", dsk_unit, dsk_reg, dsk_mtab,\r |
| 424 | (DSK_NUMDR * 2) + 1, 10, 24, 1, 8, 36,\r |
| 425 | NULL, NULL, &dsk_reset,\r |
| 426 | NULL, &dsk_attach, NULL,\r |
| 427 | &dsk_dib, DEV_DIS\r |
| 428 | };\r |
| 429 | \r |
| 430 | /* Disk channel select, from 7909 channel program */\r |
| 431 | \r |
| 432 | t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit)\r |
| 433 | {\r |
| 434 | uint32 u;\r |
| 435 | \r |
| 436 | dsk_ch = ch;\r |
| 437 | if (dsk_sta != DSK_IDLE) dsk_uend (ch, DSKS_INVS); /* not idle? seq check */\r |
| 438 | \r |
| 439 | switch (sel) {\r |
| 440 | \r |
| 441 | case CHSL_CTL: /* control */\r |
| 442 | ch_req |= REQ_CH (ch); /* request channel */\r |
| 443 | break;\r |
| 444 | \r |
| 445 | case CHSL_SNS: /* sense */\r |
| 446 | if (sim_is_active (&dsk_unit[DSK_SNS])) /* already sensing? */\r |
| 447 | return dsk_uend (ch, DSKS_INVS); /* sequence error */\r |
| 448 | sim_activate (&dsk_unit[DSK_SNS], dsk_ctime); /* set timer */\r |
| 449 | dsk_stop = 0;\r |
| 450 | break;\r |
| 451 | \r |
| 452 | case CHSL_RDS: /* read */\r |
| 453 | if (dsk_mode == DSKC_WFMT) /* write format? */\r |
| 454 | return dsk_uend (ch, DSKS_INVS); /* sequence error */\r |
| 455 | case CHSL_WRS: /* write */\r |
| 456 | if (dsk_mode == 0) dsk_uend (ch, DSKS_INVS); /* no mode? seq check */\r |
| 457 | if (dsk_mode == DSKC_WFMT) sel = CHSL_FMT; /* format? fake sel */\r |
| 458 | u = (dsk_acc * DSK_NUMDR) + dsk_mod; /* access unit number */\r |
| 459 | if (sim_is_active (&dsk_unit[u])) /* access in use? */\r |
| 460 | return dsk_uend (ch, DSKS_ACCN); /* access not ready */\r |
| 461 | sim_activate (&dsk_unit[u], dsk_rtime); /* rotational time */\r |
| 462 | break;\r |
| 463 | \r |
| 464 | default: /* other */\r |
| 465 | return STOP_ILLIOP;\r |
| 466 | }\r |
| 467 | \r |
| 468 | dsk_sta = sel; /* set new state */\r |
| 469 | return SCPE_OK;\r |
| 470 | }\r |
| 471 | \r |
| 472 | /* Disk channel write, from 7909 channel program */\r |
| 473 | \r |
| 474 | t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 stopf)\r |
| 475 | {\r |
| 476 | if (stopf) dsk_stop = 1; /* stop? */\r |
| 477 | \r |
| 478 | else {\r |
| 479 | val = val & DMASK;\r |
| 480 | switch (dsk_sta) { /* case on state */\r |
| 481 | \r |
| 482 | case CHSL_CTL: /* control */\r |
| 483 | dsk_cmd = val << 24;\r |
| 484 | if (val & 0100000000000) { /* need 2nd word? */\r |
| 485 | ch_req |= REQ_CH (ch); /* req ch for 2nd */\r |
| 486 | dsk_sta = CHSL_CTL|CHSL_2ND; /* next state */\r |
| 487 | return SCPE_OK;\r |
| 488 | }\r |
| 489 | return dsk_new_cmd (ch, dsk_cmd); /* no, do cmd */\r |
| 490 | \r |
| 491 | case CHSL_CTL|CHSL_2ND: /* 2nd control */\r |
| 492 | dsk_cmd |= (val >> 12);\r |
| 493 | return dsk_new_cmd (ch, dsk_cmd); /* do cmd */\r |
| 494 | \r |
| 495 | default:\r |
| 496 | dsk_chob = val; /* store data */\r |
| 497 | dsk_chob_v = 1; /* set valid */\r |
| 498 | }\r |
| 499 | }\r |
| 500 | \r |
| 501 | return SCPE_OK;\r |
| 502 | }\r |
| 503 | \r |
| 504 | /* New command - end of CTL sequence */\r |
| 505 | \r |
| 506 | t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd)\r |
| 507 | {\r |
| 508 | uint32 i, d, a, m, u, trk, dtyp, bcd[8];\r |
| 509 | \r |
| 510 | ch_req |= REQ_CH (ch); /* req ch for end */\r |
| 511 | ch9_set_end (ch, 0); /* set end flag */\r |
| 512 | dsk_sta = DSK_IDLE; /* ctrl is idle */\r |
| 513 | \r |
| 514 | for (i = 0; i < 8; i++) { /* get chars from cmd */\r |
| 515 | d = (uint32) (cmd >> (6 * (9 - i))) & BCD_MASK;\r |
| 516 | if (d == BCD_ZERO) d = 0;\r |
| 517 | else if (d == 0) d = BCD_ZERO; /* BCD zero cvt */\r |
| 518 | bcd[i] = d;\r |
| 519 | }\r |
| 520 | \r |
| 521 | if (bcd[OP1] == 0) { /* cmd = 0x? */\r |
| 522 | \r |
| 523 | switch (bcd[OP2]) { /* case on x */\r |
| 524 | \r |
| 525 | case DSKC_NOP: /* nop */\r |
| 526 | case DSKC_RLS: /* release */\r |
| 527 | break;\r |
| 528 | \r |
| 529 | case DSKC_8B: /* 8b mode */\r |
| 530 | dsk_sns &= ~DSKS_6B;\r |
| 531 | break;\r |
| 532 | \r |
| 533 | case DSKC_6B: /* 6b mode */\r |
| 534 | dsk_sns |= DSKS_6B;\r |
| 535 | break;\r |
| 536 | \r |
| 537 | default: /* unknown */\r |
| 538 | return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r |
| 539 | } /* end case op2 */\r |
| 540 | return SCPE_OK;\r |
| 541 | } /* end if */\r |
| 542 | \r |
| 543 | else if (bcd[OP1] == 8) { /* cmd = 8x? */\r |
| 544 | \r |
| 545 | a = bcd[ACC]; /* get access, */\r |
| 546 | m = bcd[MOD]; /* module */\r |
| 547 | u = (a * DSK_NUMDR) + m; /* unit for access */\r |
| 548 | if ((m > DSK_NUMDR) || /* invalid module? */\r |
| 549 | (dsk_unit[m].flags & UNIT_DIS)) /* disabled module? */\r |
| 550 | return dsk_uend (ch, DSKS_ACCI);\r |
| 551 | dtyp = GET_DTYPE (dsk_unit[m].flags); /* get drive type */\r |
| 552 | if ((a >= dsk_tab[dtyp].accpm) || /* invalid access? */\r |
| 553 | (dsk_unit[m].flags & (UNIT_INOP0 << a))) /* access inop? */\r |
| 554 | return dsk_uend (ch, DSKS_ACCI);\r |
| 555 | if ((bcd[T1] > 9) || (bcd[T2] > 9) || (bcd[T3] > 9) || (bcd[T4] > 9))\r |
| 556 | trk = dsk_tab[dtyp].trkpa + 1; /* bad track */\r |
| 557 | else trk = (((((bcd[T1] * 10) + bcd[T2]) * 10) + bcd[T3]) * 10) + bcd[T4];\r |
| 558 | \r |
| 559 | if (bcd[OP2] == DSKC_WCHK) { /* write check */\r |
| 560 | if (dsk_mode == 0) /* no prior operation? */\r |
| 561 | return dsk_uend (ch, DSKS_INVS);\r |
| 562 | bcd[OP2] = dsk_mode; /* use prior mode */\r |
| 563 | dsk_wchk = 1; /* set write check */\r |
| 564 | }\r |
| 565 | else dsk_wchk = 0;\r |
| 566 | dsk_sns &= ~(DSKS_ALLERR | dsk_acc_atn (u)); /* clear err, atn */\r |
| 567 | dsk_stop = 0; /* clear stop */\r |
| 568 | \r |
| 569 | switch (bcd[OP2]) {\r |
| 570 | \r |
| 571 | case DSKC_SEEK: /* seek */\r |
| 572 | if ((trk >= dsk_tab[dtyp].trkpa) && /* inv track? */\r |
| 573 | ((dtyp == TYPE_7320) || /* drum or not CE? */\r |
| 574 | (bcd[T1] > 9) || (bcd[T2] != BCD_AT) ||\r |
| 575 | (bcd[T3] > 9) || (bcd[T4] > 9)))\r |
| 576 | return dsk_uend (ch, DSKS_INVA);\r |
| 577 | if (sim_is_active (&dsk_unit[u])) /* selected acc busy? */\r |
| 578 | return dsk_uend (ch, DSKS_ACCN);\r |
| 579 | dsk_unit[u].SKF = 1; /* set seeking flag */\r |
| 580 | dsk_unit[u].TRK = trk; /* sel acc on cyl */\r |
| 581 | sim_activate (&dsk_unit[u], dsk_stime); /* seek */\r |
| 582 | dsk_mode = 0; /* clear I/O mode */\r |
| 583 | return SCPE_OK;\r |
| 584 | \r |
| 585 | case DSKC_ACCI: /* access inoperative */\r |
| 586 | dsk_unit[m].flags |= (UNIT_INOP0 << a); /* set correct flag */\r |
| 587 | dsk_mode = 0; /* clear I/O mode */\r |
| 588 | return SCPE_OK;\r |
| 589 | \r |
| 590 | case DSKC_SREC: /* single record */\r |
| 591 | break; /* no verification */\r |
| 592 | \r |
| 593 | case DSKC_WFMT: /* format */\r |
| 594 | if (!(dsk_unit[m].flags & UNIT_FMTE)) /* format enabled? */\r |
| 595 | return dsk_uend (ch, DSKS_FMTC); /* no, error */\r |
| 596 | case DSKC_TNOA: /* track no addr */\r |
| 597 | case DSKC_CYL: /* cyl no addr */\r |
| 598 | case DSKC_TWIA: /* track with addr */\r |
| 599 | case DSKC_THA: /* track home addr */\r |
| 600 | if (trk != (uint32) dsk_unit[u].TRK) /* on track? */\r |
| 601 | return dsk_uend (ch, DSKS_NRCF);\r |
| 602 | break; \r |
| 603 | \r |
| 604 | default:\r |
| 605 | return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r |
| 606 | }\r |
| 607 | \r |
| 608 | dsk_acc = a; /* save access */\r |
| 609 | dsk_mod = m; /* save module */\r |
| 610 | dsk_rec = cmd & DMASK; /* save rec/home addr */\r |
| 611 | dsk_mode = bcd[OP2]; /* save mode */\r |
| 612 | return SCPE_OK;\r |
| 613 | }\r |
| 614 | \r |
| 615 | return dsk_uend (ch, DSKS_INVC); /* invalid opcode */\r |
| 616 | }\r |
| 617 | \r |
| 618 | /* Sense unit service */\r |
| 619 | \r |
| 620 | t_stat dsk_svc_sns (UNIT *uptr)\r |
| 621 | {\r |
| 622 | t_uint64 dat;\r |
| 623 | \r |
| 624 | switch (dsk_sta) { /* case on state */\r |
| 625 | \r |
| 626 | case CHSL_SNS: /* prepare data */\r |
| 627 | dsk_buf[0] = (dsk_sns >> 24) & DMASK; /* buffer is 2 words */\r |
| 628 | dsk_buf[1] = (dsk_sns << 12) & DMASK;\r |
| 629 | dsk_rptr = 0;\r |
| 630 | dsk_rlim = 2;\r |
| 631 | dsk_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */\r |
| 632 | break;\r |
| 633 | \r |
| 634 | case CHSL_SNS|CHSL_2ND: /* second state */\r |
| 635 | if (dsk_rptr >= dsk_rlim) { /* end of buffer? */\r |
| 636 | ch9_set_end (dsk_ch, 0); /* set end */\r |
| 637 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r |
| 638 | dsk_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */\r |
| 639 | sim_activate (uptr, dsk_ctime); /* longer wait */\r |
| 640 | return SCPE_OK;\r |
| 641 | }\r |
| 642 | dat = dsk_buf[dsk_rptr++]; /* get word */\r |
| 643 | if (!dsk_stop) ch9_req_rd (dsk_ch, dat); /* send wd to chan */\r |
| 644 | break;\r |
| 645 | \r |
| 646 | case CHSL_SNS|CHSL_3RD: /* 3rd state */\r |
| 647 | if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */\r |
| 648 | dsk_sta = CHSL_SNS; /* repeat sequence */\r |
| 649 | break;\r |
| 650 | }\r |
| 651 | \r |
| 652 | sim_activate (uptr, dsk_wtime); /* sched next */\r |
| 653 | return SCPE_OK;\r |
| 654 | }\r |
| 655 | \r |
| 656 | /* Seek, read, write unit service */\r |
| 657 | \r |
| 658 | t_stat dsk_svc (UNIT *uaptr)\r |
| 659 | {\r |
| 660 | uint32 i, dtyp, trk;\r |
| 661 | uint8 fc, *format;\r |
| 662 | t_uint64 rdat;\r |
| 663 | UNIT *udptr;\r |
| 664 | t_stat r;\r |
| 665 | \r |
| 666 | if (uaptr->SKF) { /* seeking? */\r |
| 667 | uint32 u = uaptr - dsk_dev.units; /* get unit */\r |
| 668 | uaptr->SKF = 0; /* seek done */\r |
| 669 | dsk_sns |= dsk_acc_atn (u); /* set atn bit */\r |
| 670 | ch9_set_atn (dsk_ch); /* set atn flag */\r |
| 671 | return SCPE_OK;\r |
| 672 | }\r |
| 673 | \r |
| 674 | udptr = dsk_dev.units + dsk_mod; /* data unit */\r |
| 675 | if (udptr->flags & (UNIT_INOP0 << dsk_acc)) /* acc inoperative? */\r |
| 676 | return dsk_uend (dsk_ch, DSKS_ACCI); /* error */\r |
| 677 | if ((udptr->flags & UNIT_ATT) == 0) { /* not attached? */\r |
| 678 | dsk_uend (dsk_ch, DSKS_ACCI); /* error */\r |
| 679 | return SCPE_UNATT;\r |
| 680 | }\r |
| 681 | \r |
| 682 | dtyp = GET_DTYPE (udptr->flags); /* get data drive type */\r |
| 683 | trk = uaptr->TRK; /* get access track */\r |
| 684 | \r |
| 685 | switch (dsk_sta) { /* case on state */\r |
| 686 | \r |
| 687 | case CHSL_RDS: /* read start */\r |
| 688 | if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */\r |
| 689 | return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */\r |
| 690 | }\r |
| 691 | dsk_sta = CHSL_RDS|CHSL_2ND; /* next state */\r |
| 692 | break;\r |
| 693 | \r |
| 694 | case CHSL_RDS|CHSL_2ND: /* read data transmit */\r |
| 695 | if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */\r |
| 696 | if (r != ERR_ENDRC) return r; /* error? */\r |
| 697 | dsk_sta = CHSL_RDS|CHSL_3RD; /* next state */\r |
| 698 | sim_activate (uaptr, dsk_gtime); /* gap time */\r |
| 699 | return SCPE_OK;\r |
| 700 | }\r |
| 701 | rdat = dsk_buf[dsk_rptr++]; /* get word */\r |
| 702 | if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */\r |
| 703 | if (!dsk_stop) ch9_req_rd (dsk_ch, rdat); /* give to channel */\r |
| 704 | break;\r |
| 705 | \r |
| 706 | case CHSL_RDS|CHSL_3RD: /* read end rec/trk */\r |
| 707 | if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */\r |
| 708 | dsk_sta = CHSL_RDS; /* repeat sequence */\r |
| 709 | break;\r |
| 710 | \r |
| 711 | case CHSL_WRS: /* write start */\r |
| 712 | if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */\r |
| 713 | return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */\r |
| 714 | }\r |
| 715 | ch_req |= REQ_CH (dsk_ch); /* first request */\r |
| 716 | dsk_sta = CHSL_WRS|CHSL_2ND; /* next state */\r |
| 717 | dsk_chob = 0; /* clr, inval buffer */\r |
| 718 | dsk_chob_v = 0;\r |
| 719 | break;\r |
| 720 | \r |
| 721 | case CHSL_WRS|CHSL_2ND: /* write data transmit */\r |
| 722 | if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */\r |
| 723 | else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */\r |
| 724 | if (dsk_wchk) { /* write check? */\r |
| 725 | if (dsk_buf[dsk_rptr++] != dsk_chob) /* data mismatch? */\r |
| 726 | return dsk_uend (dsk_ch, DSKS_CMPC); /* error */\r |
| 727 | }\r |
| 728 | else dsk_buf[dsk_rptr++] = dsk_chob; /* write, store word */\r |
| 729 | if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */\r |
| 730 | if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */\r |
| 731 | if (r != ERR_ENDRC) return r; /* error? */\r |
| 732 | dsk_sta = CHSL_WRS|CHSL_3RD; /* next state */\r |
| 733 | sim_activate (uaptr, dsk_gtime); /* gap time */\r |
| 734 | return SCPE_OK;\r |
| 735 | }\r |
| 736 | if (!dsk_stop) ch_req |= REQ_CH (dsk_ch); /* more to do */\r |
| 737 | break;\r |
| 738 | \r |
| 739 | case CHSL_WRS|CHSL_3RD: /* write done */\r |
| 740 | if (!dsk_wchk) { /* if write */\r |
| 741 | if (r = dsk_wr_trk (udptr, trk)) return r; /* write track; err? */\r |
| 742 | }\r |
| 743 | if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */\r |
| 744 | dsk_sta = CHSL_WRS; /* repeat sequence */\r |
| 745 | break;\r |
| 746 | \r |
| 747 | /* Formatting takes place in five stages\r |
| 748 | \r |
| 749 | 1. Clear the track buffer, request the first word from the channel\r |
| 750 | 2. Match characters against the fixed overhead (HA1, HA2, and gaps)\r |
| 751 | 3. Match characters against the per-record overhead (RA and gaps)\r |
| 752 | 4. Count the characters defining the record length\r |
| 753 | 5. See if the next character is end or gap; if gap, return to stage 3\r |
| 754 | \r |
| 755 | This formating routine is not exact. It checks whether the format\r |
| 756 | will fit in the container, not whether the format would fit on a\r |
| 757 | real 7320, 1301, 1302, or 2302. */\r |
| 758 | \r |
| 759 | case CHSL_FMT: /* initialization */\r |
| 760 | for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;/* clear track buf */\r |
| 761 | dsk_rbase = T1STREC; /* init record ptr */\r |
| 762 | dsk_rptr = 0; /* init format ptr */\r |
| 763 | dsk_fmt_cntr = 0; /* init counter */\r |
| 764 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r |
| 765 | dsk_sta = CHSL_FMT|CHSL_2ND; /* next state */\r |
| 766 | dsk_chob = 0; /* clr, inval buffer */\r |
| 767 | dsk_chob_v = 0;\r |
| 768 | break;\r |
| 769 | \r |
| 770 | case CHSL_FMT|CHSL_2ND: /* match track header */\r |
| 771 | if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r |
| 772 | format = fmt_thdr_7320;\r |
| 773 | else format = fmt_thdr_1302;\r |
| 774 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r |
| 775 | if (fc != format[dsk_rptr++]) /* mismatch? */\r |
| 776 | return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r |
| 777 | if (format[dsk_rptr] == 0) { /* end format? */\r |
| 778 | dsk_sta = CHSL_FMT|CHSL_3RD; /* next state */\r |
| 779 | dsk_rptr = 0; /* reset format ptr */\r |
| 780 | }\r |
| 781 | break;\r |
| 782 | \r |
| 783 | case CHSL_FMT|CHSL_3RD: /* match record header */\r |
| 784 | if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r |
| 785 | format = fmt_rhdr_7320;\r |
| 786 | else format = fmt_rhdr_1302;\r |
| 787 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r |
| 788 | if (fc != format[dsk_rptr++]) /* mismatch? */\r |
| 789 | return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r |
| 790 | if (format[dsk_rptr] == 0) { /* end format? */\r |
| 791 | dsk_sta = CHSL_FMT|CHSL_4TH; /* next state */\r |
| 792 | dsk_rlim = 0; /* reset record ctr */\r |
| 793 | }\r |
| 794 | break;\r |
| 795 | \r |
| 796 | case CHSL_FMT|CHSL_4TH: /* count record size */\r |
| 797 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r |
| 798 | if (fc == BCD_ONE) dsk_rlim++; /* more record? */\r |
| 799 | else {\r |
| 800 | uint32 rsiz = dsk_rlim / 6; /* rec size words */\r |
| 801 | if ((fc != BCD_TWO) || /* improper end? */\r |
| 802 | (rsiz == 0) || /* smaller than min? */\r |
| 803 | ((dsk_rlim % 6) != 0) || /* not multiple of 6? */\r |
| 804 | ((dsk_rbase + rsiz + RDATA) >= dsk_tab[dtyp].wdspt))\r |
| 805 | return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r |
| 806 | dsk_buf[dsk_rbase + RLNT] = rsiz; /* record rec lnt */\r |
| 807 | dsk_rbase = dsk_rbase + rsiz + RDATA; /* new rec start */\r |
| 808 | dsk_sta = CHSL_FMT|CHSL_5TH; /* next state */\r |
| 809 | }\r |
| 810 | break;\r |
| 811 | \r |
| 812 | case CHSL_FMT|CHSL_5TH: /* record or track end */\r |
| 813 | if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */\r |
| 814 | if (fc == BCD_TWO) { /* back to record header? */\r |
| 815 | dsk_rptr = 2; /* already done 2 chars */\r |
| 816 | dsk_sta = CHSL_FMT|CHSL_3RD; /* record header state */\r |
| 817 | }\r |
| 818 | else if (fc != BCD_ONE) dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r |
| 819 | else {\r |
| 820 | if (!dsk_wchk) { /* actual write? */\r |
| 821 | trk = trk - (trk % dsk_tab[dtyp].trkpc); /* cyl start */\r |
| 822 | for (i = 0; i < dsk_tab[dtyp].trkpc; i++) { /* do all tracks */\r |
| 823 | if (r = dsk_wr_trk (udptr, trk + i)) /* wr track; err? */\r |
| 824 | return r;\r |
| 825 | }\r |
| 826 | }\r |
| 827 | ch9_set_end (dsk_ch, 0); /* set end */\r |
| 828 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r |
| 829 | dsk_sta = DSK_IDLE; /* disk is idle */\r |
| 830 | return SCPE_OK; /* done */\r |
| 831 | }\r |
| 832 | break;\r |
| 833 | \r |
| 834 | default:\r |
| 835 | return SCPE_IERR;\r |
| 836 | }\r |
| 837 | \r |
| 838 | sim_activate (uaptr, dsk_wtime);\r |
| 839 | return SCPE_OK;\r |
| 840 | }\r |
| 841 | \r |
| 842 | /* Initialize data transfer\r |
| 843 | \r |
| 844 | Inputs:\r |
| 845 | udptr = pointer to data unit\r |
| 846 | trk = track to read\r |
| 847 | Outputs:\r |
| 848 | dsk_buf contains track specified by trk\r |
| 849 | dsk_rbase, dsk_rptr, dsk_rlim are initialized\r |
| 850 | Errors:\r |
| 851 | SCPE_IOERR = I/O error (fatal, uend)\r |
| 852 | ERR_NRCF = no record found (HA2 or record number mismatch, uend)\r |
| 853 | STOP_INVFMT = invalid format (fatal, uend)\r |
| 854 | */\r |
| 855 | \r |
| 856 | t_stat dsk_init_trk (UNIT *udptr, uint32 trk)\r |
| 857 | {\r |
| 858 | uint32 k, da, dtyp, rlnt;\r |
| 859 | \r |
| 860 | dtyp = GET_DTYPE (udptr->flags); /* get drive type */\r |
| 861 | da = DSK_DA (dsk_acc, trk, dtyp); /* get disk address */\r |
| 862 | sim_fseek (udptr->fileref, da, SEEK_SET); /* read track */\r |
| 863 | k = sim_fread (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref);\r |
| 864 | if (ferror (udptr->fileref)) { /* error? */\r |
| 865 | perror ("DSK I/O error");\r |
| 866 | clearerr (udptr->fileref);\r |
| 867 | dsk_uend (dsk_ch, DSKS_DSKE);\r |
| 868 | return SCPE_IOERR;\r |
| 869 | }\r |
| 870 | for ( ; k < dsk_tab[dtyp].wdspt; k++) dsk_buf[k] = 0; /* zero fill */\r |
| 871 | dsk_rbase = T1STREC; /* record base */\r |
| 872 | rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r |
| 873 | dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */\r |
| 874 | if ((rlnt == 0) || (dsk_rlim >= dsk_tab[dtyp].wdspt)) { /* invalid record? */\r |
| 875 | dsk_uend (dsk_ch, DSKS_FMTC);\r |
| 876 | return STOP_INVFMT;\r |
| 877 | }\r |
| 878 | if (dsk_mode != DSKC_SREC) { /* not single record? */\r |
| 879 | if (dsk_mode == DSKC_THA) dsk_rptr = 0; /* trk home addr? */\r |
| 880 | else {\r |
| 881 | if (((dsk_rec << 24) ^ dsk_buf[THA2]) & HA2_MASK) {\r |
| 882 | dsk_uend (dsk_ch, DSKS_NRCF); /* invalid HA2 */\r |
| 883 | return ERR_NRCF;\r |
| 884 | }\r |
| 885 | if (dsk_mode == DSKC_TWIA) /* track with addr? */\r |
| 886 | dsk_rptr = dsk_rbase + RADDR; /* start at addr */\r |
| 887 | else dsk_rptr = dsk_rbase + RDATA; /* else, at data */\r |
| 888 | }\r |
| 889 | return SCPE_OK;\r |
| 890 | }\r |
| 891 | while (rlnt != 0) { /* until end track */\r |
| 892 | dsk_rptr = dsk_rbase + RDATA;\r |
| 893 | if (((dsk_rec ^ dsk_buf[dsk_rbase + RADDR]) & REC_MASK) == 0)\r |
| 894 | return SCPE_OK; /* rec found? done */\r |
| 895 | dsk_rbase = dsk_rlim; /* next record */\r |
| 896 | rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r |
| 897 | dsk_rlim = dsk_rbase + rlnt + RDATA; /* limit */\r |
| 898 | if (dsk_rlim >= dsk_tab[dtyp].wdspt) { /* invalid format? */\r |
| 899 | dsk_uend (dsk_ch, DSKS_FMTC);\r |
| 900 | return STOP_INVFMT;\r |
| 901 | }\r |
| 902 | }\r |
| 903 | dsk_uend (dsk_ch, DSKS_NRCF); /* not found */\r |
| 904 | return ERR_NRCF;\r |
| 905 | }\r |
| 906 | \r |
| 907 | /* Check end of transfer\r |
| 908 | \r |
| 909 | Inputs:\r |
| 910 | uptr = pointer to access unit\r |
| 911 | dtyp = drive type\r |
| 912 | Outputs:\r |
| 913 | ERR_ENDRC = end of record/track/cylinder, end sent, ch req if required\r |
| 914 | SCPE_OK = more to do, dsk_rbase, dsk_rptr, dsk_rlim may be updated\r |
| 915 | STOP_INVFMT = invalid format (fatal, uend sent)\r |
| 916 | */\r |
| 917 | \r |
| 918 | t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp)\r |
| 919 | {\r |
| 920 | uint32 rlnt;\r |
| 921 | \r |
| 922 | if (dsk_rptr < dsk_rlim) return SCPE_OK; /* record done? */\r |
| 923 | if (dsk_stop || !ch9_qconn (dsk_ch) || /* stop or err disc or */\r |
| 924 | (dsk_mode == DSKC_SREC)) { /* single record? */\r |
| 925 | ch9_set_end (dsk_ch, 0); /* set end */\r |
| 926 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r |
| 927 | return ERR_ENDRC;\r |
| 928 | }\r |
| 929 | dsk_rbase = dsk_rlim; /* next record */\r |
| 930 | rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */\r |
| 931 | dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */\r |
| 932 | if ((dsk_rbase >= dsk_tab[dtyp].wdspt) || /* invalid format? */\r |
| 933 | (dsk_rlim >= dsk_tab[dtyp].wdspt)) {\r |
| 934 | dsk_uend (dsk_ch, DSKS_FMTC);\r |
| 935 | return STOP_INVFMT;\r |
| 936 | }\r |
| 937 | if (rlnt) { /* more on track? */\r |
| 938 | if ((dsk_mode == DSKC_THA) || (dsk_mode == DSKC_TWIA))\r |
| 939 | dsk_rptr = dsk_rbase + RADDR; /* start with addr */\r |
| 940 | else dsk_rptr = dsk_rbase + RDATA; /* or data */\r |
| 941 | return SCPE_OK;\r |
| 942 | }\r |
| 943 | if (dsk_mode == DSKC_CYL) { /* cylinder mode? */\r |
| 944 | uaptr->TRK = (uaptr->TRK + 1) % dsk_tab[dtyp].trkpa; /* incr track */\r |
| 945 | if (uaptr->TRK % dsk_tab[dtyp].trkpc) /* not cyl end? */\r |
| 946 | return ERR_ENDRC; /* don't set end */\r |
| 947 | }\r |
| 948 | ch9_set_end (dsk_ch, 0); /* set end */\r |
| 949 | ch_req |= REQ_CH (dsk_ch); /* request channel */\r |
| 950 | return ERR_ENDRC;\r |
| 951 | }\r |
| 952 | \r |
| 953 | /* Write track back to file */\r |
| 954 | \r |
| 955 | t_stat dsk_wr_trk (UNIT *udptr, uint32 trk)\r |
| 956 | {\r |
| 957 | uint32 dtyp = GET_DTYPE (udptr->flags);\r |
| 958 | uint32 da = DSK_DA (dsk_acc, trk, dtyp);\r |
| 959 | \r |
| 960 | sim_fseek (udptr->fileref, da, SEEK_SET);\r |
| 961 | sim_fwrite (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref);\r |
| 962 | if (ferror (udptr->fileref)) {\r |
| 963 | perror ("DSK I/O error");\r |
| 964 | clearerr (udptr->fileref);\r |
| 965 | dsk_uend (dsk_ch, DSKS_DSKE);\r |
| 966 | return SCPE_IOERR;\r |
| 967 | }\r |
| 968 | return SCPE_OK;\r |
| 969 | }\r |
| 970 | \r |
| 971 | /* Synthesize right attention bit from (access * 10 + module) */\r |
| 972 | \r |
| 973 | t_uint64 dsk_acc_atn (uint32 u)\r |
| 974 | {\r |
| 975 | uint32 g, b;\r |
| 976 | \r |
| 977 | g = u / 4; /* bit group */\r |
| 978 | b = u % 4; /* bit within group */\r |
| 979 | return (DSKS_ATN0 >> ((g * 6) + (b? b + 1: 0)));\r |
| 980 | }\r |
| 981 | \r |
| 982 | /* Get next format character */\r |
| 983 | \r |
| 984 | t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc)\r |
| 985 | {\r |
| 986 | uint32 cc = dsk_fmt_cntr % 6;\r |
| 987 | \r |
| 988 | if (cc == 0) { /* start of word? */\r |
| 989 | if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */\r |
| 990 | else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */\r |
| 991 | }\r |
| 992 | *fc = ((uint8) (dsk_chob >> ((5 - cc) * 6))) & 077; /* get character */\r |
| 993 | if ((cc == 5) && !dsk_stop) ch_req |= REQ_CH (dsk_ch); /* end of word? */\r |
| 994 | if (dsk_fmt_cntr++ >= dsk_tab[dtyp].fchpt) { /* track overflow? */\r |
| 995 | dsk_uend (dsk_ch, DSKS_FMTC); /* format check */\r |
| 996 | return FALSE;\r |
| 997 | }\r |
| 998 | return TRUE;\r |
| 999 | }\r |
| 1000 | \r |
| 1001 | /* Unusual end (set status and stop) */\r |
| 1002 | \r |
| 1003 | t_stat dsk_uend (uint32 ch, t_uint64 stat)\r |
| 1004 | {\r |
| 1005 | dsk_sns |= stat;\r |
| 1006 | dsk_sns &= ~(DSKS_PCHK|DSKS_DCHK|DSKS_EXCC);\r |
| 1007 | if (dsk_sns & DSKS_PALL) dsk_sns |= DSKS_PCHK;\r |
| 1008 | if (dsk_sns & DSKS_DALL) dsk_sns |= DSKS_DCHK;\r |
| 1009 | if (dsk_sns & DSKS_EALL) dsk_sns |= DSKS_EXCC;\r |
| 1010 | ch9_set_end (ch, CHINT_UEND);\r |
| 1011 | ch_req |= REQ_CH (ch);\r |
| 1012 | dsk_sta = DSK_IDLE;\r |
| 1013 | return SCPE_OK;\r |
| 1014 | }\r |
| 1015 | \r |
| 1016 | /* Test for done */\r |
| 1017 | \r |
| 1018 | t_bool dsk_qdone (uint32 ch)\r |
| 1019 | {\r |
| 1020 | if (dsk_stop || !ch9_qconn (ch)) { /* stop or err disc? */\r |
| 1021 | dsk_sta = DSK_IDLE; /* disk is idle */\r |
| 1022 | return TRUE;\r |
| 1023 | }\r |
| 1024 | return FALSE;\r |
| 1025 | }\r |
| 1026 | \r |
| 1027 | /* Reset */\r |
| 1028 | \r |
| 1029 | t_stat dsk_reset (DEVICE *dptr)\r |
| 1030 | {\r |
| 1031 | uint32 i;\r |
| 1032 | UNIT *uptr;\r |
| 1033 | \r |
| 1034 | dsk_acc = 0;\r |
| 1035 | dsk_mod = 0;\r |
| 1036 | dsk_rec = 0;\r |
| 1037 | dsk_mode = 0;\r |
| 1038 | dsk_wchk = 0;\r |
| 1039 | dsk_sns = 0;\r |
| 1040 | dsk_cmd = 0;\r |
| 1041 | dsk_sta = DSK_IDLE;\r |
| 1042 | dsk_rbase = 0;\r |
| 1043 | dsk_rptr = 0;\r |
| 1044 | dsk_rlim = 0;\r |
| 1045 | dsk_stop = 0;\r |
| 1046 | dsk_fmt_cntr = 0;\r |
| 1047 | dsk_chob = 0;\r |
| 1048 | dsk_chob_v = 0;\r |
| 1049 | for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;\r |
| 1050 | for (i = 0; i <= (2 * DSK_NUMDR); i++) {\r |
| 1051 | uptr = dsk_dev.units + i;\r |
| 1052 | sim_cancel (uptr);\r |
| 1053 | uptr->TRK = 0;\r |
| 1054 | uptr->SKF = 0;\r |
| 1055 | }\r |
| 1056 | return SCPE_OK;\r |
| 1057 | }\r |
| 1058 | \r |
| 1059 | /* Attach routine, test formating */\r |
| 1060 | \r |
| 1061 | t_stat dsk_attach (UNIT *uptr, char *cptr)\r |
| 1062 | {\r |
| 1063 | uint32 dtyp = GET_DTYPE (uptr->flags);\r |
| 1064 | t_stat r;\r |
| 1065 | \r |
| 1066 | uptr->capac = dsk_tab[dtyp].size;\r |
| 1067 | r = attach_unit (uptr, cptr);\r |
| 1068 | if (r != SCPE_OK) return r;\r |
| 1069 | uptr->TRK = 0;\r |
| 1070 | uptr->SKF = 0;\r |
| 1071 | uptr->flags &= ~(UNIT_INOP0|UNIT_INOP1);\r |
| 1072 | return dsk_show_format (stdout, uptr, 0, NULL);\r |
| 1073 | }\r |
| 1074 | \r |
| 1075 | /* Set disk size */\r |
| 1076 | \r |
| 1077 | t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 1078 | {\r |
| 1079 | uint32 dtyp = GET_DTYPE (val);\r |
| 1080 | uint32 u = uptr - dsk_dev.units;\r |
| 1081 | UNIT *u1;\r |
| 1082 | \r |
| 1083 | if (u & 1) return SCPE_ARG;\r |
| 1084 | u1 = dsk_dev.units + u + 1;\r |
| 1085 | if ((uptr->flags & UNIT_ATT) || (u1->flags & UNIT_ATT))\r |
| 1086 | return SCPE_ALATT;\r |
| 1087 | if (val == TYPE_7320) u1->flags = (u1->flags & ~UNIT_DISABLE) | UNIT_DIS;\r |
| 1088 | else {\r |
| 1089 | u1->flags = (u1->flags & ~UNIT_TYPE) | val | UNIT_DISABLE;\r |
| 1090 | u1->capac = dsk_tab[dtyp].size;\r |
| 1091 | }\r |
| 1092 | uptr->capac = dsk_tab[dtyp].size;\r |
| 1093 | return SCPE_OK;\r |
| 1094 | }\r |
| 1095 | \r |
| 1096 | /* Show format */\r |
| 1097 | \r |
| 1098 | t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 1099 | {\r |
| 1100 | uint32 a, t, k, u, tlim, dtyp, da;\r |
| 1101 | uint32 rptr, rlnt, rlim, rec, ctptr, *format;\r |
| 1102 | uint32 minrsz = DSK_BUFSIZ;\r |
| 1103 | uint32 maxrsz = 0;\r |
| 1104 | uint32 minrno = DSK_BUFSIZ;\r |
| 1105 | uint32 maxrno = 0;\r |
| 1106 | t_bool ctss;\r |
| 1107 | t_uint64 dbuf[DSK_BUFSIZ];\r |
| 1108 | DEVICE *dptr;\r |
| 1109 | \r |
| 1110 | if (uptr == NULL) return SCPE_IERR;\r |
| 1111 | if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;\r |
| 1112 | dptr = find_dev_from_unit (uptr);\r |
| 1113 | u = uptr - dptr->units;\r |
| 1114 | if (dptr == NULL) return SCPE_IERR;\r |
| 1115 | \r |
| 1116 | dtyp = GET_DTYPE (uptr->flags);\r |
| 1117 | if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301))\r |
| 1118 | format = ctss_fmt_7320;\r |
| 1119 | else format = ctss_fmt_1302;\r |
| 1120 | for (a = 0, ctss = TRUE; a < dsk_tab[dtyp].accpm; a++) {\r |
| 1121 | if (val) tlim = dsk_tab[dtyp].trkpa;\r |
| 1122 | else tlim = 1;\r |
| 1123 | for (t = 0; t < tlim; t++) {\r |
| 1124 | da = DSK_DA (a, t, dtyp); /* get disk address */\r |
| 1125 | sim_fseek (uptr->fileref, da, SEEK_SET); /* read track */\r |
| 1126 | k = sim_fread (dbuf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, uptr->fileref);\r |
| 1127 | if (ferror (uptr->fileref)) return SCPE_IOERR; /* error? */\r |
| 1128 | for ( ; k < dsk_tab[dtyp].wdspt; k++) dbuf[k] = 0;\r |
| 1129 | rptr = T1STREC;\r |
| 1130 | rlnt = (uint32) dbuf[rptr + RLNT];\r |
| 1131 | if (dbuf[THA2] != CTSS_HA2) ctss = FALSE;\r |
| 1132 | if (rlnt == 0) {\r |
| 1133 | if (a || t) fprintf (st,\r |
| 1134 | "Unformatted track, unit = %d, access = %d, track = %d\n", u, a, t);\r |
| 1135 | else fprintf (st, "Unit %d is unformatted\n", u);\r |
| 1136 | return SCPE_OK;\r |
| 1137 | }\r |
| 1138 | for (rec = 0, ctptr = 0; rlnt != 0; rec++) {\r |
| 1139 | if ((format[ctptr] == 0) || format[ctptr++] != rlnt)\r |
| 1140 | ctss = FALSE;\r |
| 1141 | rlim = rptr + rlnt + RDATA;\r |
| 1142 | if (rlim >= dsk_tab[dtyp].wdspt) {\r |
| 1143 | fprintf (st, "Invalid record length %d, unit = %d, access = %d, track = %d, record = %d\n",\r |
| 1144 | rlnt, u, a, t, rec);\r |
| 1145 | return SCPE_OK;\r |
| 1146 | }\r |
| 1147 | if (rlnt > maxrsz) maxrsz = rlnt;\r |
| 1148 | if (rlnt < minrsz) minrsz = rlnt;\r |
| 1149 | rptr = rlim;\r |
| 1150 | rlnt = (uint32) dbuf[rptr + RLNT];\r |
| 1151 | }\r |
| 1152 | if (format[ctptr] != 0) ctss = FALSE;\r |
| 1153 | if (rec > maxrno) maxrno = rec;\r |
| 1154 | if (rec < minrno) minrno = rec;\r |
| 1155 | }\r |
| 1156 | }\r |
| 1157 | if (val == 0) return SCPE_OK;\r |
| 1158 | if (ctss) fprintf (st, "CTSS format\n");\r |
| 1159 | else if ((minrno == maxrno) && (minrsz == maxrsz)) fprintf (st, \r |
| 1160 | "Valid fixed format, records/track = %d, record size = %d\n",\r |
| 1161 | minrno, minrsz);\r |
| 1162 | else if (minrsz == maxrsz) fprintf (st,\r |
| 1163 | "Valid variable format, records/track = %d-%d, record size = %d\n",\r |
| 1164 | minrno, maxrno, minrsz);\r |
| 1165 | else if (minrno == maxrno) fprintf (st,\r |
| 1166 | "Valid variable format, records/track = %d, record sizes = %d-%d\n",\r |
| 1167 | minrno, minrsz, maxrsz);\r |
| 1168 | else fprintf (st,\r |
| 1169 | "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n",\r |
| 1170 | minrno, maxrno, minrsz, maxrsz);\r |
| 1171 | return SCPE_OK;\r |
| 1172 | }\r |