1 /*
2 * UAE - The Un*x Amiga Emulator
3 *
4 * SCSI tape emulation
5 *
6 * Copyright 2013 Toni Wilen
7 *
8 */
9
10 #include "sysconfig.h"
11 #include "sysdeps.h"
12
13 #include "options.h"
14 #include "filesys.h"
15 #include "scsi.h"
16 #include "blkdev.h"
17 #include "zfile.h"
18 #include "uae/memory.h"
19 #include "scsi.h"
20 #include "threaddep/thread.h"
21 #include "a2091.h"
22 #include "fsdb.h"
23
24 int log_tapeemu = 1;
25
26 #define TAPE_INDEX _T("index.tape")
27
28 static struct scsi_data_tape *tapeunits[MAX_FILESYSTEM_UNITS];
29
notape(struct scsi_data_tape * tape)30 static bool notape (struct scsi_data_tape *tape)
31 {
32 return tape->tape_dir[0] == 0 || tape->nomedia;
33 }
34
rl(uae_u8 * p)35 static int rl (uae_u8 *p)
36 {
37 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
38 }
wl(uae_u8 * p,int v)39 static void wl (uae_u8 *p, int v)
40 {
41 p[0] = v >> 24;
42 p[1] = v >> 16;
43 p[2] = v >> 8;
44 p[3] = v;
45 }
46
tape_free(struct scsi_data_tape * tape)47 void tape_free (struct scsi_data_tape *tape)
48 {
49 if (!tape)
50 return;
51 zfile_fclose (tape->zf);
52 zfile_fclose (tape->index);
53 zfile_closedir_archive (tape->zd);
54 xfree(tape);
55 for (int i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
56 if (tapeunits[i] == tape)
57 tapeunits[i] = NULL;
58 }
59 }
60
tape_init(int unit,struct scsi_data_tape * tape,const TCHAR * tape_directory,bool readonly)61 static void tape_init (int unit, struct scsi_data_tape *tape, const TCHAR *tape_directory, bool readonly)
62 {
63 TCHAR path[MAX_DPATH];
64
65 memset (tape, 0, sizeof (struct scsi_data_tape));
66 _tcscpy (tape->tape_dir, tape_directory);
67
68 tape->blocksize = 512;
69 tape->wp = readonly;
70 tape->beom = -1;
71 tape->nomedia = false;
72 tape->unloaded = false;
73
74 if (my_existsdir (tape->tape_dir)) {
75 tape->realdir = true;
76 } else {
77 tape->zd = zfile_opendir_archive (tape_directory, ZFD_ARCHIVE | ZFD_NORECURSE);
78 if (!tape->zd)
79 tape->nomedia = true;
80 }
81
82 _tcscpy (path, tape_directory);
83 _tcscat (path, FSDB_DIR_SEPARATOR_S);
84 _tcscat (path, TAPE_INDEX);
85 tape->index = zfile_fopen (path, _T("rb"), ZFD_NORMAL);
86 if (tape->index)
87 write_log (_T("TAPEEMU INDEX: '%s'\n"), path);
88 tapeunits[unit] = tape;
89 }
90
tape_alloc(int unitnum,const TCHAR * tape_directory,bool readonly)91 struct scsi_data_tape *tape_alloc (int unitnum, const TCHAR *tape_directory, bool readonly)
92 {
93 struct scsi_data_tape *tape = xcalloc (struct scsi_data_tape, 1);
94
95 tape_init (unitnum, tape, tape_directory, readonly);
96 return tape;
97 }
98
tape_media_change(int unitnum,struct uaedev_config_info * ci)99 void tape_media_change (int unitnum, struct uaedev_config_info *ci)
100 {
101 struct scsi_data_tape *tape = tapeunits[unitnum];
102 if (!tape)
103 return;
104 tape_init (unitnum, tape, ci->rootdir, ci->readonly);
105 }
106
tape_get_info(int unitnum,struct device_info * di)107 bool tape_get_info (int unitnum, struct device_info *di)
108 {
109 struct scsi_data_tape *tape = tapeunits[unitnum];
110 memset (di, 0, sizeof (struct device_info));
111 if (!tape)
112 return false;
113 di->media_inserted = notape (tape) == false;
114 _tcscpy (di->label, tape->tape_dir);
115 return true;
116 }
117
rewind(struct scsi_data_tape * tape)118 static void rewind (struct scsi_data_tape *tape)
119 {
120 zfile_fclose (tape->zf);
121 tape->zf = NULL;
122 my_closedir (tape->od);
123 tape->file_number = 0;
124 tape->file_offset = 0;
125 tape->od = NULL;
126 tape->beom = -1;
127 if (tape->index)
128 zfile_fseek (tape->index, 0, SEEK_SET);
129 if (tape->zd)
130 zfile_resetdir_archive (tape->zd);
131 }
132
erase(struct scsi_data_tape * tape)133 static void erase (struct scsi_data_tape *tape)
134 {
135 struct my_opendir_s *od;
136 if (!tape->realdir)
137 return;
138 rewind (tape);
139 od = my_opendir (tape->tape_dir);
140 if (od) {
141 for (;;) {
142 TCHAR path[MAX_DPATH], filename[MAX_DPATH];
143 if (!my_readdir (od, filename))
144 break;
145 TCHAR *ext = _tcsrchr (filename, '.');
146 if (ext && !_tcsicmp (ext, _T(".tape"))) {
147 _stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, filename);
148 if (my_existsfile (path))
149 my_unlink (path);
150 }
151 }
152 my_closedir (od);
153 }
154 }
155
next_file(struct scsi_data_tape * tape)156 static bool next_file (struct scsi_data_tape *tape)
157 {
158 zfile_fclose (tape->zf);
159 tape->zf = NULL;
160 tape->file_offset = 0;
161 if (tape->index) {
162 TCHAR path[MAX_DPATH];
163 TCHAR name[256];
164 name[0] = 0;
165 zfile_fgets (name, sizeof name / sizeof (TCHAR), tape->index);
166 my_trim (name);
167 if (name[0] == 0)
168 goto end;
169 _tcscpy (path, tape->tape_dir);
170 _tcscat (path, FSDB_DIR_SEPARATOR_S);
171 _tcscat (path, name);
172 tape->zf = zfile_fopen (path, _T("rb"), ZFD_NORMAL);
173 write_log (_T("TAPEEMU: File '%s'\n"), path);
174 } else if (tape->realdir) {
175 TCHAR path[MAX_DPATH], filename[MAX_DPATH];
176 if (tape->od == NULL)
177 tape->od = my_opendir (tape->tape_dir);
178 if (!tape->od) {
179 tape_free (tape);
180 goto end;
181 }
182 for (;;) {
183 if (!my_readdir (tape->od, filename))
184 goto end;
185 if (_tcsicmp (filename, TAPE_INDEX))
186 continue;
187 _stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, filename);
188 if (!my_existsfile (path))
189 continue;
190 tape->zf = zfile_fopen (path, _T("rb"), 0);
191 if (!tape->zf)
192 continue;
193 break;
194 }
195 if (tape->zf)
196 write_log (_T("TAPEEMU DIR: File '%s'\n"), zfile_getname (tape->zf));
197 } else {
198 tape->zf = zfile_readdir_archive_open (tape->zd, _T("rb"));
199 if (tape->zf)
200 write_log (_T("TAPEEMU ARC: File '%s'\n"), zfile_getname (tape->zf));
201 }
202 if (tape->zf) {
203 tape->file_number++;
204 return true;
205 }
206 end:
207 write_log (_T("TAPEEMU: end of tape\n"));
208 tape->beom = 1;
209 return false;
210 }
211
tape_read(struct scsi_data_tape * tape,uae_u8 * scsi_data,int len,bool * eof)212 static int tape_read (struct scsi_data_tape *tape, uae_u8 *scsi_data, int len, bool *eof)
213 {
214 int got;
215
216 *eof = false;
217 if (tape->beom > 0) {
218 *eof = true;
219 return -1;
220 }
221
222 if (!tape->zf) {
223 rewind (tape);
224 if (!next_file (tape))
225 return -1;
226 }
227 zfile_fseek (tape->zf, tape->file_offset, SEEK_SET);
228 got = zfile_fread (scsi_data, 1, len, tape->zf);
229 tape->file_offset += got;
230 if (got < len) {
231 *eof = true;
232 next_file (tape);
233 }
234 return got;
235 }
236
tape_write(struct scsi_data_tape * tape,uae_u8 * scsi_data,int len)237 static int tape_write (struct scsi_data_tape *tape, uae_u8 *scsi_data, int len)
238 {
239 TCHAR path[MAX_DPATH], numname[30];
240 int exists;
241
242 if (!tape->realdir)
243 return -1;
244 _stprintf (numname, _T("%05d.tape"), tape->file_number);
245 _stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, numname);
246 exists = my_existsfile (path);
247 struct zfile *zf = zfile_fopen (path, _T("a+b"));
248 if (!zf)
249 return -1;
250 zfile_fseek (zf, 0, SEEK_END);
251 len = zfile_fwrite (scsi_data, 1, len, zf);
252 zfile_fclose (zf);
253 if (!exists) {
254 _stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, TAPE_INDEX);
255 zf = zfile_fopen (path, _T("a+b"));
256 if (zf) {
257 zfile_fputs (zf, numname);
258 zfile_fputs (zf, _T("\n"));
259 }
260 zfile_fclose (zf);
261 }
262 return len;
263 }
264
scsi_tape_emulate(struct scsi_data_tape * tape,uae_u8 * cmdbuf,int scsi_cmd_len,uae_u8 * scsi_data,int * data_len,uae_u8 * r,int * reply_len,uae_u8 * s,int * sense_len)265 int scsi_tape_emulate (struct scsi_data_tape *tape, uae_u8 *cmdbuf, int scsi_cmd_len,
266 uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len)
267 {
268 uae_s64 len;
269 int lr = 0, ls = 0;
270 int scsi_len = -1;
271 int status = 0;
272 int lun;
273 bool eof;
274
275 if (cmdbuf == NULL)
276 return 0;
277
278 if (log_tapeemu)
279 write_log (_T("TAPEEMU: %02X.%02X.%02X.%02X.%02X.%02X\n"),
280 cmdbuf[0], cmdbuf[1], cmdbuf[2], cmdbuf[3], cmdbuf[4], cmdbuf[5]);
281
282 if (cmdbuf[0] == 3) {
283 if (tape->beom == -1)
284 s[9] |= 0x8; // beginning of media
285 if (tape->beom == 1)
286 s[2] |= 0x40; // end of media
287 if (*sense_len < 0x12)
288 *sense_len = 0x12;
289 return 0;
290 }
291
292 *reply_len = *sense_len = 0;
293 memset (r, 0, 256);
294 memset (s, 0, 256);
295 s[0] = 0x70;
296
297 lun = cmdbuf[1] >> 5;
298 if (cmdbuf[0] != 0x03 && cmdbuf[0] != 0x12 && lun) {
299 status = 2; /* CHECK CONDITION */
300 s[0] = 0x70;
301 s[2] = 5; /* ILLEGAL REQUEST */
302 s[12] = 0x25; /* INVALID LUN */
303 ls = 0x12;
304 goto end;
305 }
306
307 switch (cmdbuf[0])
308 {
309 case 0x00: /* TEST UNIT READY */
310 if (notape (tape))
311 goto notape;
312 if (tape->unloaded)
313 goto unloaded;
314 scsi_len = 0;
315 break;
316
317 case 0x19: /* ERASE */
318 if (log_tapeemu)
319 write_log (_T("TAPEEMU ERASE\n"));
320 if (notape (tape))
321 goto notape;
322 if (tape->unloaded)
323 goto unloaded;
324 if (tape->wp)
325 goto writeprot;
326 erase (tape);
327 rewind (tape);
328 tape->beom = 1;
329 scsi_len = 0;
330 break;
331
332 case 0x1b: /* LOAD/UNLOAD */
333 {
334 bool load = (cmdbuf[4] & 1) != 0;
335 bool ret = (cmdbuf[4] & 2) != 0;
336 bool eot = (cmdbuf[4] & 4) != 0;
337 if (log_tapeemu)
338 write_log (_T("TAPEEMU LOAD/UNLOAD %d:%d:%d\n"), eot, ret, load);
339 if (notape (tape))
340 goto notape;
341 if (load && eot)
342 goto errreq;
343 rewind (tape);
344 tape->unloaded = !load;
345 if (eot) {
346 tape->beom = 1;
347 } else {
348 tape->beom = -1;
349 }
350 scsi_len = 0;
351 break;
352 }
353
354 case 0x01: /* REWIND */
355 if (log_tapeemu)
356 write_log (_T("TAPEEMU: REWIND. IMMED=%d\n"), cmdbuf[1] & 1);
357 if (notape (tape))
358 goto notape;
359 if (tape->unloaded)
360 goto unloaded;
361 rewind (tape);
362 scsi_len = 0;
363 break;
364
365 case 0x11: /* SPACE */
366 {
367 int code = cmdbuf[1] & 15;
368 int count = rl (cmdbuf + 1) & 0xffffff;
369 if (log_tapeemu)
370 write_log (_T("TAPEEMU: SPACE code=%d count=%d\n"), code, count);
371 if (notape (tape))
372 goto notape;
373 if (tape->unloaded)
374 goto unloaded;
375 if (code >= 2)
376 goto errreq;
377 if (code == 1) {
378 if (!next_file (tape))
379 goto endoftape;
380 }
381 scsi_len = 0;
382 }
383 break;
384
385 case 0x10: /* WRITE FILEMARK */
386 len = rl (cmdbuf + 1) & 0xffffff;
387 if (log_tapeemu)
388 write_log (_T("TAPEEMU WRITE FILEMARK %lld\n"), len);
389 if (notape (tape))
390 goto notape;
391 if (tape->unloaded)
392 goto unloaded;
393 if (tape->wp)
394 goto writeprot;
395 if (len > 0) {
396 tape->file_number++;
397 tape_write (tape, scsi_data, 0);
398 }
399 scsi_len = 0;
400 break;
401
402 case 0x0a: /* WRITE (6) */
403 len = rl (cmdbuf + 1) & 0xffffff;
404 if (cmdbuf[1] & 1)
405 len *= tape->blocksize;
406 if (log_tapeemu)
407 write_log (_T("TAPEEMU WRITE %lld (%d, %d)\n"), len, rl (cmdbuf + 1) & 0xffffff, cmdbuf[1] & 1);
408 if (notape (tape))
409 goto notape;
410 if (tape->unloaded)
411 goto unloaded;
412 if (tape->wp)
413 goto writeprot;
414 if (tape->beom < 0)
415 tape->beom = 0;
416 scsi_len = tape_write (tape, scsi_data, len);
417 if (scsi_len < 0)
418 goto writeprot;
419 break;
420
421 case 0x08: /* READ (6) */
422 len = rl (cmdbuf + 1) & 0xffffff;
423 if (cmdbuf[1] & 1)
424 len *= tape->blocksize;
425 if (log_tapeemu)
426 write_log (_T("TAPEEMU READ %lld (%d, %d)\n"), len, rl (cmdbuf + 1) & 0xffffff, cmdbuf[1] & 1);
427 if (notape (tape))
428 goto notape;
429 if (tape->unloaded)
430 goto unloaded;
431 if (tape->beom < 0)
432 tape->beom = 0;
433 scsi_len = tape_read (tape, scsi_data, len, &eof);
434 if (log_tapeemu)
435 write_log (_T("-> READ %d bytes\n"), scsi_len);
436 if ((cmdbuf[1] & 1) && scsi_len > 0 && scsi_len < len) {
437 int gap = tape->blocksize - (scsi_len & (tape->blocksize - 1));
438 if (gap > 0 && gap < tape->blocksize)
439 memset (scsi_data + scsi_len, 0, gap);
440 scsi_len += tape->blocksize - 1;
441 scsi_len &= ~(tape->blocksize - 1);
442 }
443 if (scsi_len < 0) {
444 tape->beom = 2;
445 status = SCSI_STATUS_CHECK_CONDITION;
446 s[0] = 0x80 | 0x70;
447 s[2] = 8; /* BLANK CHECK */
448 if (cmdbuf[1] & 1)
449 wl (&s[3], len / tape->blocksize);
450 else
451 wl (&s[3], len);
452 s[12] = 0;
453 s[13] = 2; /* End-of-partition/medium detected */
454 ls = 0x12;
455 } else if (eof) {
456 status = SCSI_STATUS_CHECK_CONDITION;
457 s[0] = 0x80 | 0x70; // Valid + code
458 s[2] = 0x80 | 0; /* File Mark Detected + NO SENSE */
459 if (cmdbuf[1] & 1)
460 wl (&s[3], (len - scsi_len) / tape->blocksize);
461 else
462 wl (&s[3], len);
463 s[12] = 0;
464 s[13] = 1; /* File Mark detected */
465 ls = 0x12;
466 if (log_tapeemu)
467 write_log (_T("TAPEEMU READ FILE END, %lld remaining\n"), len - scsi_len);
468 }
469 break;
470
471 case 0x5a: // MODE SENSE(10)
472 case 0x1a: /* MODE SENSE(6) */
473 {
474 uae_u8 *p;
475 int maxlen;
476 bool sense10 = cmdbuf[0] == 0x5a;
477 int totalsize, bdsize;
478 int pc = cmdbuf[2] >> 6;
479 int pcode = cmdbuf[2] & 0x3f;
480 int dbd = cmdbuf[1] & 8;
481 if (cmdbuf[0] == 0x5a)
482 dbd = 1;
483 if (log_tapeemu)
484 write_log (_T("TAPEEMU MODE SENSE PC=%d CODE=%d DBD=%d\n"), pc, pcode, dbd);
485 p = r;
486 if (sense10) {
487 totalsize = 8 - 2;
488 maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
489 p[2] = 0;
490 p[3] = 0;
491 p[4] = 0;
492 p[5] = 0;
493 p[6] = 0;
494 p[7] = 0;
495 p += 8;
496 } else {
497 totalsize = 4 - 1;
498 maxlen = cmdbuf[4];
499 p[1] = 0;
500 p[2] = tape->wp ? 0x80 : 0;
501 p[3] = 0;
502 p += 4;
503 }
504 bdsize = 0;
505 if (!dbd) {
506 wl(p + 0, 0);
507 wl(p + 4, tape->blocksize);
508 bdsize = 8;
509 p += bdsize;
510 }
511 if (pcode)
512 goto errreq;
513 if (sense10) {
514 totalsize += bdsize;
515 r[6] = bdsize >> 8;
516 r[7] = bdsize & 0xff;
517 r[0] = totalsize >> 8;
518 r[1] = totalsize & 0xff;
519 } else {
520 totalsize += bdsize;
521 r[3] = bdsize & 0xff;
522 r[0] = totalsize & 0xff;
523 }
524 lr = totalsize + 1;
525 if (lr > maxlen)
526 lr = maxlen;
527 }
528 break;
529
530 case 0x55: // MODE SELECT(10)
531 case 0x15: // MODE SELECT(6)
532 {
533 uae_u8 *p;
534 bool mode10 = cmdbuf[0] == 0x55;
535 int nb = 0, bl = 512;
536 p = scsi_data + 4;
537 if (mode10)
538 p += 4;
539 if (scsi_data[3] >= 8) {
540 nb = rl (p) & 0xffffff;
541 bl = rl (p + 4) & 0xffffff;
542 }
543 if (log_tapeemu)
544 write_log (_T("TAPEEMU MODE SELECT BUFM=%d BDL=%d Density=%d NB=%d BL=%d\n"), (scsi_data[2] & 0x10) != 0, scsi_data[3], p[0], nb, bl);
545 if (nb || bl != 512)
546 goto err;
547 scsi_len = 0;
548 break;
549 }
550
551 case 0x05: /* READ BLOCK LIMITS */
552 if (log_tapeemu)
553 write_log (_T("TAPEEMU READ BLOCK LIMITS\n"));
554 r[0] = 0;
555 r[1] = 0;
556 r[2] = 2; // 0x200 = 512
557 r[3] = 0;
558 r[4] = 2; // 0x200 = 512
559 r[5] = 0;
560 lr = 6;
561 break;
562
563 case 0x16: /* RESERVE UNIT */
564 if (log_tapeemu)
565 write_log (_T("TAPEEMU RESERVE UNIT\n"));
566 scsi_len = 0;
567 break;
568
569 case 0x17: /* RELEASE UNIT */
570 if (log_tapeemu)
571 write_log (_T("TAPEEMU RELEASE UNIT\n"));
572 scsi_len = 0;
573 break;
574
575 case 0x1e: /* PREVENT/ALLOW MEDIUM REMOVAL */
576 if (log_tapeemu)
577 write_log (_T("TAPEEMU PREVENT/ALLOW MEDIUM REMOVAL\n"));
578 scsi_len = 0;
579 break;
580
581 case 0x12: /* INQUIRY */
582 {
583 if (log_tapeemu)
584 write_log (_T("TAPEEMU INQUIRY\n"));
585 if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
586 goto err;
587 len = cmdbuf[4];
588 if (cmdbuf[1] >> 5) {
589 r[0] = 0x7f;
590 } else {
591 r[0] = INQ_SEQD;
592 }
593 r[1] |= 0x80; // removable
594 r[2] = 2; /* supports SCSI-2 */
595 r[3] = 2; /* response data format */
596 r[4] = 32; /* additional length */
597 r[7] = 0;
598 scsi_len = lr = len < 36 ? len : 36;
599 r[2] = 2;
600 r[3] = 2;
601 char *s = ua (_T("UAE"));
602 memcpy (r + 8, s, strlen (s));
603 xfree (s);
604 s = ua (_T("SCSI TAPE"));
605 memcpy (r + 16, s, strlen (s));
606 xfree (s);
607 s = ua (_T("0.1"));
608 memcpy (r + 32, s, strlen (s));
609 xfree (s);
610 for (int i = 8; i < 36; i++) {
611 if (r[i] == 0)
612 r[i] = 32;
613 }
614 }
615 break;
616 default:
617 write_log (_T("TAPEEMU: unsupported scsi command 0x%02X\n"), cmdbuf[0]);
618 err:
619 status = SCSI_STATUS_CHECK_CONDITION;
620 s[0] = 0x70;
621 s[2] = SCSI_SK_ILLEGAL_REQ;
622 s[12] = SCSI_INVALID_COMMAND;
623 ls = 0x12;
624 break;
625 writeprot:
626 status = 2; /* CHECK CONDITION */
627 s[0] = 0x70;
628 s[2] = 7; /* DATA PROTECT */
629 s[12] = 0x27; /* WRITE PROTECTED */
630 ls = 0x12;
631 break;
632 errreq:
633 lr = -1;
634 status = SCSI_STATUS_CHECK_CONDITION;
635 s[0] = 0x70;
636 s[2] = SCSI_SK_ILLEGAL_REQ;
637 s[12] = SCSI_INVALID_FIELD;
638 ls = 0x12;
639 break;
640 endoftape:
641 status = SCSI_STATUS_CHECK_CONDITION;
642 s[0] = 0x70;
643 s[2] = SCSI_SK_MED_ERR;
644 s[12] = 0;
645 s[13] = 2; /* End-of-partition/medium detected */
646 ls = 0x12;
647 break;
648 unloaded:
649 status = SCSI_STATUS_CHECK_CONDITION;
650 s[0] = 0x70;
651 s[2] = SCSI_SK_NOT_READY;
652 s[12] = SCSI_NOT_READY;
653 ls = 0x12;
654 break;
655 notape:
656 status = SCSI_STATUS_CHECK_CONDITION;
657 s[0] = 0x70;
658 s[2] = SCSI_SK_NOT_READY;
659 s[12] = SCSI_MEDIUM_NOT_PRESENT;
660 ls = 0x12;
661 break;
662 }
663 end:
664 *data_len = scsi_len;
665 *reply_len = lr;
666 *sense_len = ls;
667 if (lr > 0) {
668 if (log_tapeemu) {
669 write_log (_T("TAPEEMU REPLY: "));
670 for (int i = 0; i < lr && i < 40; i++)
671 write_log (_T("%02X."), r[i]);
672 write_log (_T("\n"));
673 }
674 }
675 if (ls > 0) {
676 if (tape->beom == 1)
677 s[2] |= 0x40;
678 if (tape->beom == -1)
679 s[9] |= 0x8;
680 if (log_tapeemu) {
681 write_log (_T("TAPEEMU SENSE: "));
682 for (int i = 0; i < ls; i++)
683 write_log (_T("%02X."), s[i]);
684 write_log (_T("\n"));
685 }
686 }
687 return status;
688 }
689