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