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