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