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