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