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 
110 {
111  motherBoard.getMSXCliComm().update(CliComm::HARDWARE, name, "remove");
112 
113  unsigned id = name[2] - 'a';
114  assert((*lsInUse)[id]);
115  (*lsInUse)[id] = false;
116 }
117 
118 void SCSILS120::reset()
119 {
120  mediaChanged = false;
121  currentSector = 0;
122  currentLength = 0;
123  busReset();
124 }
125 
126 void SCSILS120::busReset()
127 {
128  keycode = 0;
129  unitAttention = (mode & MODE_UNITATTENTION) != 0;
130 }
131 
132 void SCSILS120::disconnect()
133 {
134  motherBoard.getLedStatus().setLed(LedStatus::FDD, false);
135 }
136 
137 // Check the initiator in the call origin.
138 bool SCSILS120::isSelected()
139 {
140  lun = 0;
141  return file.is_open();
142 }
143 
144 bool SCSILS120::getReady()
145 {
146  if (file.is_open()) return true;
148  return false;
149 }
150 
151 void SCSILS120::testUnitReady()
152 {
153  if ((mode & MODE_NOVAXIS) == 0) {
154  if (getReady() && mediaChanged && (mode & MODE_MEGASCSI)) {
155  // Disk change is surely sent for the driver of MEGA-SCSI.
156  keycode = SCSI::SENSE_POWER_ON;
157  }
158  }
159  mediaChanged = false;
160 }
161 
162 void SCSILS120::startStopUnit()
163 {
164  switch (cdb[4]) {
165  case 2: // Eject
166  eject();
167  break;
168  case 3: // Insert TODO: how can this happen?
169  //if (!diskPresent(diskId)) {
170  // *disk = disk;
171  // updateExtendedDiskName(diskId, disk->fileName, disk->fileNameInZip);
172  // boardChangeDiskette(diskId, disk->fileName, disk->fileNameInZip);
173  //}
174  break;
175  }
176 }
177 
178 unsigned SCSILS120::inquiry()
179 {
180  auto total = getNbSectors();
181  unsigned length = currentLength;
182 
183  bool fdsmode = (total > 0) && (total <= 2880);
184 
185  if (length == 0) return 0;
186 
187  if (fdsmode) {
188  memcpy(buffer + 2, inqData + 2, 6);
189  memcpy(buffer + 8, fds120, 28);
190  } else {
191  memcpy(buffer + 2, inqData + 2, 34);
192  }
193 
194  buffer[0] = SCSI::DT_DirectAccess;
195  buffer[1] = 0x80; // removable
196 
197  if (!(mode & BIT_SCSI2)) {
198  buffer[2] = 1;
199  buffer[3] = 1;
200  if (!fdsmode) buffer[20] = '1';
201  } else {
202  if (mode & BIT_SCSI3) {
203  buffer[2] = 5;
204  if (!fdsmode) buffer[20] = '3';
205  }
206  }
207 
208  if (mode & BIT_SCSI3) {
209  length = std::min(length, 96u);
210  buffer[4] = 91;
211  if (length > 56) {
212  memset(buffer + 56, 0, 40);
213  buffer[58] = 0x03;
214  buffer[60] = 0x01;
215  buffer[61] = 0x80;
216  }
217  } else {
218  length = std::min(length, 56u);
219  }
220 
221  if (length > 36) {
222  std::string filename(FileOperations::getFilename(file.getURL()));
223  filename.resize(20, ' ');
224  memcpy(buffer + 36, filename.data(), 20);
225  }
226  return length;
227 }
228 
229 unsigned SCSILS120::modeSense()
230 {
231  byte* pBuffer = buffer;
232 
233  if ((currentLength > 0) && (cdb[2] == 3)) {
234  auto total = getNbSectors();
235  byte media = MT_UNKNOWN;
236  byte sectors = 64;
237  byte blockLength = SECTOR_SIZE >> 8;
238  byte tracks = 8;
239  byte size = 4 + 24;
240  byte removable = 0xa0;
241 
242  memset(pBuffer + 2, 0, 34);
243 
244  if (total == 0) {
245  media = MT_NO_DISK;
246  } else {
247  if (total == 1440) {
248  media = MT_2DD;
249  sectors = 9;
250  blockLength = 2048 >> 8; // FDS-120 value
251  tracks = 160;
252  } else {
253  if (total == 2880) {
254  media = MT_2HD_144;
255  sectors = 18;
256  blockLength = 2048 >> 8;
257  tracks = 160;
258  }
259  }
260  }
261 
262  // Mode Parameter Header 4bytes
263  pBuffer[1] = media; // Medium Type
264  pBuffer[3] = 8; // block descripter length
265  pBuffer += 4;
266 
267  // Disable Block Descriptor check
268  if (!(cdb[1] & 0x08)) {
269  // Block Descriptor 8bytes
270  pBuffer[1] = (total >> 16) & 0xff; // 1..3 Number of Blocks
271  pBuffer[2] = (total >> 8) & 0xff;
272  pBuffer[3] = (total >> 0) & 0xff;
273  pBuffer[6] = blockLength & 0xff; // 5..7 Block Length in Bytes
274  pBuffer += 8;
275  size += 8;
276  }
277 
278  // Format Device Page 24bytes
279  pBuffer[ 0] = 3; // 0 Page
280  pBuffer[ 1] = 0x16; // 1 Page Length
281  pBuffer[ 3] = tracks; // 2, 3 Tracks per Zone
282  pBuffer[11] = sectors; // 10,11 Sectors per Track
283  pBuffer[12] = blockLength; // 12,13 Data Bytes per Physical Sector
284  pBuffer[20] = removable; // 20 bit7 Soft Sector bit5 Removable
285 
286  buffer[0] = size - 1; // sense data length
287 
288  return std::min<unsigned>(currentLength, size);
289  }
291  return 0;
292 }
293 
294 unsigned SCSILS120::requestSense()
295 {
296  unsigned length = currentLength;
297  unsigned tmpKeycode = unitAttention ? SCSI::SENSE_POWER_ON : keycode;
298  unitAttention = false;
299 
300  keycode = SCSI::SENSE_NO_SENSE;
301 
302  memset(buffer + 1, 0, 17);
303  if (length == 0) {
304  if (mode & BIT_SCSI2) {
305  return 0;
306  }
307  buffer[ 0] = (tmpKeycode >> 8) & 0xff; // Sense code
308  length = 4;
309  } else {
310  buffer[ 0] = 0x70;
311  buffer[ 2] = (tmpKeycode >> 16) & 0xff; // Sense key
312  buffer[ 7] = 10; // Additional sense length
313  buffer[12] = (tmpKeycode >> 8) & 0xff; // Additional sense code
314  buffer[13] = (tmpKeycode >> 0) & 0xff; // Additional sense code qualifier
315  length = std::min(length, 18u);
316  }
317  return length;
318 }
319 
320 bool SCSILS120::checkReadOnly()
321 {
322  if (file.isReadOnly()) {
323  keycode = SCSI::SENSE_WRITE_PROTECT;
324  return true;
325  }
326  return false;
327 }
328 
329 unsigned SCSILS120::readCapacity()
330 {
331  auto block = unsigned(getNbSectors());
332 
333  if (block == 0) {
334  // drive not ready
336  return 0;
337  }
338 
339  --block;
340  Endian::writeB32(&buffer[0], block);
341  Endian::writeB32(&buffer[4], SECTOR_SIZE); // TODO see SCSIHD
342 
343  return 8;
344 }
345 
346 bool SCSILS120::checkAddress()
347 {
348  auto total = unsigned(getNbSectors());
349  if (total == 0) {
350  // drive not ready
352  return false;
353  }
354 
355  if ((currentLength > 0) && (currentSector + currentLength <= total)) {
356  return true;
357  }
359  return false;
360 }
361 
362 // Execute scsiDeviceCheckAddress previously.
363 unsigned SCSILS120::readSector(unsigned& blocks)
364 {
365  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
366 
367  unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
368  unsigned counter = currentLength * SECTOR_SIZE;
369 
370  try {
371  // TODO: somehow map this to SectorAccessibleDisk::readSector?
372  file.seek(SECTOR_SIZE * currentSector);
373  file.read(buffer, SECTOR_SIZE * numSectors);
374  currentSector += numSectors;
375  currentLength -= numSectors;
376  blocks = currentLength;
377  return counter;
378  } catch (FileException&) {
379  blocks = 0;
381  return 0;
382  }
383 }
384 
385 unsigned SCSILS120::dataIn(unsigned& blocks)
386 {
387  if (cdb[0] == SCSI::OP_READ10) {
388  unsigned counter = readSector(blocks);
389  if (counter) {
390  return counter;
391  }
392  }
393  // error
394  blocks = 0;
395  return 0;
396 }
397 
398 // Execute scsiDeviceCheckAddress and scsiDeviceCheckReadOnly previously.
399 unsigned SCSILS120::writeSector(unsigned& blocks)
400 {
401  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
402 
403  unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE);
404 
405  // TODO: somehow map this to SectorAccessibleDisk::writeSector?
406  try {
407  file.seek(SECTOR_SIZE * currentSector);
408  file.write(buffer, SECTOR_SIZE * numSectors);
409  currentSector += numSectors;
410  currentLength -= numSectors;
411 
412  unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
413  blocks = currentLength - tmp;
414  unsigned counter = tmp * SECTOR_SIZE;
415  return counter;
416  } catch (FileException&) {
417  keycode = SCSI::SENSE_WRITE_FAULT;
418  blocks = 0;
419  return 0;
420  }
421 }
422 
423 unsigned SCSILS120::dataOut(unsigned& blocks)
424 {
425  if (cdb[0] == SCSI::OP_WRITE10) {
426  return writeSector(blocks);
427  }
428  // error
429  blocks = 0;
430  return 0;
431 }
432 
433 // MBR erase only
434 void SCSILS120::formatUnit()
435 {
436  if (getReady() && !checkReadOnly()) {
437  memset(buffer, 0, SECTOR_SIZE);
438  try {
439  file.seek(0);
440  file.write(buffer, SECTOR_SIZE);
441  unitAttention = true;
442  mediaChanged = true;
443  } catch (FileException&) {
444  keycode = SCSI::SENSE_WRITE_FAULT;
445  }
446  }
447 }
448 
449 byte SCSILS120::getStatusCode()
450 {
451  return keycode ? SCSI::ST_CHECK_CONDITION : SCSI::ST_GOOD;
452 }
453 
454 void SCSILS120::eject()
455 {
456  file.close();
457  mediaChanged = true;
458  if (mode & MODE_UNITATTENTION) {
459  unitAttention = true;
460  }
461  motherBoard.getMSXCliComm().update(CliComm::MEDIA, name, {});
462 }
463 
464 void SCSILS120::insert(const std::string& filename)
465 {
466  file = File(filename);
467  mediaChanged = true;
468  if (mode & MODE_UNITATTENTION) {
469  unitAttention = true;
470  }
471  motherBoard.getMSXCliComm().update(CliComm::MEDIA, name, filename);
472 }
473 
474 unsigned SCSILS120::executeCmd(const byte* cdb_, SCSI::Phase& phase, unsigned& blocks)
475 {
476  memcpy(cdb, cdb_, sizeof(cdb));
477  message = 0;
478  phase = SCSI::STATUS;
479  blocks = 0;
480 
481  // check unit attention
482  if (unitAttention && (mode & MODE_UNITATTENTION) &&
484  unitAttention = false;
485  keycode = SCSI::SENSE_POWER_ON;
486  if (cdb[0] == SCSI::OP_TEST_UNIT_READY) {
487  mediaChanged = false;
488  }
489  // Unit Attention. This command is not executed.
490  return 0;
491  }
492 
493  // check LUN
494  if (((cdb[1] & 0xe0) || lun) && (cdb[0] != SCSI::OP_REQUEST_SENSE) &&
495  !(cdb[0] == SCSI::OP_INQUIRY && !(mode & MODE_NOVAXIS))) {
496  keycode = SCSI::SENSE_INVALID_LUN;
497  // check LUN error
498  return 0;
499  }
500 
501  if (cdb[0] != SCSI::OP_REQUEST_SENSE) {
502  keycode = SCSI::SENSE_NO_SENSE;
503  }
504 
505  if (cdb[0] < SCSI::OP_GROUP1) {
506  currentSector = ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3];
507  currentLength = cdb[4];
508 
509  switch (cdb[0]) {
511  testUnitReady();
512  return 0;
513 
514  case SCSI::OP_INQUIRY: {
515  unsigned counter = inquiry();
516  if (counter) {
517  phase = SCSI::DATA_IN;
518  }
519  return counter;
520  }
521  case SCSI::OP_REQUEST_SENSE: {
522  unsigned counter = requestSense();
523  if (counter) {
524  phase = SCSI::DATA_IN;
525  }
526  return counter;
527  }
528  case SCSI::OP_READ6:
529  if (currentLength == 0) {
530  currentLength = SECTOR_SIZE >> 1;
531  }
532  if (checkAddress()) {
533  unsigned counter = readSector(blocks);
534  if (counter) {
535  cdb[0] = SCSI::OP_READ10;
536  phase = SCSI::DATA_IN;
537  return counter;
538  }
539  }
540  return 0;
541 
542  case SCSI::OP_WRITE6:
543  if (currentLength == 0) {
544  currentLength = SECTOR_SIZE >> 1;
545  }
546  if (checkAddress() && !checkReadOnly()) {
547  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
548  unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
549  blocks = currentLength - tmp;
550  unsigned counter = tmp * SECTOR_SIZE;
551  cdb[0] = SCSI::OP_WRITE10;
552  phase = SCSI::DATA_OUT;
553  return counter;
554  }
555  return 0;
556 
557  case SCSI::OP_SEEK6:
558  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
559  currentLength = 1;
560  (void)checkAddress();
561  return 0;
562 
563  case SCSI::OP_MODE_SENSE: {
564  unsigned counter = modeSense();
565  if (counter) {
566  phase = SCSI::DATA_IN;
567  }
568  return counter;
569  }
571  formatUnit();
572  return 0;
573 
575  startStopUnit();
576  return 0;
577 
583  // SCSI_Group0 dummy
584  return 0;
585  }
586  } else {
587  currentSector = Endian::read_UA_B32(&cdb[2]);
588  currentLength = Endian::read_UA_B16(&cdb[7]);
589 
590  switch (cdb[0]) {
591  case SCSI::OP_READ10:
592  if (checkAddress()) {
593  unsigned counter = readSector(blocks);
594  if (counter) {
595  phase = SCSI::DATA_IN;
596  return counter;
597  }
598  }
599  return 0;
600 
601  case SCSI::OP_WRITE10:
602  if (checkAddress() && !checkReadOnly()) {
603  unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE);
604  blocks = currentLength - tmp;
605  unsigned counter = tmp * SECTOR_SIZE;
606  phase = SCSI::DATA_OUT;
607  return counter;
608  }
609  return 0;
610 
611  case SCSI::OP_READ_CAPACITY: {
612  unsigned counter = readCapacity();
613  if (counter) {
614  phase = SCSI::DATA_IN;
615  }
616  return counter;
617  }
618  case SCSI::OP_SEEK10:
619  motherBoard.getLedStatus().setLed(LedStatus::FDD, true);
620  currentLength = 1;
621  (void)checkAddress();
622  return 0;
623  }
624  }
625 
626  // unsupported command
628  return 0;
629 }
630 
631 unsigned SCSILS120::executingCmd(SCSI::Phase& phase, unsigned& blocks)
632 {
633  phase = SCSI::EXECUTE;
634  blocks = 0;
635  return 0; // we're very fast
636 }
637 
638 byte SCSILS120::msgIn()
639 {
640  byte result = message;
641  message = 0;
642  return result;
643 }
644 
645 /*
646 scsiDeviceMsgOut()
647 Notes:
648  [out]
649  -1: Busfree demand. (Please process it in the call origin.)
650  bit2: Status phase demand. Error happend.
651  bit1: Make it to a busfree if ATN has not been released.
652  bit0: There is a message(MsgIn).
653 */
654 int SCSILS120::msgOut(byte value)
655 {
656  if (value & 0x80) {
657  lun = value & 7;
658  return 0;
659  }
660 
661  switch (value) {
664  return 6;
665 
667  busReset();
668  [[fallthrough]];
669  case SCSI::MSG_ABORT:
670  return -1;
671 
672  case SCSI::MSG_REJECT:
675  return 2;
676  }
677  message = SCSI::MSG_REJECT;
678  return ((value >= 0x04) && (value <= 0x11)) ? 3 : 1;
679 }
680 
681 size_t SCSILS120::getNbSectorsImpl() const
682 {
683  return file.is_open() ? (const_cast<File&>(file).getSize() / SECTOR_SIZE) : 0;
684 }
685 
686 bool SCSILS120::isWriteProtectedImpl() const
687 {
688  return false;
689 }
690 
691 Sha1Sum SCSILS120::getSha1SumImpl(FilePool& filePool)
692 {
693  if (hasPatches()) {
694  return SectorAccessibleDisk::getSha1SumImpl(filePool);
695  }
696  return filePool.getSha1Sum(file);
697 }
698 
699 void SCSILS120::readSectorsImpl(
700  SectorBuffer* buffers, size_t startSector, size_t num)
701 {
702  file.seek(startSector * sizeof(SectorBuffer));
703  file.read(buffers, num * sizeof(SectorBuffer));
704 }
705 
706 void SCSILS120::writeSectorImpl(size_t sector, const SectorBuffer& buf)
707 {
708  file.seek(sizeof(buf) * sector);
709  file.write(&buf, sizeof(buf));
710 }
711 
712 SectorAccessibleDisk* SCSILS120::getSectorAccessibleDisk()
713 {
714  return this;
715 }
716 
717 std::string_view SCSILS120::getContainerName() const
718 {
719  return name;
720 }
721 
722 bool SCSILS120::diskChanged()
723 {
724  return mediaChanged; // TODO not reset on read
725 }
726 
727 int SCSILS120::insertDisk(const std::string& filename)
728 {
729  try {
730  insert(filename);
731  return 0;
732  } catch (MSXException&) {
733  return -1;
734  }
735 }
736 
737 
738 // class LSXCommand
739 
741  StateChangeDistributor& stateChangeDistributor_,
742  Scheduler& scheduler_, SCSILS120& ls_)
743  : RecordedCommand(commandController_, stateChangeDistributor_,
744  scheduler_, ls_.name)
745  , ls(ls_)
746 {
747 }
748 
750  EmuTime::param /*time*/)
751 {
752  if (tokens.size() == 1) {
753  auto& file = ls.file;
754  result.addListElement(tmpStrCat(ls.name, ':'),
755  file.is_open() ? file.getURL() : std::string{});
756  if (!file.is_open()) result.addListElement("empty");
757  } else if ((tokens.size() == 2) && (tokens[1] == one_of("eject", "-eject"))) {
758  ls.eject();
759  // TODO check for locked tray
760  if (tokens[1] == "-eject") {
761  result = "Warning: use of '-eject' is deprecated, "
762  "instead use the 'eject' subcommand";
763  }
764  } else if ((tokens.size() == 2) ||
765  ((tokens.size() == 3) && (tokens[1] == "insert"))) {
766  int fileToken = 1;
767  if (tokens[1] == "insert") {
768  if (tokens.size() > 2) {
769  fileToken = 2;
770  } else {
771  throw CommandException(
772  "Missing argument to insert subcommand");
773  }
774  }
775  try {
776  ls.insert(userFileContext().resolve(tokens[fileToken].getString()));
777  } catch (FileException& e) {
778  throw CommandException("Can't change disk image: ",
779  e.getMessage());
780  }
781  } else {
782  throw CommandException("Too many or wrong arguments.");
783  }
784 }
785 
786 std::string LSXCommand::help(span<const TclObject> /*tokens*/) const
787 {
788  return strCat(
789  ls.name, " : display the disk image for this LS-120 drive\n",
790  ls.name, " eject : eject the disk image from this LS-120 drive\n",
791  ls.name, " insert <filename> : change the disk image for this LS-120 drive\n",
792  ls.name, " <filename> : change the disk image for this LS-120 drive\n");
793 }
794 
795 void LSXCommand::tabCompletion(std::vector<std::string>& tokens) const
796 {
797  using namespace std::literals;
798  static constexpr std::array extra = {"eject"sv, "insert"sv};
799  completeFileName(tokens, userFileContext(), extra);
800 }
801 
802 
803 template<typename Archive>
804 void SCSILS120::serialize(Archive& ar, unsigned /*version*/)
805 {
806  std::string filename = file.is_open() ? file.getURL() : std::string{};
807  ar.serialize("filename", filename);
808  if constexpr (Archive::IS_LOADER) {
809  // re-insert disk before restoring 'mediaChanged'
810  if (filename.empty()) {
811  eject();
812  } else {
813  insert(filename);
814  }
815  }
816 
817  ar.serialize("keycode", keycode,
818  "currentSector", currentSector,
819  "currentLength", currentLength,
820  "unitAttention", unitAttention,
821  "mediaChanged", mediaChanged,
822  "message", message,
823  "lun", lun);
824  ar.serialize_blob("cdb", cdb, sizeof(cdb));
825 }
828 
829 } // 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 tabCompletion(std::vector< std::string > &tokens) const override
Attempt tab completion for this command.
Definition: SCSILS120.cc:795
std::string help(span< const TclObject > tokens) const override
Print help for this command.
Definition: SCSILS120.cc:786
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:749
LSXCommand(CommandController &commandController, StateChangeDistributor &stateChangeDistributor, Scheduler &scheduler, SCSILS120 &ls)
Definition: SCSILS120.cc:740
void setLed(Led led, bool status)
Definition: LedStatus.cc:40
const std::string & getMessage() const &
Definition: MSXException.hh:23
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:109
SCSILS120(const SCSILS120 &)=delete
void serialize(Archive &ar, unsigned version)
Definition: SCSILS120.cc:804
static constexpr size_t SECTOR_SIZE
virtual Sha1Sum getSha1SumImpl(FilePool &filepool)
void addListElement(const T &t)
Definition: TclObject.hh:129
Definition: span.hh:126
constexpr index_type size() const noexcept
Definition: span.hh:296
ALWAYS_INLINE uint16_t read_UA_B16(const void *p)
Definition: endian.hh:218
ALWAYS_INLINE uint32_t read_UA_B32(const void *p)
Definition: endian.hh:231
void writeB32(void *p, uint32_t x)
Definition: endian.hh:140
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:343
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:173
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:998
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
std::string strCat(Ts &&...ts)
Definition: strCat.hh:591