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