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