openMSX
SCSILS120.cc
Go to the documentation of this file.
1 /* Ported from:
2 ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.c,v
3 ** Revision: 1.10
4 ** Date: 2007-05-21 21:38:29 +0200 (Mon, 21 May 2007)
5 **
6 ** More info: http://www.bluemsx.com
7 **
8 ** Copyright (C) 2003-2007 Daniel Vik, white cat
9 */
10 
11 /*
12  * Notes:
13  * It follows the SCSI1(CCS) standard or the SCSI2 standard.
14  * Only the direct access device is supported now.
15  * Message system might be imperfect.
16  *
17  * NOTE: this version supports a removable LS-120 disk, as the class
18  * name suggests. Refer to revision 6526 of SCSIHD.cc to see what was removed
19  * from the generic/parameterised code.
20  */
21 
22 #include "SCSILS120.hh"
23 #include "FileOperations.hh"
24 #include "FileException.hh"
25 #include "FilePool.hh"
26 #include "LedStatus.hh"
27 #include "MSXMotherBoard.hh"
28 #include "DeviceConfig.hh"
29 #include "RecordedCommand.hh"
30 #include "CliComm.hh"
31 #include "TclObject.hh"
32 #include "CommandException.hh"
33 #include "FileContext.hh"
34 #include "endian.hh"
35 #include "serialize.hh"
36 #include <algorithm>
37 #include <cstring>
38 #include <memory>
39 #include <vector>
40 
41 using std::string;
42 using std::vector;
43 
44 namespace openmsx {
45 
46 // Medium type (value like LS-120)
47 constexpr byte MT_UNKNOWN = 0x00;
48 constexpr byte MT_2DD_UN = 0x10;
49 constexpr byte MT_2DD = 0x11;
50 constexpr byte MT_2HD_UN = 0x20;
51 constexpr byte MT_2HD_12_98 = 0x22;
52 constexpr byte MT_2HD_12 = 0x23;
53 constexpr byte MT_2HD_144 = 0x24;
54 constexpr byte MT_LS120 = 0x31;
55 constexpr byte MT_NO_DISK = 0x70;
56 constexpr byte MT_DOOR_OPEN = 0x71;
57 constexpr byte MT_FMT_ERROR = 0x72;
58 
59 constexpr byte inqdata[36] = {
60  0, // bit5-0 device type code.
61  0, // bit7 = 1 removable device
62  2, // bit7,6 ISO version. bit5,4,3 ECMA version.
63  // bit2,1,0 ANSI Version (001=SCSI1, 010=SCSI2)
64  2, // bit7 AENC. bit6 TrmIOP.
65  // bit3-0 Response Data Format. (0000=SCSI1, 0001=CCS, 0010=SCSI2)
66  51, // addtional length
67  0, 0,// reserved
68  0, // bit7 RelAdr, bit6 WBus32, bit5 Wbus16, bit4 Sync, bit3 Linked,
69  // bit2 reseved bit1 CmdQue, bit0 SftRe
70  'o', 'p', 'e', 'n', 'M', 'S', 'X', ' ', // vendor ID (8bytes)
71  'S', 'C', 'S', 'I', '2', ' ', 'L', 'S', // product ID (16bytes)
72  '-', '1', '2', '0', 'd', 'i', 's', 'k',
73  '0', '1', '0', 'a' // product version (ASCII 4bytes)
74 };
75 
76 // for FDSFORM.COM
77 constexpr char fds120[28 + 1] = "IODATA LS-120 COSM 0001";
78 
79 constexpr unsigned BUFFER_BLOCK_SIZE = SCSIDevice::BUFFER_SIZE /
81 
82 class LSXCommand final : public RecordedCommand
83 {
84 public:
85  LSXCommand(CommandController& commandController,
86  StateChangeDistributor& stateChangeDistributor,
87  Scheduler& scheduler, SCSILS120& ls);
88  void execute(span<const TclObject> tokens,
89  TclObject& result, EmuTime::param time) override;
90  string help(const vector<string>& tokens) const override;
91  void tabCompletion(vector<string>& tokens) const override;
92 private:
93  SCSILS120& ls;
94 };
95 
96 SCSILS120::SCSILS120(const DeviceConfig& targetconfig,
97  AlignedBuffer& buf, unsigned mode_)
98  : motherBoard(targetconfig.getMotherBoard())
99  , buffer(buf)
100  , name("lsX")
101  , mode(mode_)
102  , scsiId(targetconfig.getAttributeAsInt("id"))
103 {
104  lsInUse = motherBoard.getSharedStuff<LSInUse>("lsInUse");
105 
106  unsigned id = 0;
107  while ((*lsInUse)[id]) {
108  ++id;
109  if (id == MAX_LS) {
110  throw MSXException("Too many LSs");
111  }
112  }
113  name[2] = char('a' + id);
114  (*lsInUse)[id] = true;
115  lsxCommand = std::make_unique<LSXCommand>(
116  motherBoard.getCommandController(),
117  motherBoard.getStateChangeDistributor(),
118  motherBoard.getScheduler(), *this);
119 
120  lun = 0; // TODO move to reset() ?
121  message = 0;
122  reset();
123 }
124 
126 {
127  motherBoard.getMSXCliComm().update(CliComm::HARDWARE, name, "remove");
128 
129  unsigned id = name[2] - 'a';
130  assert((*lsInUse)[id]);
131  (*lsInUse)[id] = false;
132 }
133 
134 void SCSILS120::reset()
135 {
136  mediaChanged = false;
137  currentSector = 0;
138  currentLength = 0;
139  busReset();
140 }
141 
142 void SCSILS120::busReset()
143 {
144  keycode = 0;
145  unitAttention = (mode & MODE_UNITATTENTION) != 0;
146 }
147 
148 void SCSILS120::disconnect()
149 {
150  motherBoard.getLedStatus().setLed(LedStatus::FDD, false);
151 }
152 
153 // Check the initiator in the call origin.
154 bool SCSILS120::isSelected()
155 {
156  lun = 0;
157  return file.is_open();
158 }
159 
160 bool SCSILS120::getReady()
161 {
162  if (file.is_open()) return true;
164  return false;
165 }
166 
167 void SCSILS120::testUnitReady()
168 {
169  if ((mode & MODE_NOVAXIS) == 0) {
170  if (getReady() && mediaChanged && (mode & MODE_MEGASCSI)) {
171  // Disk change is surely sent for the driver of MEGA-SCSI.
172  keycode = SCSI::SENSE_POWER_ON;
173  }
174  }
175  mediaChanged = false;
176 }
177 
178 void SCSILS120::startStopUnit()
179 {
180  switch (cdb[4]) {
181  case 2: // Eject
182  eject();
183  break;
184  case 3: // Insert TODO: how can this happen?
185  //if (!diskPresent(diskId)) {
186  // *disk = disk;
187  // updateExtendedDiskName(diskId, disk->fileName, disk->fileNameInZip);
188  // boardChangeDiskette(diskId, disk->fileName, disk->fileNameInZip);
189  //}
190  break;
191  }
192 }
193 
194 unsigned SCSILS120::inquiry()
195 {
196  auto total = getNbSectors();
197  unsigned length = currentLength;
198 
199  bool fdsmode = (total > 0) && (total <= 2880);
200 
201  if (length == 0) return 0;
202 
203  if (fdsmode) {
204  memcpy(buffer + 2, inqdata + 2, 6);
205  memcpy(buffer + 8, fds120, 28);
206  } else {
207  memcpy(buffer + 2, inqdata + 2, 34);
208  }
209 
210  buffer[0] = SCSI::DT_DirectAccess;
211  buffer[1] = 0x80; // removable
212 
213  if (!(mode & BIT_SCSI2)) {
214  buffer[2] = 1;
215  buffer[3] = 1;
216  if (!fdsmode) buffer[20] = '1';
217  } else {
218  if (mode & BIT_SCSI3) {
219  buffer[2] = 5;
220  if (!fdsmode) buffer[20] = '3';
221  }
222  }
223 
224  if (mode & BIT_SCSI3) {
225  length = std::min(length, 96u);
226  buffer[4] = 91;
227  if (length > 56) {
228  memset(buffer + 56, 0, 40);
229  buffer[58] = 0x03;
230  buffer[60] = 0x01;
231  buffer[61] = 0x80;
232  }
233  } else {
234  length = std::min(length, 56u);
235  }
236 
237  if (length > 36) {
239  filename.resize(20, ' ');
240  memcpy(buffer + 36, filename.data(), 20);
241  }
242  return length;
243 }
244 
245 unsigned SCSILS120::modeSense()
246 {
247  byte* pBuffer = buffer;
248 
249  if ((currentLength > 0) && (cdb[2] == 3)) {
250  auto total = getNbSectors();
251  byte media = MT_UNKNOWN;
252  byte sectors = 64;
253  byte blockLength = SECTOR_SIZE >> 8;
254  byte tracks = 8;
255  byte size = 4 + 24;
256  byte removable = 0xa0;
257 
258  memset(pBuffer + 2, 0, 34);
259 
260  if (total == 0) {
261  media = MT_NO_DISK;
262  } else {
263  if (total == 1440) {
264  media = MT_2DD;
265  sectors = 9;
266  blockLength = 2048 >> 8; // FDS-120 value
267  tracks = 160;
268  } else {
269  if (total == 2880) {
270  media = MT_2HD_144;
271  sectors = 18;
272  blockLength = 2048 >> 8;
273  tracks = 160;
274  }
275  }
276  }
277 
278  // Mode Parameter Header 4bytes
279  pBuffer[1] = media; // Medium Type
280  pBuffer[3] = 8; // block descripter length
281  pBuffer += 4;
282 
283  // Disable Block Descriptor check
284  if (!(cdb[1] & 0x08)) {
285  // Block Descriptor 8bytes
286  pBuffer[1] = (total >> 16) & 0xff; // 1..3 Number of Blocks
287  pBuffer[2] = (total >> 8) & 0xff;
288  pBuffer[3] = (total >> 0) & 0xff;
289  pBuffer[6] = blockLength & 0xff; // 5..7 Block Length in Bytes
290  pBuffer += 8;
291  size += 8;
292  }
293 
294  // Format Device Page 24bytes
295  pBuffer[ 0] = 3; // 0 Page
296  pBuffer[ 1] = 0x16; // 1 Page Length
297  pBuffer[ 3] = tracks; // 2, 3 Tracks per Zone
298  pBuffer[11] = sectors; // 10,11 Sectors per Track
299  pBuffer[12] = blockLength; // 12,13 Data Bytes per Physical Sector
300  pBuffer[20] = removable; // 20 bit7 Soft Sector bit5 Removable
301 
302  buffer[0] = size - 1; // sense data length
303 
304  return std::min<unsigned>(currentLength, size);
305  }
307  return 0;
308 }
309 
310 unsigned SCSILS120::requestSense()
311 {
312  unsigned length = currentLength;
313  unsigned tmpKeycode = unitAttention ? SCSI::SENSE_POWER_ON : keycode;
314  unitAttention = false;
315 
316  keycode = SCSI::SENSE_NO_SENSE;
317 
318  memset(buffer + 1, 0, 17);
319  if (length == 0) {
320  if (mode & BIT_SCSI2) {
321  return 0;
322  }
323  buffer[ 0] = (tmpKeycode >> 8) & 0xff; // Sense code
324  length = 4;
325  } else {
326  buffer[ 0] = 0x70;
327  buffer[ 2] = (tmpKeycode >> 16) & 0xff; // Sense key
328  buffer[ 7] = 10; // Additional sense length
329  buffer[12] = (tmpKeycode >> 8) & 0xff; // Additional sense code
330  buffer[13] = (tmpKeycode >> 0) & 0xff; // Additional sense code qualifier
331  length = std::min(length, 18u);
332  }
333  return length;
334 }
335 
336 bool SCSILS120::checkReadOnly()
337 {
338  if (file.isReadOnly()) {
339  keycode = SCSI::SENSE_WRITE_PROTECT;
340  return true;
341  }
342  return false;
343 }
344 
345 unsigned SCSILS120::readCapacity()
346 {
347  auto block = unsigned(getNbSectors());
348 
349  if (block == 0) {
350  // drive not ready
352  return 0;
353  }
354 
355  --block;
356  Endian::writeB32(&buffer[0], block);
357  Endian::writeB32(&buffer[4], SECTOR_SIZE); // TODO see SCSIHD
358 
359  return 8;
360 }
361 
362 bool SCSILS120::checkAddress()
363 {
364  auto total = unsigned(getNbSectors());
365  if (total == 0) {
366  // drive not ready
368  return false;
369  }
370 
371  if ((currentLength > 0) && (currentSector + currentLength <= total)) {
372  return true;
373  }
375  return false;
376 }
377 
378 // Execute scsiDeviceCheckAddress previously.
379 unsigned SCSILS120::readSector(unsigned& blocks)
380 {
381  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
382 
383  unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
384  unsigned counter = currentLength * SECTOR_SIZE;
385 
386  try {
387  // TODO: somehow map this to SectorAccessibleDisk::readSector?
388  file.seek(SECTOR_SIZE * currentSector);
389  file.read(buffer, SECTOR_SIZE * numSectors);
390  currentSector += numSectors;
391  currentLength -= numSectors;
392  blocks = currentLength;
393  return counter;
394  } catch (FileException&) {
395  blocks = 0;
397  return 0;
398  }
399 }
400 
401 unsigned SCSILS120::dataIn(unsigned& blocks)
402 {
403  if (cdb[0] == SCSI::OP_READ10) {
404  unsigned counter = readSector(blocks);
405  if (counter) {
406  return counter;
407  }
408  }
409  // error
410  blocks = 0;
411  return 0;
412 }
413 
414 // Execute scsiDeviceCheckAddress and scsiDeviceCheckReadOnly previously.
415 unsigned SCSILS120::writeSector(unsigned& blocks)
416 {
417  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
418 
419  unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
420 
421  // TODO: somehow map this to SectorAccessibleDisk::writeSector?
422  try {
423  file.seek(SECTOR_SIZE * currentSector);
424  file.write(buffer, SECTOR_SIZE * numSectors);
425  currentSector += numSectors;
426  currentLength -= numSectors;
427 
428  unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
429  blocks = currentLength - tmp;
430  unsigned counter = tmp * SECTOR_SIZE;
431  return counter;
432  } catch (FileException&) {
433  keycode = SCSI::SENSE_WRITE_FAULT;
434  blocks = 0;
435  return 0;
436  }
437 }
438 
439 unsigned SCSILS120::dataOut(unsigned& blocks)
440 {
441  if (cdb[0] == SCSI::OP_WRITE10) {
442  return writeSector(blocks);
443  }
444  // error
445  blocks = 0;
446  return 0;
447 }
448 
449 // MBR erase only
450 void SCSILS120::formatUnit()
451 {
452  if (getReady() && !checkReadOnly()) {
453  memset(buffer, 0, SECTOR_SIZE);
454  try {
455  file.seek(0);
456  file.write(buffer, SECTOR_SIZE);
457  unitAttention = true;
458  mediaChanged = true;
459  } catch (FileException&) {
460  keycode = SCSI::SENSE_WRITE_FAULT;
461  }
462  }
463 }
464 
465 byte SCSILS120::getStatusCode()
466 {
467  return keycode ? SCSI::ST_CHECK_CONDITION : SCSI::ST_GOOD;
468 }
469 
470 void SCSILS120::eject()
471 {
472  file.close();
473  mediaChanged = true;
474  if (mode & MODE_UNITATTENTION) {
475  unitAttention = true;
476  }
477  motherBoard.getMSXCliComm().update(CliComm::MEDIA, name, {});
478 }
479 
480 void SCSILS120::insert(std::string_view filename)
481 {
482  file = File(filename);
483  mediaChanged = true;
484  if (mode & MODE_UNITATTENTION) {
485  unitAttention = true;
486  }
487  motherBoard.getMSXCliComm().update(CliComm::MEDIA, name, filename);
488 }
489 
490 unsigned SCSILS120::executeCmd(const byte* cdb_, SCSI::Phase& phase, unsigned& blocks)
491 {
492  memcpy(cdb, cdb_, sizeof(cdb));
493  message = 0;
494  phase = SCSI::STATUS;
495  blocks = 0;
496 
497  // check unit attention
498  if (unitAttention && (mode & MODE_UNITATTENTION) &&
499  (cdb[0] != SCSI::OP_INQUIRY) && (cdb[0] != SCSI::OP_REQUEST_SENSE)) {
500  unitAttention = false;
501  keycode = SCSI::SENSE_POWER_ON;
502  if (cdb[0] == SCSI::OP_TEST_UNIT_READY) {
503  mediaChanged = false;
504  }
505  // Unit Attention. This command is not executed.
506  return 0;
507  }
508 
509  // check LUN
510  if (((cdb[1] & 0xe0) || lun) && (cdb[0] != SCSI::OP_REQUEST_SENSE) &&
511  !(cdb[0] == SCSI::OP_INQUIRY && !(mode & MODE_NOVAXIS))) {
512  keycode = SCSI::SENSE_INVALID_LUN;
513  // check LUN error
514  return 0;
515  }
516 
517  if (cdb[0] != SCSI::OP_REQUEST_SENSE) {
518  keycode = SCSI::SENSE_NO_SENSE;
519  }
520 
521  if (cdb[0] < SCSI::OP_GROUP1) {
522  currentSector = ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3];
523  currentLength = cdb[4];
524 
525  switch (cdb[0]) {
527  testUnitReady();
528  return 0;
529 
530  case SCSI::OP_INQUIRY: {
531  unsigned counter = inquiry();
532  if (counter) {
533  phase = SCSI::DATA_IN;
534  }
535  return counter;
536  }
537  case SCSI::OP_REQUEST_SENSE: {
538  unsigned counter = requestSense();
539  if (counter) {
540  phase = SCSI::DATA_IN;
541  }
542  return counter;
543  }
544  case SCSI::OP_READ6:
545  if (currentLength == 0) {
546  currentLength = SECTOR_SIZE >> 1;
547  }
548  if (checkAddress()) {
549  unsigned counter = readSector(blocks);
550  if (counter) {
551  cdb[0] = SCSI::OP_READ10;
552  phase = SCSI::DATA_IN;
553  return counter;
554  }
555  }
556  return 0;
557 
558  case SCSI::OP_WRITE6:
559  if (currentLength == 0) {
560  currentLength = SECTOR_SIZE >> 1;
561  }
562  if (checkAddress() && !checkReadOnly()) {
563  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
564  unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
565  blocks = currentLength - tmp;
566  unsigned counter = tmp * SECTOR_SIZE;
567  cdb[0] = SCSI::OP_WRITE10;
568  phase = SCSI::DATA_OUT;
569  return counter;
570  }
571  return 0;
572 
573  case SCSI::OP_SEEK6:
574  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
575  currentLength = 1;
576  checkAddress();
577  return 0;
578 
579  case SCSI::OP_MODE_SENSE: {
580  unsigned counter = modeSense();
581  if (counter) {
582  phase = SCSI::DATA_IN;
583  }
584  return counter;
585  }
587  formatUnit();
588  return 0;
589 
591  startStopUnit();
592  return 0;
593 
599  // SCSI_Group0 dummy
600  return 0;
601  }
602  } else {
603  currentSector = Endian::read_UA_B32(&cdb[2]);
604  currentLength = Endian::read_UA_B16(&cdb[7]);
605 
606  switch (cdb[0]) {
607  case SCSI::OP_READ10:
608  if (checkAddress()) {
609  unsigned counter = readSector(blocks);
610  if (counter) {
611  phase = SCSI::DATA_IN;
612  return counter;
613  }
614  }
615  return 0;
616 
617  case SCSI::OP_WRITE10:
618  if (checkAddress() && !checkReadOnly()) {
619  unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
620  blocks = currentLength - tmp;
621  unsigned counter = tmp * SECTOR_SIZE;
622  phase = SCSI::DATA_OUT;
623  return counter;
624  }
625  return 0;
626 
627  case SCSI::OP_READ_CAPACITY: {
628  unsigned counter = readCapacity();
629  if (counter) {
630  phase = SCSI::DATA_IN;
631  }
632  return counter;
633  }
634  case SCSI::OP_SEEK10:
635  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
636  currentLength = 1;
637  checkAddress();
638  return 0;
639  }
640  }
641 
642  // unsupported command
644  return 0;
645 }
646 
647 unsigned SCSILS120::executingCmd(SCSI::Phase& phase, unsigned& blocks)
648 {
649  phase = SCSI::EXECUTE;
650  blocks = 0;
651  return 0; // we're very fast
652 }
653 
654 byte SCSILS120::msgIn()
655 {
656  byte result = message;
657  message = 0;
658  return result;
659 }
660 
661 /*
662 scsiDeviceMsgOut()
663 Notes:
664  [out]
665  -1: Busfree demand. (Please process it in the call origin.)
666  bit2: Status phase demand. Error happend.
667  bit1: Make it to a busfree if ATN has not been released.
668  bit0: There is a message(MsgIn).
669 */
670 int SCSILS120::msgOut(byte value)
671 {
672  if (value & 0x80) {
673  lun = value & 7;
674  return 0;
675  }
676 
677  switch (value) {
680  return 6;
681 
683  busReset();
684  [[fallthrough]];
685  case SCSI::MSG_ABORT:
686  return -1;
687 
688  case SCSI::MSG_REJECT:
691  return 2;
692  }
693  message = SCSI::MSG_REJECT;
694  return ((value >= 0x04) && (value <= 0x11)) ? 3 : 1;
695 }
696 
697 size_t SCSILS120::getNbSectorsImpl() const
698 {
699  return file.is_open() ? (const_cast<File&>(file).getSize() / SECTOR_SIZE) : 0;
700 }
701 
702 bool SCSILS120::isWriteProtectedImpl() const
703 {
704  return false;
705 }
706 
707 Sha1Sum SCSILS120::getSha1SumImpl(FilePool& filePool)
708 {
709  if (hasPatches()) {
710  return SectorAccessibleDisk::getSha1SumImpl(filePool);
711  }
712  return filePool.getSha1Sum(file);
713 }
714 
715 void SCSILS120::readSectorImpl(size_t sector, SectorBuffer& buf)
716 {
717  file.seek(sizeof(buf) * sector);
718  file.read(&buf, sizeof(buf));
719 }
720 
721 void SCSILS120::writeSectorImpl(size_t sector, const SectorBuffer& buf)
722 {
723  file.seek(sizeof(buf) * sector);
724  file.write(&buf, sizeof(buf));
725 }
726 
727 SectorAccessibleDisk* SCSILS120::getSectorAccessibleDisk()
728 {
729  return this;
730 }
731 
732 const std::string& SCSILS120::getContainerName() const
733 {
734  return name;
735 }
736 
737 bool SCSILS120::diskChanged()
738 {
739  return mediaChanged; // TODO not reset on read
740 }
741 
742 int SCSILS120::insertDisk(std::string_view filename)
743 {
744  try {
745  insert(filename);
746  return 0;
747  } catch (MSXException&) {
748  return -1;
749  }
750 }
751 
752 
753 // class LSXCommand
754 
756  StateChangeDistributor& stateChangeDistributor_,
757  Scheduler& scheduler_, SCSILS120& ls_)
758  : RecordedCommand(commandController_, stateChangeDistributor_,
759  scheduler_, ls_.name)
760  , ls(ls_)
761 {
762 }
763 
765  EmuTime::param /*time*/)
766 {
767  if (tokens.size() == 1) {
768  auto& file = ls.file;
769  result.addListElement(ls.name + ':',
770  file.is_open() ? file.getURL() : string{});
771  if (!file.is_open()) result.addListElement("empty");
772  } else if ((tokens.size() == 2) &&
773  ((tokens[1] == "eject") || (tokens[1] == "-eject"))) {
774  ls.eject();
775  // TODO check for locked tray
776  if (tokens[1] == "-eject") {
777  result = "Warning: use of '-eject' is deprecated, "
778  "instead use the 'eject' subcommand";
779  }
780  } else if ((tokens.size() == 2) ||
781  ((tokens.size() == 3) && (tokens[1] == "insert"))) {
782  int fileToken = 1;
783  if (tokens[1] == "insert") {
784  if (tokens.size() > 2) {
785  fileToken = 2;
786  } else {
787  throw CommandException(
788  "Missing argument to insert subcommand");
789  }
790  }
791  try {
792  string filename = userFileContext().resolve(
793  string(tokens[fileToken].getString()));
794  ls.insert(filename);
795  // return filename; // Note: the diskX command doesn't do this either, so this has not been converted to TclObject style here
796  } catch (FileException& e) {
797  throw CommandException("Can't change disk image: ",
798  e.getMessage());
799  }
800  } else {
801  throw CommandException("Too many or wrong arguments.");
802  }
803 }
804 
805 string LSXCommand::help(const vector<string>& /*tokens*/) const
806 {
807  return strCat(
808  ls.name, " : display the disk image for this LS-120 drive\n",
809  ls.name, " eject : eject the disk image from this LS-120 drive\n",
810  ls.name, " insert <filename> : change the disk image for this LS-120 drive\n",
811  ls.name, " <filename> : change the disk image for this LS-120 drive\n");
812 }
813 
814 void LSXCommand::tabCompletion(vector<string>& tokens) const
815 {
816  static constexpr const char* const extra[] = { "eject", "insert" };
817  completeFileName(tokens, userFileContext(), extra);
818 }
819 
820 
821 template<typename Archive>
822 void SCSILS120::serialize(Archive& ar, unsigned /*version*/)
823 {
824  string filename = file.is_open() ? file.getURL() : string{};
825  ar.serialize("filename", filename);
826  if (ar.isLoader()) {
827  // re-insert disk before restoring 'mediaChanged'
828  if (filename.empty()) {
829  eject();
830  } else {
831  insert(filename);
832  }
833  }
834 
835  ar.serialize("keycode", keycode,
836  "currentSector", currentSector,
837  "currentLength", currentLength,
838  "unitAttention", unitAttention,
839  "mediaChanged", mediaChanged,
840  "message", message,
841  "lun", lun);
842  ar.serialize_blob("cdb", cdb, sizeof(cdb));
843 }
846 
847 } // namespace openmsx
bool isReadOnly() const
Check if this file is readonly.
Definition: File.cc:144
string_view getFilename(string_view path)
constexpr byte DT_DirectAccess
Definition: SCSI.hh:87
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:343
static constexpr unsigned BIT_SCSI3
Definition: SCSIDevice.hh:13
LSXCommand(CommandController &commandController, StateChangeDistributor &stateChangeDistributor, Scheduler &scheduler, SCSILS120 &ls)
Definition: SCSILS120.cc:755
constexpr byte OP_RESERVE_UNIT
Definition: SCSI.hh:27
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
constexpr byte OP_GROUP1
Definition: SCSI.hh:34
static constexpr size_t SECTOR_SIZE
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
const std::string & getMessage() const &
Definition: MSXException.hh:23
constexpr byte OP_INQUIRY
Definition: SCSI.hh:26
constexpr byte ST_CHECK_CONDITION
Definition: SCSI.hh:83
constexpr byte OP_SEEK10
Definition: SCSI.hh:38
constexpr byte MT_2DD
Definition: SCSIHD.cc:40
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
constexpr byte inqdata[36]
Definition: SCSIHD.cc:50
constexpr byte OP_READ6
Definition: SCSI.hh:23
Definition: span.hh:34
constexpr byte MT_2DD_UN
Definition: SCSIHD.cc:39
constexpr byte OP_SEEK6
Definition: SCSI.hh:25
constexpr unsigned SENSE_INVALID_COMMAND_CODE
Definition: SCSI.hh:63
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
Commands that directly influence the MSX state should send and events so that they can be recorded by...
static constexpr unsigned MODE_NOVAXIS
Definition: SCSIDevice.hh:21
static constexpr unsigned MODE_MEGASCSI
Definition: SCSIDevice.hh:19
constexpr byte OP_RELEASE_UNIT
Definition: SCSI.hh:28
constexpr byte OP_WRITE10
Definition: SCSI.hh:37
constexpr byte OP_TEST_UNIT_READY
Definition: SCSI.hh:18
static constexpr unsigned MODE_UNITATTENTION
Definition: SCSIDevice.hh:18
void serialize(Archive &ar, unsigned version)
Definition: SCSILS120.cc:822
size_t size(std::string_view utf8)
constexpr byte ST_GOOD
Definition: SCSI.hh:82
constexpr byte OP_MODE_SENSE
Definition: SCSI.hh:29
constexpr byte OP_READ10
Definition: SCSI.hh:36
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
Definition: SCSILS120.cc:814
std::string getURL() const
Returns the URL of this file object.
Definition: File.cc:128
constexpr unsigned BUFFER_BLOCK_SIZE
Definition: SCSIHD.cc:67
static void completeFileName(std::vector< std::string > &tokens, const FileContext &context, const RANGE &extra)
Definition: Completer.hh:139
constexpr unsigned SENSE_WRITE_PROTECT
Definition: SCSI.hh:67
std::string resolve(std::string_view filename) const
Definition: FileContext.cc:77
constexpr byte MSG_ABORT
Definition: SCSI.hh:75
FileContext userFileContext(string_view savePath)
Definition: FileContext.cc:161
constexpr byte MT_UNKNOWN
Definition: SCSIHD.cc:38
This class represents the result of a sha1 calculation (a 160-bit value).
Definition: sha1.hh:19
constexpr const char *const filename
constexpr byte MT_2HD_12
Definition: SCSIHD.cc:43
bool is_open() const
Return true iff this file handle refers to an open file.
Definition: File.hh:61
string help(const vector< string > &tokens) const override
Print help for this command.
Definition: SCSILS120.cc:805
constexpr unsigned SENSE_UNRECOVERED_READ_ERROR
Definition: SCSI.hh:61
constexpr byte MSG_INITIATOR_DETECT_ERROR
Definition: SCSI.hh:74
constexpr byte MT_2HD_UN
Definition: SCSIHD.cc:41
static constexpr unsigned BIT_SCSI2
Definition: SCSIDevice.hh:11
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
constexpr unsigned SENSE_WRITE_FAULT
Definition: SCSI.hh:62
virtual Sha1Sum getSha1SumImpl(FilePool &filepool)
constexpr unsigned SENSE_INITIATOR_DETECTED_ERR
Definition: SCSI.hh:69
CommandController & getCommandController()
constexpr byte MT_2HD_12_98
Definition: SCSIHD.cc:42
constexpr byte MSG_PARITY_ERROR
Definition: SCSI.hh:78
constexpr unsigned SENSE_MEDIUM_NOT_PRESENT
Definition: SCSI.hh:60
constexpr byte OP_FORMAT_UNIT
Definition: SCSI.hh:21
void execute(span< const TclObject > tokens, TclObject &result, EmuTime::param time) override
This is like the execute() method of the Command class, it only has an extra time parameter...
Definition: SCSILS120.cc:764
constexpr unsigned SENSE_ILLEGAL_BLOCK_ADDRESS
Definition: SCSI.hh:64
constexpr byte MT_DOOR_OPEN
Definition: SCSIHD.cc:47
void addListElement(T t)
Definition: TclObject.hh:121
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
constexpr byte OP_READ_CAPACITY
Definition: SCSI.hh:35
SCSILS120(const SCSILS120 &)=delete
Sha1Sum getSha1Sum(File &file)
Calculate sha1sum for the given File object.
Definition: FilePool.cc:545
std::shared_ptr< T > getSharedStuff(std::string_view name, Args &&...args)
Some MSX device parts are shared between several MSX devices (e.g.
constexpr byte MT_NO_DISK
Definition: SCSIHD.cc:46
void write(const void *buffer, size_t num)
Write to file.
Definition: File.cc:88
void close()
Close the current file.
Definition: File.cc:78
static constexpr unsigned BUFFER_SIZE
Definition: SCSIDevice.hh:23
constexpr byte MT_2HD_144
Definition: SCSIHD.cc:44
constexpr byte OP_REQUEST_SENSE
Definition: SCSI.hh:20
void seek(size_t pos)
Move read/write pointer to the specified position.
Definition: File.cc:108
std::string strCat(Ts &&...ts)
Definition: strCat.hh:573
constexpr byte MT_LS120
Definition: SCSIHD.cc:45
void setLed(Led led, bool status)
Definition: LedStatus.cc:39
constexpr byte MSG_REJECT
Definition: SCSI.hh:76
constexpr byte OP_SEND_DIAGNOSTIC
Definition: SCSI.hh:31
void read(void *buffer, size_t num)
Read from file.
Definition: File.cc:83
constexpr byte OP_REASSIGN_BLOCKS
Definition: SCSI.hh:22
constexpr byte MT_FMT_ERROR
Definition: SCSIHD.cc:48
constexpr byte MSG_BUS_DEVICE_RESET
Definition: SCSI.hh:79
constexpr char fds120[28+1]
Definition: SCSILS120.cc:77
constexpr byte OP_REZERO_UNIT
Definition: SCSI.hh:19
constexpr unsigned SENSE_INVALID_LUN
Definition: SCSI.hh:65
constexpr byte MSG_NO_OPERATION
Definition: SCSI.hh:77
constexpr unsigned SENSE_POWER_ON
Definition: SCSI.hh:66
~SCSILS120() override
Definition: SCSILS120.cc:125
constexpr byte OP_START_STOP_UNIT
Definition: SCSI.hh:30
StateChangeDistributor & getStateChangeDistributor()
constexpr byte OP_WRITE6
Definition: SCSI.hh:24
constexpr unsigned SENSE_NO_SENSE
Definition: SCSI.hh:58