This source file includes following definitions.
- sim_disk_set_fmt
- sim_disk_show_fmt
- sim_disk_set_capac
- sim_disk_show_capac
- sim_disk_isavailable
- sim_disk_isavailable_a
- sim_disk_wrp
- sim_disk_size
- _sim_disk_rdsect
- sim_disk_rdsect
- sim_disk_rdsect_a
- _sim_disk_wrsect
- sim_disk_wrsect
- sim_disk_wrsect_a
- sim_disk_unload
- _sim_disk_io_flush
- _err_return
- ODS2Checksum
- get_filesystem_size
- sim_disk_attach
- sim_disk_detach
- sim_disk_attach_help
- sim_disk_reset
- sim_disk_perror
- sim_disk_clearerr
- sim_disk_data_trace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 #define _FILE_OFFSET_BITS 64
62
63 #include "sim_defs.h"
64 #include "sim_disk.h"
65
66 #include <ctype.h>
67 #include <signal.h>
68 #include <sys/stat.h>
69
70 #include "../decNumber/decContext.h"
71 #include "../decNumber/decNumberLocal.h"
72
73 #if defined(_WIN32)
74 # include <windows.h>
75 #endif
76
77 #if !defined(DECLITEND)
78 # error Unknown platform endianness
79 #endif
80
81 #if defined(FREE)
82 # undef FREE
83 #endif
84 #define FREE(p) do \
85 { \
86 free((p)); \
87 (p) = NULL; \
88 } while(0)
89
90 struct disk_context {
91 DEVICE *dptr;
92 uint32 dbit;
93 uint32 sector_size;
94 uint32 capac_factor;
95 uint32 xfer_element_size;
96 uint32 storage_sector_size;
97 uint32 removable;
98 uint32 auto_format;
99 };
100
101 #define disk_ctx up8
102
103
104
105 struct sim_disk_fmt {
106 const char *name;
107 int32 uflags;
108 int32 fmtval;
109 t_stat (*impl_fnc)(void);
110 };
111
112 static struct sim_disk_fmt fmts[DKUF_N_FMT] = {
113 { "SIMH", 0, DKUF_F_STD, NULL},
114 { NULL, 0, 0, 0 } };
115
116
117
118 t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
119 {
120 uint32 f;
121
122 if (uptr == NULL)
123 return SCPE_IERR;
124 if (cptr == NULL)
125 return SCPE_ARG;
126 for (f = 0; f < DKUF_N_FMT && fmts[f].name; f++) {
127 if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) {
128 if ((fmts[f].impl_fnc) && (fmts[f].impl_fnc() != SCPE_OK))
129 return SCPE_NOFNC;
130 uptr->flags = (uptr->flags & ~DKUF_FMT) |
131 (fmts[f].fmtval << DKUF_V_FMT) | fmts[f].uflags;
132 return SCPE_OK;
133 }
134 }
135 return SCPE_ARG;
136 }
137
138
139
140 t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
141 {
142 int32 f = DK_GET_FMT (uptr);
143 size_t i;
144
145 for (i = 0; i < DKUF_N_FMT; i++)
146 if (fmts[i].fmtval == f) {
147 fprintf (st, "%s format", fmts[i].name);
148 return SCPE_OK;
149 }
150 fprintf (st, "invalid format");
151 return SCPE_OK;
152 }
153
154
155
156 t_stat sim_disk_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
157 {
158 t_offset cap;
159 t_stat r;
160 DEVICE *dptr = find_dev_from_unit (uptr);
161
162 if ((cptr == NULL) || (*cptr == 0))
163 return SCPE_ARG;
164 if (uptr->flags & UNIT_ATT)
165 return SCPE_ALATT;
166 cap = (t_offset) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r);
167 if (r != SCPE_OK)
168 return SCPE_ARG;
169 uptr->capac = (t_addr)((cap * ((t_offset) 1000000))/((dptr->flags & DEV_SECTORS) ? 512 : 1));
170 return SCPE_OK;
171 }
172
173
174
175 t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
176 {
177 const char *cap_units = "B";
178 DEVICE *dptr = find_dev_from_unit (uptr);
179 t_offset capac = ((t_offset)uptr->capac)*((dptr->flags & DEV_SECTORS) ? 512 : 1);
180
181 if ((dptr->dwidth / dptr->aincr) == 16)
182 cap_units = "W";
183 if (capac) {
184 if (capac >= (t_offset) 1000000)
185 fprintf (st, "capacity=%luM%s", (unsigned long) (capac / ((t_offset) 1000000)), cap_units);
186 else if (uptr->capac >= (t_addr) 1000)
187 fprintf (st, "capacity=%luK%s", (unsigned long) (capac / ((t_offset) 1000)), cap_units);
188 else fprintf (st, "capacity=%lu%s", (unsigned long) capac, cap_units);
189 }
190 else fprintf (st, "undefined capacity");
191 return SCPE_OK;
192 }
193
194
195
196 t_bool sim_disk_isavailable (UNIT *uptr)
197 {
198 if (!(uptr->flags & UNIT_ATT))
199 return FALSE;
200 switch (DK_GET_FMT (uptr)) {
201 case DKUF_F_STD:
202 return TRUE;
203
204 break;
205 default:
206 return FALSE;
207 }
208 }
209
210 t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback)
211 {
212 t_bool r = FALSE;
213 r = sim_disk_isavailable (uptr);
214 return r;
215 }
216
217
218
219 t_bool sim_disk_wrp (UNIT *uptr)
220 {
221 return (uptr->flags & DKUF_WRP)? TRUE: FALSE;
222 }
223
224
225
226 t_offset sim_disk_size (UNIT *uptr)
227 {
228 switch (DK_GET_FMT (uptr)) {
229 case DKUF_F_STD:
230 return sim_fsize_ex (uptr->fileref);
231
232 break;
233 default:
234 return (t_offset)-1;
235 }
236 }
237
238
239
240 static t_stat _sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
241 {
242 t_offset da;
243 uint32 err, tbc;
244 size_t i;
245 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
246
247 sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_rdsect(unit=%lu, lba=0x%X, sects=%lu)\n",
248 (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
249
250 da = ((t_offset)lba) * ctx->sector_size;
251 tbc = sects * ctx->sector_size;
252 if (sectsread)
253 *sectsread = 0;
254 err = sim_fseeko (uptr->fileref, da, SEEK_SET);
255 if (!err) {
256 i = sim_fread (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref);
257 if (i < tbc/ctx->xfer_element_size)
258 (void)memset (&buf[i*ctx->xfer_element_size], 0, tbc-(i*ctx->xfer_element_size));
259 err = ferror (uptr->fileref);
260 if ((!err) && (sectsread))
261 *sectsread = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size);
262 }
263 return err;
264 }
265
266 t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects)
267 {
268 t_stat r;
269 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
270 t_seccnt sread = 0;
271
272 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_rdsect(unit=%lu, lba=0x%X, sects=%lu)\n",
273 (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
274
275 if ((sects == 1) &&
276 (lba >= (uptr->capac*ctx->capac_factor)/(ctx->sector_size/((ctx->dptr->flags & DEV_SECTORS) ? 512 : 1)))) {
277 (void)memset (buf, '\0', ctx->sector_size);
278 if (sectsread)
279 *sectsread = 1;
280 return SCPE_OK;
281 }
282
283 if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) ||
284 ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) &&
285 (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) {
286 switch (DK_GET_FMT (uptr)) {
287 case DKUF_F_STD:
288 return _sim_disk_rdsect (uptr, lba, buf, sectsread, sects);
289
290 break;
291 default:
292 return SCPE_NOFNC;
293 }
294
295
296
297
298
299
300 }
301 else {
302 uint8 *tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size);
303 t_lba sspsts = ctx->storage_sector_size/ctx->sector_size;
304 t_lba tlba = lba & ~(sspsts - 1);
305 t_seccnt tsects = sects + (lba - tlba);
306
307 tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1);
308 if (sectsread)
309 *sectsread = 0;
310 if (tbuf == NULL)
311 return SCPE_MEM;
312 switch (DK_GET_FMT (uptr)) {
313 case DKUF_F_STD:
314 r = _sim_disk_rdsect (uptr, tlba, tbuf, &sread, tsects);
315 break;
316 default:
317 FREE (tbuf);
318 return SCPE_NOFNC;
319 }
320 if (r == SCPE_OK) {
321 memcpy (buf, tbuf + ((lba - tlba) * ctx->sector_size), sects * ctx->sector_size);
322 if (sectsread) {
323 *sectsread = sread - (lba - tlba);
324 if (*sectsread > sects)
325 *sectsread = sects;
326 }
327 }
328 FREE (tbuf);
329 return r;
330 }
331 }
332
333 t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback)
334 {
335 t_stat r = SCPE_OK;
336 r = sim_disk_rdsect (uptr, lba, buf, sectsread, sects);
337 return r;
338 }
339
340
341
342 static t_stat _sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
343 {
344 t_offset da;
345 uint32 err, tbc;
346 size_t i;
347 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
348
349 sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_wrsect(unit=%lu, lba=0x%X, sects=%lu)\n",
350 (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
351
352 da = ((t_offset)lba) * ctx->sector_size;
353 tbc = sects * ctx->sector_size;
354 if (sectswritten)
355 *sectswritten = 0;
356 err = sim_fseeko (uptr->fileref, da, SEEK_SET);
357 if (!err) {
358 i = sim_fwrite (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref);
359 err = ferror (uptr->fileref);
360 if ((!err) && (sectswritten))
361 *sectswritten = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size);
362 }
363 return err;
364 }
365
366 t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects)
367 {
368 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
369 uint32 f = DK_GET_FMT (uptr);
370 t_stat r;
371 uint8 *tbuf = NULL;
372
373 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_wrsect(unit=%lu, lba=0x%X, sects=%lu)\n",
374 (unsigned long)(uptr-ctx->dptr->units), lba, (unsigned long)sects);
375
376 if (uptr->dynflags & UNIT_DISK_CHK) {
377 DEVICE *dptr = find_dev_from_unit (uptr);
378 uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1;
379 t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(ctx->sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
380 t_lba sect;
381
382 for (sect = 0; sect < sects; sect++) {
383 t_lba offset;
384 t_bool sect_error = FALSE;
385
386 for (offset = 0; offset < ctx->sector_size; offset += sizeof(uint32)) {
387 if (*((uint32 *)&buf[sect*ctx->sector_size + offset]) != (uint32)(lba + sect)) {
388 sect_error = TRUE;
389 break;
390 }
391 }
392 if (sect_error) {
393 uint32 save_dctrl = dptr->dctrl;
394 FILE *save_sim_deb = sim_deb;
395
396 sim_printf ("\n%s%lu: Write Address Verification Error on lbn %lu(0x%X) of %lu(0x%X).\n",
397 sim_dname (dptr), (unsigned long)(uptr-dptr->units),
398 (unsigned long)((unsigned long)lba+(unsigned long)sect),
399 (int)((int)lba+(int)sect), (unsigned long)total_sectors, (int)total_sectors);
400 dptr->dctrl = 0xFFFFFFFF;
401 sim_deb = save_sim_deb ? save_sim_deb : stdout;
402 sim_disk_data_trace (uptr, buf+sect*ctx->sector_size, lba+sect, ctx->sector_size, "Found", TRUE, 1);
403 dptr->dctrl = save_dctrl;
404 sim_deb = save_sim_deb;
405 }
406 }
407 }
408 if (f == DKUF_F_STD)
409 return _sim_disk_wrsect (uptr, lba, buf, sectswritten, sects);
410 if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) ||
411 ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) &&
412 (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) {
413 sim_end = DECLITEND;
414 if (sim_end || (ctx->xfer_element_size == sizeof (char)))
415 switch (DK_GET_FMT (uptr)) {
416 default:
417 return SCPE_NOFNC;
418 }
419
420 tbuf = (uint8*) malloc (sects * ctx->sector_size);
421 if (NULL == tbuf)
422 return SCPE_MEM;
423 sim_buf_copy_swapped (tbuf, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size);
424
425 switch (DK_GET_FMT (uptr)) {
426 default:
427 r = SCPE_NOFNC;
428 break;
429 }
430 }
431 else {
432 t_lba sspsts = ctx->storage_sector_size/ctx->sector_size;
433 t_lba tlba = lba & ~(sspsts - 1);
434 t_seccnt tsects = sects + (lba - tlba);
435
436 tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size);
437 tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1);
438 if (sectswritten)
439 *sectswritten = 0;
440 if (tbuf == NULL)
441 return SCPE_MEM;
442
443 if ((lba & (sspsts - 1)) ||
444 (sects < sspsts))
445 switch (DK_GET_FMT (uptr)) {
446 default:
447 r = SCPE_NOFNC;
448 break;
449 }
450 if ((tsects > sspsts) &&
451 ((sects + lba - tlba) & (sspsts - 1)))
452 switch (DK_GET_FMT (uptr)) {
453 default:
454 r = SCPE_NOFNC;
455 break;
456 }
457 sim_buf_copy_swapped (tbuf + (lba & (sspsts - 1)) * ctx->sector_size,
458 buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size);
459 switch (DK_GET_FMT (uptr)) {
460 default:
461 r = SCPE_NOFNC;
462 break;
463 }
464 if ((r == SCPE_OK) && sectswritten) {
465 *sectswritten -= (lba - tlba);
466 if (*sectswritten > sects)
467 *sectswritten = sects;
468 }
469 }
470 FREE (tbuf);
471 return r;
472 }
473
474 t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback)
475 {
476 t_stat r = SCPE_OK;
477 r = sim_disk_wrsect (uptr, lba, buf, sectswritten, sects);
478 return r;
479 }
480
481 t_stat sim_disk_unload (UNIT *uptr)
482 {
483 switch (DK_GET_FMT (uptr)) {
484 case DKUF_F_STD:
485 return sim_disk_detach (uptr);
486 default:
487 return SCPE_NOFNC;
488 }
489 }
490
491 static void _sim_disk_io_flush (UNIT *uptr)
492 {
493 uint32 f = DK_GET_FMT (uptr);
494
495 switch (f) {
496 case DKUF_F_STD:
497 (void)fflush (uptr->fileref);
498 break;
499 }
500 }
501
502 static t_stat _err_return (UNIT *uptr, t_stat stat)
503 {
504 FREE (uptr->filename);
505 uptr->filename = NULL;
506 FREE (uptr->disk_ctx);
507 uptr->disk_ctx = NULL;
508 return stat;
509 }
510
511 #if defined(__xlc__)
512 # pragma pack(1)
513 #else
514 # pragma pack(push,1)
515 #endif
516 typedef struct _ODS2_HomeBlock
517 {
518 uint32 hm2_l_homelbn;
519 uint32 hm2_l_alhomelbn;
520 uint32 hm2_l_altidxlbn;
521 uint8 hm2_b_strucver;
522 uint8 hm2_b_struclev;
523 uint16 hm2_w_cluster;
524 uint16 hm2_w_homevbn;
525 uint16 hm2_w_alhomevbn;
526 uint16 hm2_w_altidxvbn;
527 uint16 hm2_w_ibmapvbn;
528 uint32 hm2_l_ibmaplbn;
529 uint32 hm2_l_maxfiles;
530 uint16 hm2_w_ibmapsize;
531 uint16 hm2_w_resfiles;
532 uint16 hm2_w_devtype;
533 uint16 hm2_w_rvn;
534 uint16 hm2_w_setcount;
535 uint16 hm2_w_volchar;
536 uint32 hm2_l_volowner;
537 uint32 hm2_l_reserved;
538 uint16 hm2_w_protect;
539 uint16 hm2_w_fileprot;
540 uint16 hm2_w_reserved;
541 uint16 hm2_w_checksum1;
542 uint32 hm2_q_credate[2];
543 uint8 hm2_b_window;
544 uint8 hm2_b_lru_lim;
545 uint16 hm2_w_extend;
546 uint32 hm2_q_retainmin[2];
547 uint32 hm2_q_retainmax[2];
548 uint32 hm2_q_revdate[2];
549 uint8 hm2_r_min_class[20];
550 uint8 hm2_r_max_class[20];
551 uint8 hm2_r_reserved[320];
552 uint32 hm2_l_serialnum;
553 uint8 hm2_t_strucname[12];
554 uint8 hm2_t_volname[12];
555 uint8 hm2_t_ownername[12];
556 uint8 hm2_t_format[12];
557 uint16 hm2_w_reserved2;
558 uint16 hm2_w_checksum2;
559 } ODS2_HomeBlock;
560
561 typedef struct _ODS2_FileHeader
562 {
563 uint8 fh2_b_idoffset;
564 uint8 fh2_b_mpoffset;
565 uint8 fh2_b_acoffset;
566 uint8 fh2_b_rsoffset;
567 uint16 fh2_w_seg_num;
568 uint16 fh2_w_structlev;
569 uint16 fh2_w_fid[3];
570 uint16 fh2_w_ext_fid[3];
571 uint16 fh2_w_recattr[16];
572 uint32 fh2_l_filechar;
573 uint16 fh2_w_remaining[228];
574 } ODS2_FileHeader;
575
576 typedef union _ODS2_Retreval
577 {
578 struct
579 {
580 unsigned fm2___fill : 14;
581 unsigned fm2_v_format : 2;
582 } fm2_r_word0_bits;
583 struct
584 {
585 unsigned fm2_v_exact : 1;
586 unsigned fm2_v_oncyl : 1;
587 unsigned fm2___fill : 10;
588 unsigned fm2_v_lbn : 1;
589 unsigned fm2_v_rvn : 1;
590 unsigned fm2_v_format0 : 2;
591 } fm2_r_map_bits0;
592 struct
593 {
594 unsigned fm2_b_count1 : 8;
595 unsigned fm2_v_highlbn1 : 6;
596 unsigned fm2_v_format1 : 2;
597 unsigned fm2_w_lowlbn1 : 16;
598 } fm2_r_map_bits1;
599 struct
600 {
601 struct
602 {
603 unsigned fm2_v_count2 : 14;
604 unsigned fm2_v_format2 : 2;
605 unsigned fm2_l_lowlbn2 : 16;
606 } fm2_r_map2_long0;
607 uint16 fm2_l_highlbn2;
608 } fm2_r_map_bits2;
609 struct
610 {
611 struct
612 {
613 unsigned fm2_v_highcount3 : 14;
614 unsigned fm2_v_format3 : 2;
615 unsigned fm2_w_lowcount3 : 16;
616 } fm2_r_map3_long0;
617 uint32 fm2_l_lbn3;
618 } fm2_r_map_bits3;
619 } ODS2_Retreval;
620
621 typedef struct _ODS2_StorageControlBlock
622 {
623 uint8 scb_b_strucver;
624 uint8 scb_b_struclev;
625 uint16 scb_w_cluster;
626 uint32 scb_l_volsize;
627 uint32 scb_l_blksize;
628 uint32 scb_l_sectors;
629 uint32 scb_l_tracks;
630 uint32 scb_l_cylinder;
631 uint32 scb_l_status;
632 uint32 scb_l_status2;
633 uint16 scb_w_writecnt;
634 uint8 scb_t_volockname[12];
635 uint32 scb_q_mounttime[2];
636 uint16 scb_w_backrev;
637 uint32 scb_q_genernum[2];
638 uint8 scb_b_reserved[446];
639 uint16 scb_w_checksum;
640 } ODS2_SCB;
641 #if defined(__xlc__)
642 # pragma pack(reset)
643 #else
644 # pragma pack(pop)
645 #endif
646
647 static uint16
648 ODS2Checksum (void *Buffer, uint16 WordCount)
649 {
650 int i;
651 uint16 CheckSum = 0;
652 uint16 *Buf = (uint16 *)Buffer;
653
654 for (i=0; i<WordCount; i++)
655 CheckSum += Buf[i];
656 return CheckSum;
657 }
658
659 static t_offset get_filesystem_size (UNIT *uptr)
660 {
661 DEVICE *dptr;
662 t_addr saved_capac;
663 t_offset temp_capac = 512 * (t_offset)0xFFFFFFFFu;
664 uint32 capac_factor;
665 ODS2_HomeBlock Home;
666 ODS2_FileHeader Header;
667 ODS2_Retreval *Retr;
668 ODS2_SCB Scb;
669 uint16 CheckSum1, CheckSum2;
670 uint32 ScbLbn = 0;
671 t_offset ret_val = (t_offset)-1;
672
673 if ((dptr = find_dev_from_unit (uptr)) == NULL)
674 return ret_val;
675 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1;
676 saved_capac = uptr->capac;
677
678 uptr->capac = (t_addr)(temp_capac/(capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
679 if (sim_disk_rdsect (uptr, 1, (uint8 *)&Home, NULL, 1))
680 goto Return_Cleanup;
681
682 CheckSum1 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum1)-((char *)&Home.hm2_l_homelbn))/2));
683
684 CheckSum2 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum2)-((char *)&Home.hm2_l_homelbn))/2));
685 if ((Home.hm2_l_homelbn == 0) ||
686 (Home.hm2_l_alhomelbn == 0) ||
687 (Home.hm2_l_altidxlbn == 0) ||
688 ((Home.hm2_b_struclev != 2) &&
689 (Home.hm2_b_struclev != 5)) ||
690 (Home.hm2_b_strucver == 0) ||
691 (Home.hm2_w_cluster == 0) ||
692 (Home.hm2_w_homevbn == 0) ||
693 (Home.hm2_w_alhomevbn == 0) ||
694 (Home.hm2_w_ibmapvbn == 0) ||
695 (Home.hm2_l_ibmaplbn == 0) ||
696 (Home.hm2_w_resfiles >= Home.hm2_l_maxfiles) ||
697 (Home.hm2_w_ibmapsize == 0) ||
698 (Home.hm2_w_resfiles < 5) ||
699 (Home.hm2_w_checksum1 != CheckSum1) ||
700 (Home.hm2_w_checksum2 != CheckSum2))
701 goto Return_Cleanup;
702 if (sim_disk_rdsect (uptr, Home.hm2_l_ibmaplbn+Home.hm2_w_ibmapsize+1, (uint8 *)&Header, NULL, 1))
703 goto Return_Cleanup;
704 CheckSum1 = ODS2Checksum (&Header, 255);
705 if (CheckSum1 != *(((uint16 *)&Header)+255))
706 goto Return_Cleanup;
707 Retr = (ODS2_Retreval *)(((uint16*)(&Header))+Header.fh2_b_mpoffset);
708
709 if (Retr->fm2_r_word0_bits.fm2_v_format == 0)
710 Retr = (ODS2_Retreval *)(((uint16 *)Retr)+1);
711 switch (Retr->fm2_r_word0_bits.fm2_v_format)
712 {
713 case 1:
714 ScbLbn = (Retr->fm2_r_map_bits1.fm2_v_highlbn1<<16)+Retr->fm2_r_map_bits1.fm2_w_lowlbn1;
715 break;
716 case 2:
717 ScbLbn = (Retr->fm2_r_map_bits2.fm2_l_highlbn2<<16)+Retr->fm2_r_map_bits2.fm2_r_map2_long0.fm2_l_lowlbn2;
718 break;
719 case 3:
720 ScbLbn = Retr->fm2_r_map_bits3.fm2_l_lbn3;
721 break;
722 }
723 Retr = (ODS2_Retreval *)(((uint16 *)Retr)+Retr->fm2_r_word0_bits.fm2_v_format+1);
724 if (sim_disk_rdsect (uptr, ScbLbn, (uint8 *)&Scb, NULL, 1))
725 goto Return_Cleanup;
726 CheckSum1 = ODS2Checksum (&Scb, 255);
727 if (CheckSum1 != *(((uint16 *)&Scb)+255))
728 goto Return_Cleanup;
729 if ((Scb.scb_w_cluster != Home.hm2_w_cluster) ||
730 (Scb.scb_b_strucver != Home.hm2_b_strucver) ||
731 (Scb.scb_b_struclev != Home.hm2_b_struclev))
732 goto Return_Cleanup;
733 if (!sim_quiet) {
734 sim_printf ("%s%lu: '%s' Contains ODS%lu File system:\n",
735 sim_dname (dptr), (unsigned long)(uptr-dptr->units), uptr->filename, (unsigned long)Home.hm2_b_struclev);
736 sim_printf ("%s%lu: Volume Name: %12.12s ",
737 sim_dname (dptr), (unsigned long)(uptr-dptr->units), Home.hm2_t_volname);
738 sim_printf ("Format: %12.12s ",
739 Home.hm2_t_format);
740 sim_printf ("SectorsInVolume: %lu\n",
741 (unsigned long)Scb.scb_l_volsize);
742 }
743 ret_val = ((t_offset)Scb.scb_l_volsize) * 512;
744
745 Return_Cleanup:
746 uptr->capac = saved_capac;
747 return ret_val;
748 }
749
750 t_stat sim_disk_attach (UNIT *uptr, const char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize,
751 uint32 dbit, const char *dtype, uint32 pdp11tracksize, int completion_delay)
752 {
753 struct disk_context *ctx;
754 DEVICE *dptr;
755 FILE *(*open_function)(const char *filename, const char *mode) = sim_fopen;
756 FILE *(*create_function)(const char *filename, t_offset desiredsize) = NULL;
757 t_offset (*size_function)(FILE *file) = NULL;
758 t_stat (*storage_function)(FILE *file, uint32 *sector_size, uint32 *removable) = NULL;
759 t_bool created = FALSE, copied = FALSE;
760 t_bool auto_format = FALSE;
761 t_offset capac, filesystem_capac;
762
763 if (uptr->flags & UNIT_DIS)
764 return SCPE_UDIS;
765 if (!(uptr->flags & UNIT_ATTABLE))
766 return SCPE_NOATT;
767 if ((dptr = find_dev_from_unit (uptr)) == NULL)
768 return SCPE_NOATT;
769 if (sim_switches & SWMASK ('F')) {
770 char gbuf[CBUFSIZE];
771 cptr = get_glyph (cptr, gbuf, 0);
772 if (*cptr == 0)
773 return SCPE_2FARG;
774 if (sim_disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK)
775 return sim_messagef (SCPE_ARG, "Invalid Disk Format: %s\n", gbuf);
776 sim_switches = sim_switches & ~(SWMASK ('F'));
777 auto_format = TRUE;
778 }
779 open_function = sim_fopen;
780 size_function = sim_fsize_ex;
781 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char));
782 uptr->disk_ctx = ctx = (struct disk_context *)calloc(1, sizeof(struct disk_context));
783 if (!ctx)
784 {
785 fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
786 __func__, __FILE__, __LINE__);
787 #if defined(USE_BACKTRACE)
788 # if defined(SIGUSR2)
789 (void)raise(SIGUSR2);
790
791 # endif
792 #endif
793 abort();
794 }
795 if ((uptr->filename == NULL) || (uptr->disk_ctx == NULL))
796 return _err_return (uptr, SCPE_MEM);
797 #if defined(__GNUC__)
798 # if !defined(__clang_version__)
799 # if __GNUC__ > 7
800 # if !defined(__INTEL_COMPILER)
801 # pragma GCC diagnostic push
802 # pragma GCC diagnostic ignored "-Wstringop-truncation"
803 # endif
804 # endif
805 # endif
806 #endif
807 strncpy (uptr->filename, cptr, CBUFSIZE);
808 #if defined(__GNUC__)
809 # if !defined(__clang_version__)
810 # if __GNUC__ > 7
811 # if !defined(__INTEL_COMPILER)
812 # pragma GCC diagnostic pop
813 # endif
814 # endif
815 # endif
816 #endif
817 ctx->sector_size = (uint32)sector_size;
818 ctx->capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1;
819 ctx->xfer_element_size = (uint32)xfer_element_size;
820 ctx->dptr = dptr;
821 ctx->dbit = dbit;
822 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_attach(unit=%lu,filename='%s')\n",
823 (unsigned long)(uptr-ctx->dptr->units), uptr->filename);
824 ctx->auto_format = auto_format;
825 ctx->storage_sector_size = (uint32)sector_size;
826 if ((sim_switches & SWMASK ('R')) ||
827 ((uptr->flags & UNIT_RO) != 0)) {
828 if (((uptr->flags & UNIT_ROABLE) == 0) &&
829 ((uptr->flags & UNIT_RO) == 0))
830 return _err_return (uptr, SCPE_NORO);
831 uptr->fileref = open_function (cptr, "rb");
832 if (uptr->fileref == NULL)
833 return _err_return (uptr, SCPE_OPENERR);
834 uptr->flags = uptr->flags | UNIT_RO;
835 if (!sim_quiet) {
836 sim_printf ("%s%lu: unit is read only (%s)\n", sim_dname (dptr),
837 (unsigned long)(uptr-dptr->units), cptr);
838 }
839 }
840 else {
841 uptr->fileref = open_function (cptr, "rb+");
842 if (uptr->fileref == NULL) {
843 if ((errno == EROFS) || (errno == EACCES)) {
844 if ((uptr->flags & UNIT_ROABLE) == 0)
845 return _err_return (uptr, SCPE_NORO);
846 uptr->fileref = open_function (cptr, "rb");
847 if (uptr->fileref == NULL)
848 return _err_return (uptr, SCPE_OPENERR);
849 uptr->flags = uptr->flags | UNIT_RO;
850 if (!sim_quiet)
851 sim_printf ("%s%lu: unit is read only (%s)\n", sim_dname (dptr),
852 (unsigned long)(uptr-dptr->units), cptr);
853 }
854 else {
855 if (sim_switches & SWMASK ('E'))
856 return _err_return (uptr, SCPE_OPENERR);
857 if (create_function)
858 uptr->fileref = create_function (cptr,
859 ((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1));
860 else
861 uptr->fileref = open_function (cptr, "wb+");
862 if (uptr->fileref == NULL)
863 return _err_return (uptr, SCPE_OPENERR);
864 if (!sim_quiet)
865 sim_printf ("%s%lu: creating new file (%s)\n", sim_dname (dptr),
866 (unsigned long)(uptr-dptr->units), cptr);
867 created = TRUE;
868 }
869 }
870 }
871 uptr->flags = uptr->flags | UNIT_ATT;
872 uptr->pos = 0;
873
874
875 if (storage_function)
876 storage_function (uptr->fileref, &ctx->storage_sector_size, &ctx->removable);
877
878 if ((created) && (!copied)) {
879 t_stat r = SCPE_OK;
880 uint8 *secbuf = (uint8 *)calloc (1, ctx->sector_size);
881 if (secbuf == NULL)
882 r = SCPE_MEM;
883 if (r == SCPE_OK)
884 r = sim_disk_wrsect (uptr,
885 (t_lba)(((((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)) - \
886 ctx->sector_size)/ctx->sector_size), secbuf, NULL, 1);
887 if (r == SCPE_OK)
888 r = sim_disk_wrsect (uptr, (t_lba)(0), secbuf, NULL, 1);
889 FREE (secbuf);
890 if (r != SCPE_OK) {
891 sim_disk_detach (uptr);
892 remove (cptr);
893 return SCPE_OPENERR;
894 }
895 if (sim_switches & SWMASK ('I')) {
896 uint8 *init_buf = (uint8*) malloc (1024*1024);
897 t_lba lba, sect;
898 uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1;
899 t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size);
900 t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
901 t_seccnt sects = sectors_per_buffer;
902
903 if (!init_buf) {
904 sim_disk_detach (uptr);
905 remove (cptr);
906 return SCPE_MEM;
907 }
908 for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
909 sects = sectors_per_buffer;
910 if (lba + sects > total_sectors)
911 sects = total_sectors - lba;
912 for (sect = 0; sect < sects; sect++) {
913 t_lba offset;
914 for (offset = 0; offset < sector_size; offset += sizeof(uint32))
915 *((uint32 *)&init_buf[sect*sector_size + offset]) = (uint32)(lba + sect);
916 }
917 r = sim_disk_wrsect (uptr, lba, init_buf, NULL, sects);
918 if (r != SCPE_OK) {
919 FREE (init_buf);
920 sim_disk_detach (uptr);
921 remove (cptr);
922 return SCPE_OPENERR;
923 }
924 if (!sim_quiet)
925 sim_printf ("%s%lu: Initialized To Sector Address %luMB. %lu%% complete.\r",
926 sim_dname (dptr), (unsigned long)(uptr-dptr->units),
927 (unsigned long)((((float)lba)*sector_size)/1000000),
928 (unsigned long)((((float)lba)*100)/total_sectors));
929 }
930 if (!sim_quiet)
931 sim_printf ("%s%lu: Initialized To Sector Address %luMB. 100%% complete.\n",
932 sim_dname (dptr), (unsigned long)(uptr-dptr->units),
933 (unsigned long)((((float)lba)*sector_size)/1000000));
934 FREE (init_buf);
935 }
936 }
937
938 if (sim_switches & SWMASK ('K')) {
939 t_stat r = SCPE_OK;
940 t_lba lba, sect;
941 uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1;
942 t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size);
943 t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1)));
944 t_seccnt sects = sectors_per_buffer;
945 uint8 *verify_buf = (uint8*) malloc (1024*1024);
946
947 if (!verify_buf) {
948 sim_disk_detach (uptr);
949 return SCPE_MEM;
950 }
951 for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) {
952 sects = sectors_per_buffer;
953 if (lba + sects > total_sectors)
954 sects = total_sectors - lba;
955 r = sim_disk_rdsect (uptr, lba, verify_buf, NULL, sects);
956 if (r == SCPE_OK) {
957 for (sect = 0; sect < sects; sect++) {
958 t_lba offset;
959 t_bool sect_error = FALSE;
960
961 for (offset = 0; offset < sector_size; offset += sizeof(uint32)) {
962 if (*((uint32 *)&verify_buf[sect*sector_size + offset]) != (uint32)(lba + sect)) {
963 sect_error = TRUE;
964 break;
965 }
966 }
967 if (sect_error) {
968 uint32 save_dctrl = dptr->dctrl;
969 FILE *save_sim_deb = sim_deb;
970
971 sim_printf ("\n%s%lu: Verification Error on lbn %lu(0x%X) of %lu(0x%X).\n", sim_dname (dptr),
972 (unsigned long)(uptr-dptr->units),
973 (unsigned long)((unsigned long)lba+(unsigned long)sect),
974 (int)((int)lba+(int)sect),
975 (unsigned long)total_sectors,
976 (int)total_sectors);
977 dptr->dctrl = 0xFFFFFFFF;
978 sim_deb = stdout;
979 sim_disk_data_trace (uptr, verify_buf+sect*sector_size, lba+sect, sector_size,
980 "Found", TRUE, 1);
981 dptr->dctrl = save_dctrl;
982 sim_deb = save_sim_deb;
983 }
984 }
985 }
986 if (!sim_quiet)
987 sim_printf ("%s%lu: Verified containing Sector Address %luMB. %lu%% complete.\r",
988 sim_dname (dptr), (unsigned long)(uptr-dptr->units),
989 (unsigned long)((((float)lba)*sector_size)/1000000),
990 (unsigned long)((((float)lba)*100)/total_sectors));
991 }
992 if (!sim_quiet)
993 sim_printf ("%s%lu: Verified containing Sector Address %luMB. 100%% complete.\n",
994 sim_dname (dptr), (unsigned long)(uptr-dptr->units),
995 (unsigned long)((((float)lba)*sector_size)/1000000));
996 FREE (verify_buf);
997 uptr->dynflags |= UNIT_DISK_CHK;
998 }
999
1000 filesystem_capac = get_filesystem_size (uptr);
1001 capac = size_function (uptr->fileref);
1002 if (capac && (capac != (t_offset)-1)) {
1003 if (dontautosize) {
1004 t_addr saved_capac = uptr->capac;
1005
1006 if ((filesystem_capac != (t_offset)-1) &&
1007 (filesystem_capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)))) {
1008 if (!sim_quiet) {
1009 uptr->capac = (t_addr)(filesystem_capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
1010 sim_printf ("%s%lu: The file system on the disk %s is larger than simulated device (%s > ",
1011 sim_dname (dptr), (unsigned long)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr));
1012 uptr->capac = saved_capac;
1013 sim_printf ("%s)\n", sprint_capac (dptr, uptr));
1014 }
1015 sim_disk_detach (uptr);
1016 return SCPE_OPENERR;
1017 }
1018 if ((capac < (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) && \
1019 (DKUF_F_STD != DK_GET_FMT (uptr))) {
1020 if (!sim_quiet) {
1021 uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
1022 sim_printf ("%s%lu: non expandable disk %s is smaller than simulated device (%s < ",
1023 sim_dname (dptr), (unsigned long)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr));
1024 uptr->capac = saved_capac;
1025 sim_printf ("%s)\n", sprint_capac (dptr, uptr));
1026 }
1027 sim_disk_detach (uptr);
1028 return SCPE_OPENERR;
1029 }
1030 }
1031 else {
1032 if ((filesystem_capac != (t_offset)-1) &&
1033 (filesystem_capac > capac))
1034 capac = filesystem_capac;
1035 if ((capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) ||
1036 (DKUF_F_STD != DK_GET_FMT (uptr)))
1037 uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)));
1038 }
1039 }
1040
1041 uptr->io_flush = _sim_disk_io_flush;
1042
1043 return SCPE_OK;
1044 }
1045
1046 t_stat sim_disk_detach (UNIT *uptr)
1047 {
1048 struct disk_context *ctx;
1049 int (*close_function)(FILE *f);
1050 FILE *fileref;
1051
1052 if ((uptr == NULL) || !(uptr->flags & UNIT_ATT))
1053 return SCPE_NOTATT;
1054
1055 ctx = (struct disk_context *)uptr->disk_ctx;
1056 fileref = uptr->fileref;
1057
1058
1059
1060
1061 switch (DK_GET_FMT (uptr)) {
1062 case DKUF_F_STD:
1063 close_function = fclose;
1064 break;
1065 default:
1066 return SCPE_IERR;
1067 }
1068 if (!(uptr->flags & UNIT_ATTABLE))
1069 return SCPE_NOATT;
1070 if (!(uptr->flags & UNIT_ATT))
1071 return SCPE_OK;
1072 if (NULL == find_dev_from_unit (uptr))
1073 return SCPE_OK;
1074
1075 if (uptr->io_flush)
1076 uptr->io_flush (uptr);
1077
1078 uptr->flags &= ~(UNIT_ATT | UNIT_RO);
1079 uptr->dynflags &= ~(UNIT_NO_FIO | UNIT_DISK_CHK);
1080 FREE (uptr->filename);
1081 uptr->filename = NULL;
1082 uptr->fileref = NULL;
1083 if (uptr->disk_ctx) {
1084 FREE (uptr->disk_ctx);
1085 uptr->disk_ctx = NULL;
1086 }
1087 uptr->io_flush = NULL;
1088 if (ctx && ctx -> auto_format)
1089 sim_disk_set_fmt (uptr, 0, "SIMH", NULL);
1090 if (close_function (fileref) == EOF)
1091 return SCPE_IOERR;
1092 return SCPE_OK;
1093 }
1094
1095 t_stat sim_disk_attach_help(FILE *st, DEVICE *dptr, const UNIT *uptr, int32 flag, const char *cptr)
1096 {
1097 fprintf (st, "%s Disk Attach Help\n\n", dptr->name);
1098 fprintf (st, "Disk files are stored in the following format:\n\n");
1099 fprintf (st, " SIMH An unstructured binary file of the size appropriate\n");
1100 fprintf (st, " for the disk drive being simulated.\n\n");
1101
1102 if (0 == (uptr-dptr->units)) {
1103 if (dptr->numunits > 1) {
1104 uint32 i;
1105
1106 for (i=0; i < dptr->numunits; ++i)
1107 if (dptr->units[i].flags & UNIT_ATTABLE)
1108 fprintf (st, " sim> ATTACH {switches} %s%lu diskfile\n", dptr->name, (unsigned long)i);
1109 }
1110 else
1111 fprintf (st, " sim> ATTACH {switches} %s diskfile\n", dptr->name);
1112 }
1113 else
1114 fprintf (st, " sim> ATTACH {switches} %s diskfile\n\n", dptr->name);
1115 fprintf (st, "\n%s attach command switches\n", dptr->name);
1116 fprintf (st, " -R Attach Read Only.\n");
1117 fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n");
1118 fprintf (st, " disk container will be attempted).\n");
1119 fprintf (st, " -F Open the indicated disk container in a specific format\n");
1120 fprintf (st, " -I Initialize newly created disk so that each sector contains its\n");
1121 fprintf (st, " sector address\n");
1122 fprintf (st, " -K Verify that the disk contents contain the sector address in each\n");
1123 fprintf (st, " sector. Whole disk checked at attach time and each sector is\n");
1124 fprintf (st, " checked when written.\n");
1125 fprintf (st, " -V Perform a verification pass to confirm successful data copy\n");
1126 fprintf (st, " operation.\n");
1127 fprintf (st, " -U Fix inconsistencies which are overridden by the -O switch\n");
1128 fprintf (st, " -Y Answer Yes to prompt to overwrite last track (on disk create)\n");
1129 fprintf (st, " -N Answer No to prompt to overwrite last track (on disk create)\n");
1130 return SCPE_OK;
1131 }
1132
1133 t_stat sim_disk_reset (UNIT *uptr)
1134 {
1135 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
1136
1137 if (!(uptr->flags & UNIT_ATT))
1138 return SCPE_OK;
1139
1140 sim_debug (ctx->dbit, ctx->dptr, "sim_disk_reset(unit=%lu)\n", (unsigned long)(uptr-ctx->dptr->units));
1141
1142 _sim_disk_io_flush(uptr);
1143 return SCPE_OK;
1144 }
1145
1146 t_stat sim_disk_perror (UNIT *uptr, const char *msg)
1147 {
1148 int saved_errno = errno;
1149
1150 if (!(uptr->flags & UNIT_ATTABLE))
1151 return SCPE_NOATT;
1152 switch (DK_GET_FMT (uptr)) {
1153 case DKUF_F_STD:
1154 perror (msg);
1155 sim_printf ("%s %s: %s (Error %d)\r\n", sim_uname(uptr), msg, xstrerror_l(saved_errno), saved_errno);
1156
1157 default:
1158 ;
1159 }
1160 return SCPE_OK;
1161 }
1162
1163 t_stat sim_disk_clearerr (UNIT *uptr)
1164 {
1165 if (!(uptr->flags & UNIT_ATTABLE))
1166 return SCPE_NOATT;
1167 switch (DK_GET_FMT (uptr)) {
1168 case DKUF_F_STD:
1169 clearerr (uptr->fileref);
1170 break;
1171 default:
1172 ;
1173 }
1174 return SCPE_OK;
1175 }
1176
1177 void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason)
1178 {
1179 struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx;
1180
1181 if (sim_deb && (ctx->dptr->dctrl & reason)) {
1182 char pos[32];
1183
1184 (void)sprintf (pos, "lbn: %08X ", (unsigned int)lba);
1185 sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), pos, len, txt, reason);
1186 }
1187 }