Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* i1401_dp.c: IBM 1311 disk simulator\r |
2 | \r | |
3 | Copyright (c) 2002-2005, 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 | dp 1311 disk pack\r | |
27 | \r | |
28 | 18-Oct-02 RMS Fixed bug in address comparison logic\r | |
29 | 19-Sep-02 RMS Minor edit for consistency with 1620\r | |
30 | 15-Jun-02 RMS Reworked address comparison logic\r | |
31 | \r | |
32 | The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track.\r | |
33 | Each sector contains 106 characters of information:\r | |
34 | \r | |
35 | 6c sector address\r | |
36 | 100c sector data\r | |
37 | \r | |
38 | By default, a sector's address field will be '000000', which is illegal.\r | |
39 | This is interpreted to mean the implied sector number that would be in\r | |
40 | place if the disk pack had been formatted with sequential sector numbers.\r | |
41 | \r | |
42 | The sector data can be 100 characters without word marks, or 90 characters\r | |
43 | with word marks. Load mode transfers 90 characters per sector with\r | |
44 | word marks, move mode transfers 100 characters per sector without word\r | |
45 | marks. No attempt is made to catch incompatible writes (eg, load mode\r | |
46 | write followed by move mode read).\r | |
47 | */\r | |
48 | \r | |
49 | #include "i1401_defs.h"\r | |
50 | \r | |
51 | #define DP_NUMDR 5 /* #drives */\r | |
52 | #define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */\r | |
53 | #define UNIT_WAE (1 << UNIT_V_WAE)\r | |
54 | \r | |
55 | /* Disk format */\r | |
56 | \r | |
57 | #define DP_ADDR 6 /* address */\r | |
58 | #define DP_DATA 100 /* data */\r | |
59 | #define DP_NUMCH (DP_ADDR + DP_DATA)\r | |
60 | \r | |
61 | #define DP_NUMSC 20 /* #sectors */\r | |
62 | #define DP_NUMSF 10 /* #surfaces */\r | |
63 | #define DP_NUMCY 100 /* #cylinders */\r | |
64 | #define DP_TOTSC (DP_NUMCY*DP_NUMSF*DP_NUMSC)\r | |
65 | #define DP_SIZE (DP_TOTSC*DP_NUMCH)\r | |
66 | \r | |
67 | /* Disk control field */\r | |
68 | \r | |
69 | #define DCF_DRV 0 /* drive select */\r | |
70 | #define DCF_SEC 1 /* sector addr */\r | |
71 | #define DCF_SEC_LEN 6\r | |
72 | #define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */\r | |
73 | #define DCF_CNT_LEN 3\r | |
74 | #define DCF_LEN (DCF_CNT + DCF_CNT_LEN)\r | |
75 | #define DCF_DIR 1 /* direct seek */\r | |
76 | #define DCF_DIR_LEN 4\r | |
77 | #define DCF_DIR_FL (DCF_DIR + DCF_DIR_LEN) /* direct seek flag */\r | |
78 | #define DCF_DSEEK 0xB\r | |
79 | \r | |
80 | /* Functions */\r | |
81 | \r | |
82 | #define FNC_SEEK 0 /* seek */\r | |
83 | #define FNC_CHECK 3 /* check */\r | |
84 | #define FNC_READ 1 /* read sectors */\r | |
85 | #define FNC_RSCO 5 /* read sec cnt overlay */\r | |
86 | #define FNC_RTRK 6 /* read track */\r | |
87 | #define FNC_WOFF 10 /* offset for write */\r | |
88 | #define FNC_WRITE 11 /* write sectors */\r | |
89 | #define FNC_WRSCO 15 /* write sec cnt overlay */\r | |
90 | #define FNC_WRTRK 16 /* write track */\r | |
91 | \r | |
92 | #define CYL u3 /* current cylinder */\r | |
93 | \r | |
94 | extern uint8 M[]; /* memory */\r | |
95 | extern int32 ind[64];\r | |
96 | extern int32 AS, BS, iochk;\r | |
97 | extern int32 bcd_to_bin[16];\r | |
98 | extern int32 bin_to_bcd[16];\r | |
99 | extern UNIT cpu_unit;\r | |
100 | \r | |
101 | int32 dp_lastf = 0; /* prior function */\r | |
102 | int32 dp_time = 0; /* seek time */\r | |
103 | \r | |
104 | t_stat dp_reset (DEVICE *dptr);\r | |
105 | t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 wchk);\r | |
106 | t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 wchk);\r | |
107 | t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg);\r | |
108 | t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg);\r | |
109 | int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf);\r | |
110 | t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf);\r | |
111 | t_bool dp_zeroad (uint8 *ap);\r | |
112 | t_bool dp_cmp_ad (uint8 *ap, int32 dcf);\r | |
113 | int32 dp_trkop (int32 drv, int32 sec);\r | |
114 | int32 dp_cvt_bcd (int32 ad, int32 len);\r | |
115 | void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg);\r | |
116 | int32 dp_get_cnt (int32 dcf);\r | |
117 | void dp_fill (UNIT *uptr, uint32 da, int32 cnt);\r | |
118 | \r | |
119 | /* DP data structures\r | |
120 | \r | |
121 | dp_dev DSK device descriptor\r | |
122 | dp_unit DSK unit list\r | |
123 | dp_reg DSK register list\r | |
124 | dp_mod DSK modifier list\r | |
125 | */\r | |
126 | \r | |
127 | UNIT dp_unit[] = {\r | |
128 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r | |
129 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r | |
130 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r | |
131 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r | |
132 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r | |
133 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r | |
134 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r | |
135 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },\r | |
136 | { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +\r | |
137 | UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }\r | |
138 | };\r | |
139 | \r | |
140 | REG dp_reg[] = {\r | |
141 | { FLDATA (ACC, ind[IN_ACC], 0) },\r | |
142 | { FLDATA (PWC, ind[IN_DPW], 0) },\r | |
143 | { FLDATA (WLR, ind[IN_LNG], 0) },\r | |
144 | { FLDATA (UNA, ind[IN_UNA], 0) },\r | |
145 | { FLDATA (ERR, ind[IN_DSK], 0) },\r | |
146 | { FLDATA (BSY, ind[IN_DBY], 0) },\r | |
147 | { DRDATA (LASTF, dp_lastf, 3) },\r | |
148 | { DRDATA (TIME, dp_time, 24), PV_LEFT },\r | |
149 | { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0,\r | |
150 | DP_NUMDR, PV_LEFT + REG_RO) },\r | |
151 | { NULL }\r | |
152 | };\r | |
153 | \r | |
154 | MTAB dp_mod[] = {\r | |
155 | { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL },\r | |
156 | { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL }, \r | |
157 | { 0 }\r | |
158 | };\r | |
159 | \r | |
160 | DEVICE dp_dev = {\r | |
161 | "DP", dp_unit, dp_reg, dp_mod,\r | |
162 | DP_NUMDR, 10, 21, 1, 8, 7,\r | |
163 | NULL, NULL, &dp_reset,\r | |
164 | NULL, NULL, NULL\r | |
165 | };\r | |
166 | \r | |
167 | /* Disk IO routine\r | |
168 | \r | |
169 | Inputs:\r | |
170 | fnc = function character\r | |
171 | flg = load vs move mode\r | |
172 | mod = modifier character\r | |
173 | Outputs:\r | |
174 | status = status\r | |
175 | */\r | |
176 | \r | |
177 | t_stat dp_io (int32 fnc, int32 flg, int32 mod)\r | |
178 | {\r | |
179 | int32 dcf, drv, sec, psec, cnt, qwc, qzr, diff;\r | |
180 | UNIT *uptr;\r | |
181 | t_stat r;\r | |
182 | \r | |
183 | dcf = BS; /* save DCF addr */\r | |
184 | qwc = 0; /* not wcheck */\r | |
185 | ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */\r | |
186 | ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;\r | |
187 | if (sim_is_active (&dp_unit[0])) { /* ctlr busy? */\r | |
188 | ind[IN_DBY] = ind[IN_DSK] = 1; /* set indicators */\r | |
189 | return SCPE_OK; /* done */\r | |
190 | }\r | |
191 | \r | |
192 | AS = dcf + 6; /* AS for most ops */\r | |
193 | BS = dcf + DCF_CNT - 1; /* minimum DCF */\r | |
194 | if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */\r | |
195 | if (M[dcf] & BBIT) drv = M[dcf + DCF_SEC + 1] & 0xE; /* impl sel? cyl 8-4-2 */\r | |
196 | else drv = M[dcf] & DIGIT; /* get drive sel */\r | |
197 | if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */\r | |
198 | return STOP_INVDSK;\r | |
199 | drv = bcd_to_bin[drv] >> 1; /* convert */\r | |
200 | uptr = dp_dev.units + drv; /* get unit ptr */\r | |
201 | if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */\r | |
202 | ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */\r | |
203 | CRETIOE (iochk, SCPE_UNATT);\r | |
204 | }\r | |
205 | \r | |
206 | if ((fnc == FNC_SEEK) && /* seek and */\r | |
207 | (M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */\r | |
208 | diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */\r | |
209 | if (diff < 0) return STOP_INVDSC; /* error? */\r | |
210 | diff = diff >> 1; /* diff is *2 */\r | |
211 | if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT)\r | |
212 | diff = -diff; /* get sign */\r | |
213 | uptr->CYL = uptr->CYL + diff; /* bound seek */\r | |
214 | if (uptr->CYL < 0) uptr->CYL = 0;\r | |
215 | else if (uptr->CYL >= DP_NUMCY) { /* too big? */\r | |
216 | uptr->CYL = 0; /* system hangs */\r | |
217 | return STOP_INVDCY;\r | |
218 | }\r | |
219 | sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */\r | |
220 | return SCPE_OK; /* done! */\r | |
221 | }\r | |
222 | \r | |
223 | sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */\r | |
224 | if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */\r | |
225 | return STOP_INVDSC;\r | |
226 | if (fnc == FNC_SEEK) { /* seek? */\r | |
227 | uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */\r | |
228 | DP_NUMCY;\r | |
229 | sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */\r | |
230 | return SCPE_OK; /* done! */\r | |
231 | }\r | |
232 | \r | |
233 | BS = dcf + DCF_LEN; /* full DCF */\r | |
234 | if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */\r | |
235 | cnt = dp_get_cnt (dcf); /* get count */\r | |
236 | if (cnt < 0) return STOP_INVDCN; /* bad count? */\r | |
237 | \r | |
238 | if (fnc >= FNC_WOFF) return STOP_INVDFN; /* invalid func */\r | |
239 | if (mod == BCD_W) { /* write? */\r | |
240 | if (fnc == FNC_CHECK) { /* write check? */\r | |
241 | qwc = 1; /* special read */\r | |
242 | fnc = dp_lastf; /* use last func */\r | |
243 | }\r | |
244 | else {\r | |
245 | dp_lastf = fnc; /* save func */ \r | |
246 | fnc = fnc + FNC_WOFF; /* change to write */\r | |
247 | }\r | |
248 | }\r | |
249 | else if (mod == BCD_R) dp_lastf = fnc; /* read? save func */\r | |
250 | else return STOP_INVM; /* other? error */\r | |
251 | \r | |
252 | switch (fnc) { /* case on function */\r | |
253 | \r | |
254 | case FNC_RSCO: /* read sec cnt ov */\r | |
255 | BS = dcf + DCF_CNT; /* set count back */\r | |
256 | /* fall thru */\r | |
257 | case FNC_READ: /* read */\r | |
258 | psec = dp_fndsec (uptr, sec, dcf); /* find sector */\r | |
259 | if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */\r | |
260 | for (;;) { /* loop */\r | |
261 | qzr = (--cnt == 0); /* set zero latch */\r | |
262 | dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */\r | |
263 | if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read sector */\r | |
264 | break;\r | |
265 | cnt = dp_get_cnt (dcf); /* get new count */\r | |
266 | if (cnt < 0) return STOP_INVDCN; /* bad count? */\r | |
267 | if (qzr) break; /* zero latch? done */\r | |
268 | sec++; psec++; /* next sector */\r | |
269 | dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */\r | |
270 | if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */\r | |
271 | }\r | |
272 | break; /* done, clean up */\r | |
273 | \r | |
274 | case FNC_RTRK: /* read track */\r | |
275 | AS = dcf + 9; /* special AS */\r | |
276 | psec = dp_trkop (drv, sec); /* start of track */\r | |
277 | for (;;) { /* loop */\r | |
278 | qzr = (--cnt == 0); /* set zero latch */\r | |
279 | dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */\r | |
280 | if (r = dp_rdadr (uptr, psec, flg, qwc)) /* read addr */\r | |
281 | break; /* error? */\r | |
282 | if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read data */\r | |
283 | break; /* error? */\r | |
284 | cnt = dp_get_cnt (dcf); /* get new count */\r | |
285 | if (cnt < 0) return STOP_INVDCN; /* bad count? */\r | |
286 | if (qzr) break; /* zero latch? done */\r | |
287 | psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);\r | |
288 | }\r | |
289 | break; /* done, clean up */ \r | |
290 | \r | |
291 | case FNC_WRSCO: /* write sec cnt ov */\r | |
292 | BS = dcf + DCF_CNT; /* set count back */\r | |
293 | /* fall through */\r | |
294 | case FNC_WRITE: /* read */\r | |
295 | psec = dp_fndsec (uptr, sec, dcf); /* find sector */\r | |
296 | if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */\r | |
297 | for (;;) { /* loop */\r | |
298 | qzr = (--cnt == 0); /* set zero latch */\r | |
299 | dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */\r | |
300 | if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */\r | |
301 | if (qzr) break; /* zero latch? done */\r | |
302 | sec++; psec++; /* next sector */\r | |
303 | dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */\r | |
304 | if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */\r | |
305 | }\r | |
306 | break; /* done, clean up */\r | |
307 | \r | |
308 | case FNC_WRTRK: /* write track */\r | |
309 | if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */\r | |
310 | return STOP_WRADIS;\r | |
311 | AS = dcf + 9; /* special AS */\r | |
312 | psec = dp_trkop (drv, sec); /* start of track */\r | |
313 | for (;;) { /* loop */\r | |
314 | qzr = (--cnt == 0); /* set zero latch */\r | |
315 | dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */\r | |
316 | if (r = dp_wradr (uptr, psec, flg)) break; /* write addr */\r | |
317 | if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */\r | |
318 | if (qzr) break; /* zero latch? done */\r | |
319 | psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC);\r | |
320 | }\r | |
321 | break; /* done, clean up */\r | |
322 | \r | |
323 | default: /* unknown */\r | |
324 | return STOP_INVDFN;\r | |
325 | }\r | |
326 | \r | |
327 | if (r == SCPE_OK) { /* normal so far? */\r | |
328 | BS++; /* advance BS */\r | |
329 | if (ADDR_ERR (BS)) return STOP_WRAP; /* address error? */\r | |
330 | if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */\r | |
331 | ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */\r | |
332 | r = STOP_INVDLN;\r | |
333 | }\r | |
334 | }\r | |
335 | CRETIOE (iochk || !ind[IN_DSK], r); /* return status */\r | |
336 | }\r | |
337 | \r | |
338 | /* Read or compare address with memory */\r | |
339 | \r | |
340 | t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc)\r | |
341 | {\r | |
342 | int32 i;\r | |
343 | uint8 ac;\r | |
344 | int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */\r | |
345 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r | |
346 | t_bool zad = dp_zeroad (ap); /* zero address */\r | |
347 | static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */\r | |
348 | 100000, 10000, 1000, 100, 10, 1\r | |
349 | } ;\r | |
350 | \r | |
351 | for (i = 0; i < DP_ADDR; i++) { /* copy address */\r | |
352 | if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */\r | |
353 | ind[IN_LNG] = ind[IN_DSK] = 1; /* error */\r | |
354 | return STOP_INVDLN;\r | |
355 | }\r | |
356 | if (zad) { /* addr zero? */\r | |
357 | ac = sec / dec_tab[i]; /* get addr digit */\r | |
358 | sec = sec % dec_tab[i]; /* get remainder */\r | |
359 | ac = bcd_to_bin[ac]; /* cvt to BCD */\r | |
360 | }\r | |
361 | else ac = *ap; /* addr char */\r | |
362 | if (qwc) { /* wr chk? skip if zad */\r | |
363 | if (!zad && (flg? (M[BS] != ac): /* L? cmp with WM */\r | |
364 | ((M[BS] & CHAR) != (ac & CHAR)))) { /* M? cmp w/o WM */\r | |
365 | ind[IN_DPW] = ind[IN_DSK] = 1;\r | |
366 | return STOP_WRCHKE;\r | |
367 | }\r | |
368 | }\r | |
369 | else if (flg) M[BS] = ac & CHAR; /* load mode */\r | |
370 | else M[BS] = (M[BS] & WM) | (ac & CHAR); /* move mode */\r | |
371 | ap++; BS++; /* adv ptrs */\r | |
372 | if (ADDR_ERR (BS)) return STOP_WRAP;\r | |
373 | }\r | |
374 | return SCPE_OK;\r | |
375 | }\r | |
376 | \r | |
377 | /* Read or compare data with memory */\r | |
378 | \r | |
379 | t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc)\r | |
380 | {\r | |
381 | int32 i, lim;\r | |
382 | int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */\r | |
383 | uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */\r | |
384 | \r | |
385 | lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */\r | |
386 | for (i = 0; i < lim; i++) { /* copy data */\r | |
387 | if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */\r | |
388 | ind[IN_LNG] = ind[IN_DSK] = 1; /* error */\r | |
389 | return STOP_INVDLN;\r | |
390 | }\r | |
391 | if (qwc) { /* write check? */\r | |
392 | if (flg? (M[BS] != *ap): /* load mode cmp */\r | |
393 | ((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */\r | |
394 | ind[IN_DPW] = ind[IN_DSK] = 1; /* error */\r | |
395 | return STOP_WRCHKE;\r | |
396 | }\r | |
397 | }\r | |
398 | else if (flg) M[BS] = *ap & (WM | CHAR); /* load mode */\r | |
399 | else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */\r | |
400 | ap++; BS++; /* adv ptrs */\r | |
401 | if (ADDR_ERR (BS)) return STOP_WRAP;\r | |
402 | }\r | |
403 | return SCPE_OK;\r | |
404 | }\r | |
405 | \r | |
406 | /* Write address to disk */\r | |
407 | \r | |
408 | t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg)\r | |
409 | {\r | |
410 | int32 i;\r | |
411 | uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */\r | |
412 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r | |
413 | \r | |
414 | for (i = 0; i < DP_ADDR; i++) { /* copy address */\r | |
415 | if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */\r | |
416 | dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */ \r | |
417 | ind[IN_LNG] = ind[IN_DSK] = 1; /* error */\r | |
418 | return STOP_INVDLN;\r | |
419 | }\r | |
420 | if (flg) *ap = M[BS] & (WM | CHAR); /* L? copy WM */\r | |
421 | else *ap = M[BS] & CHAR; /* M? strip WM */\r | |
422 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r | |
423 | da++; ap++; BS++; /* adv ptrs */\r | |
424 | if (ADDR_ERR (BS)) return STOP_WRAP;\r | |
425 | }\r | |
426 | return SCPE_OK;\r | |
427 | }\r | |
428 | \r | |
429 | /* Write data to disk */\r | |
430 | \r | |
431 | t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg)\r | |
432 | {\r | |
433 | int32 i, lim;\r | |
434 | uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */\r | |
435 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r | |
436 | \r | |
437 | lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */\r | |
438 | for (i = 0; i < lim; i++) { /* copy data */\r | |
439 | if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */\r | |
440 | dp_fill (uptr, da, DP_DATA - i); /* fill, set err */\r | |
441 | ind[IN_LNG] = ind[IN_DSK] = 1; /* error */\r | |
442 | return STOP_INVDLN;\r | |
443 | }\r | |
444 | if (flg) *ap = M[BS] & (WM | CHAR); /* load, copy WM */\r | |
445 | else *ap = M[BS] & CHAR; /* move, strip WM */\r | |
446 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r | |
447 | da++; ap++; BS++; /* adv ptrs */\r | |
448 | if (ADDR_ERR (BS)) return STOP_WRAP;\r | |
449 | }\r | |
450 | return SCPE_OK;\r | |
451 | }\r | |
452 | \r | |
453 | /* Find sector */\r | |
454 | \r | |
455 | int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf)\r | |
456 | {\r | |
457 | int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */\r | |
458 | int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;\r | |
459 | int32 da = psec * DP_NUMCH; /* char number */\r | |
460 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r | |
461 | int32 i;\r | |
462 | \r | |
463 | if (dp_zeroad (ap)) return psec; /* addr zero? ok */\r | |
464 | if (dp_cmp_ad (ap, dcf)) return psec; /* addr comp? ok */\r | |
465 | psec = psec - (psec % DP_NUMSC); /* sector 0 */\r | |
466 | for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */\r | |
467 | da = psec * DP_NUMCH; /* char number */\r | |
468 | ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */\r | |
469 | if (dp_zeroad (ap)) continue; /* no implicit match */\r | |
470 | if (dp_cmp_ad (ap, dcf)) return psec; /* match? */\r | |
471 | }\r | |
472 | ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */\r | |
473 | return -1;\r | |
474 | }\r | |
475 | \r | |
476 | /* Find next sector - must be sequential, cannot cross cylinder boundary */\r | |
477 | \r | |
478 | t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf)\r | |
479 | {\r | |
480 | int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */\r | |
481 | int32 da = psec * DP_NUMCH; /* word number */\r | |
482 | uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */\r | |
483 | \r | |
484 | if (ctrk) { /* not trk zero? */\r | |
485 | if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */\r | |
486 | if (dp_cmp_ad (ap, dcf)) return SCPE_OK; /* addr comp? ok */\r | |
487 | }\r | |
488 | ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */\r | |
489 | return STOP_INVDAD;\r | |
490 | }\r | |
491 | \r | |
492 | /* Test for zero address */\r | |
493 | \r | |
494 | t_bool dp_zeroad (uint8 *ap)\r | |
495 | {\r | |
496 | int32 i;\r | |
497 | \r | |
498 | for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */\r | |
499 | if (*ap & CHAR) return FALSE; /* nonzero? lose */\r | |
500 | }\r | |
501 | return TRUE; /* all zeroes */\r | |
502 | }\r | |
503 | \r | |
504 | /* Compare disk address to memory sector address - always omit word marks */\r | |
505 | \r | |
506 | t_bool dp_cmp_ad (uint8 *ap, int32 dcf)\r | |
507 | {\r | |
508 | int32 i;\r | |
509 | uint8 c;\r | |
510 | \r | |
511 | for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */\r | |
512 | c = M[dcf + DCF_SEC + i]; /* sector addr char */\r | |
513 | if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */\r | |
514 | return FALSE;\r | |
515 | }\r | |
516 | return TRUE; /* compare ok */\r | |
517 | }\r | |
518 | \r | |
519 | /* Track operation setup */\r | |
520 | \r | |
521 | int32 dp_trkop (int32 drv, int32 sec)\r | |
522 | {\r | |
523 | int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;\r | |
524 | \r | |
525 | return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +\r | |
526 | (ctrk * DP_NUMSC));\r | |
527 | }\r | |
528 | \r | |
529 | /* Convert DCF BCD field to binary */\r | |
530 | \r | |
531 | int32 dp_cvt_bcd (int32 ad, int32 len)\r | |
532 | {\r | |
533 | uint8 c;\r | |
534 | int32 r;\r | |
535 | \r | |
536 | for (r = 0; len > 0; len--) { /* loop thru char */\r | |
537 | c = M[ad] & DIGIT; /* get digit */\r | |
538 | if ((c == 0) || (c > BCD_ZERO)) return -1; /* invalid? */\r | |
539 | r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */\r | |
540 | ad++; /* next digit */\r | |
541 | }\r | |
542 | return r;\r | |
543 | }\r | |
544 | \r | |
545 | /* Convert binary to DCF BCD field */\r | |
546 | \r | |
547 | void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg)\r | |
548 | {\r | |
549 | int32 r;\r | |
550 | \r | |
551 | for ( ; len > 0; len--) { /* loop thru char */\r | |
552 | r = val % 10; /* get digit */\r | |
553 | if (flg) M[ad + len - 1] = bin_to_bcd[r]; /* load mode? */\r | |
554 | else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r];\r | |
555 | val = val / 10;\r | |
556 | }\r | |
557 | return;\r | |
558 | } \r | |
559 | \r | |
560 | /* Get and validate count */\r | |
561 | \r | |
562 | int32 dp_get_cnt (int32 dcf)\r | |
563 | {\r | |
564 | int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */\r | |
565 | if (cnt < 0) return -1; /* bad count? */\r | |
566 | if (cnt == 0) return 1000; /* 0 => 1000 */\r | |
567 | return cnt;\r | |
568 | }\r | |
569 | \r | |
570 | /* Fill sector buffer with blanks */\r | |
571 | \r | |
572 | void dp_fill (UNIT *uptr, uint32 da, int32 cnt)\r | |
573 | {\r | |
574 | while (cnt-- > 0) { /* fill with blanks */\r | |
575 | *(((uint8 *) uptr->filebuf) + da) = BCD_BLANK;\r | |
576 | if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r | |
577 | da++;\r | |
578 | }\r | |
579 | return;\r | |
580 | }\r | |
581 | \r | |
582 | /* Reset routine */\r | |
583 | \r | |
584 | t_stat dp_reset (DEVICE *dptr)\r | |
585 | {\r | |
586 | int32 i;\r | |
587 | \r | |
588 | for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */\r | |
589 | dp_lastf = 0; /* clear state */\r | |
590 | ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */\r | |
591 | ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;\r | |
592 | sim_cancel (&dp_unit[0]); /* cancel timer */\r | |
593 | return SCPE_OK;\r | |
594 | }\r |