From 5def9e3dfe0746121e82fc09b13dccb22796fc2a Mon Sep 17 00:00:00 2001 From: akuker <34318535+akuker@users.noreply.github.com> Date: Fri, 28 Aug 2020 09:18:02 -0500 Subject: [PATCH] Break up disk.h and disk.cpp to be smaller files (#22) --- src/raspberrypi/Makefile | 66 +- src/raspberrypi/controllers/sasidev_ctrl.cpp | 1999 ++++ src/raspberrypi/controllers/sasidev_ctrl.h | 211 + src/raspberrypi/controllers/scsidev_ctrl.cpp | 1869 ++++ src/raspberrypi/controllers/scsidev_ctrl.h | 142 + src/raspberrypi/devices/cfilesystem.cpp | 4794 +++++++++ src/raspberrypi/devices/cfilesystem.h | 1184 +++ src/raspberrypi/devices/ctapdriver.cpp | 209 + src/raspberrypi/devices/ctapdriver.h | 53 + src/raspberrypi/devices/disk.cpp | 2346 +++++ src/raspberrypi/devices/disk.h | 368 + src/raspberrypi/devices/sasihd.cpp | 164 + src/raspberrypi/devices/sasihd.h | 40 + src/raspberrypi/devices/scsi_host_bridge.cpp | 1502 +++ src/raspberrypi/devices/scsi_host_bridge.h | 153 + src/raspberrypi/devices/scsicd.cpp | 1089 ++ src/raspberrypi/devices/scsicd.h | 230 + src/raspberrypi/devices/scsihd.cpp | 268 + src/raspberrypi/devices/scsihd.h | 44 + src/raspberrypi/devices/scsihd_apple.cpp | 93 + src/raspberrypi/devices/scsihd_apple.h | 39 + src/raspberrypi/devices/scsihd_nec.cpp | 295 + src/raspberrypi/devices/scsihd_nec.h | 61 + src/raspberrypi/devices/scsimo.cpp | 421 + src/raspberrypi/devices/scsimo.h | 49 + src/raspberrypi/disk.cpp | 9872 ------------------ src/raspberrypi/disk.h | 1148 -- src/raspberrypi/fileio.h | 2 + src/raspberrypi/filepath.h | 2 + src/raspberrypi/rascsi.cpp | 12 +- 30 files changed, 17683 insertions(+), 11042 deletions(-) create mode 100644 src/raspberrypi/controllers/sasidev_ctrl.cpp create mode 100644 src/raspberrypi/controllers/sasidev_ctrl.h create mode 100644 src/raspberrypi/controllers/scsidev_ctrl.cpp create mode 100644 src/raspberrypi/controllers/scsidev_ctrl.h create mode 100644 src/raspberrypi/devices/cfilesystem.cpp create mode 100644 src/raspberrypi/devices/cfilesystem.h create mode 100644 src/raspberrypi/devices/ctapdriver.cpp create mode 100644 src/raspberrypi/devices/ctapdriver.h create mode 100644 src/raspberrypi/devices/disk.cpp create mode 100644 src/raspberrypi/devices/disk.h create mode 100644 src/raspberrypi/devices/sasihd.cpp create mode 100644 src/raspberrypi/devices/sasihd.h create mode 100644 src/raspberrypi/devices/scsi_host_bridge.cpp create mode 100644 src/raspberrypi/devices/scsi_host_bridge.h create mode 100644 src/raspberrypi/devices/scsicd.cpp create mode 100644 src/raspberrypi/devices/scsicd.h create mode 100644 src/raspberrypi/devices/scsihd.cpp create mode 100644 src/raspberrypi/devices/scsihd.h create mode 100644 src/raspberrypi/devices/scsihd_apple.cpp create mode 100644 src/raspberrypi/devices/scsihd_apple.h create mode 100644 src/raspberrypi/devices/scsihd_nec.cpp create mode 100644 src/raspberrypi/devices/scsihd_nec.h create mode 100644 src/raspberrypi/devices/scsimo.cpp create mode 100644 src/raspberrypi/devices/scsimo.h delete mode 100644 src/raspberrypi/disk.cpp delete mode 100644 src/raspberrypi/disk.h diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 0ea3860b..49a4f166 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -9,15 +9,17 @@ CXX = $(CROSS_COMPILE)g++ DEBUG ?= 0 ifeq ($(DEBUG), 1) # Debug CFLAGS - CFLAGS = -DDISK_LOG -O0 -g -Wall -DDEBUG - CXXFLAGS = -DDISK_LOG -O0 -g -Wall -DDEBUG + CFLAGS += -DDISK_LOG -O0 -g -Wall -DDEBUG + CXXFLAGS += -DDISK_LOG -O0 -g -Wall -DDEBUG BUILD_TYPE = Debug else # Release CFLAGS - CFLAGS ?= -O3 -Wall -Werror - CXXFLAGS ?= -O3 -Wall -Werror + CFLAGS += -O3 -Wall -Werror + CXXFLAGS += -O3 -Wall -Werror BUILD_TYPE = Release endif +CFLAGS += -iquote . +CXXFLAGS += -std=c++14 -iquote . # If its not specified, build for STANDARD configuration CONNECT_TYPE ?= STANDARD @@ -37,24 +39,31 @@ USR_LOCAL_BIN = /usr/local/bin MAN_PAGE_DIR = /usr/share/man/man1 DOC_DIR = ../../doc +OBJDIR := ./obj +BINDIR := ./bin + #BIN_ALL = $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON) # Temporarily remove the RASDUMP and RASDUMP tools, since they're not needed # for my specific use case. If you need them - add them back in! -BIN_ALL = $(RASCSI) $(RASCTL) +BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) SRC_RASCSI = \ rascsi.cpp \ scsi.cpp \ - disk.cpp \ gpiobus.cpp \ - ctapdriver.cpp \ - cfilesystem.cpp \ filepath.cpp \ - fileio.cpp + fileio.cpp\ +# os.cpp +# rasctl_command.cpp +# rascsi_mgr.cpp +# command_thread.cpp +SRC_RASCSI += $(notdir $(shell find ./controllers -name '*.cpp')) +SRC_RASCSI += $(notdir $(shell find ./devices -name '*.cpp')) SRC_RASCTL = \ rasctl.cpp +# rasctl_command.cpp SRC_RASDUMP = \ rasdump.cpp \ @@ -70,18 +79,31 @@ SRC_SASIDUMP = \ filepath.cpp \ fileio.cpp -OBJ_RASCSI := $(SRC_RASCSI:%.cpp=%.o) -OBJ_RASCTL := $(SRC_RASCTL:%.cpp=%.o) -OBJ_RASDUMP := $(SRC_RASDUMP:%.cpp=%.o) -OBJ_SASIDUMP := $(SRC_SASIDUMP:%.cpp=%.o) -OBJ_SCSIMON := $(SRC_SCSIMON:%.cpp=%.o) +vpath %.h ./ ./controllers ./devices +vpath %.cpp ./ ./controllers ./devices +vpath %.o ./$(OBJDIR) +vpath ./$(BINDIR) + + +OBJ_RASCSI := $(SRC_RASCSI:%.cpp=$(OBJDIR)/%.o) +OBJ_RASCTL := $(SRC_RASCTL:%.cpp=$(OBJDIR)/%.o) +OBJ_RASDUMP := $(SRC_RASDUMP:%.cpp=$(OBJDIR)/%.o) +OBJ_SASIDUMP := $(SRC_SASIDUMP:%.cpp=$(OBJDIR)/%.o) +OBJ_SCSIMON := $(SRC_SCSIMON:%.cpp=$(OBJDIR)/%.o) #OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) -%.o: %.cpp +$(OBJDIR) $(BINDIR): + mkdir -p $@ + +$(OBJDIR)/%.o: %.cpp $(OBJDIR) $(CXX) $(CXXFLAGS) -c $< -o $@ +# $(OBJDIR)/%.o: %.c +# $(CXX) $(CXXFLAGS) -c $< -o $@ +# %.o: %.cpp +# $(CXX) $(CXXFLAGS) -c $(OBJDIR)/$< -o $@ .DEFAULT_GOAL := all .PHONY: all ALL docs @@ -90,23 +112,25 @@ ALL: all docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt -$(RASCSI): $(OBJ_RASCSI) +$(BINDIR)/$(RASCSI): $(OBJ_RASCSI) $(BINDIR) + @echo -- Linking $(RASCSI) $(CXX) -o $@ $(OBJ_RASCSI) -lpthread -$(RASCTL): $(OBJ_RASCTL) +$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) $(BINDIR) + @echo -- Linking $(RASCTL) $(CXX) -o $@ $(OBJ_RASCTL) -$(RASDUMP): $(OBJ_RASDUMP) +$(RASDUMP): $(OBJ_RASDUMP) $(BINDIR) $(CXX) -o $@ $(OBJ_RASDUMP) -$(SASIDUMP): $(OBJ_SASIDUMP) +$(SASIDUMP): $(OBJ_SASIDUMP) $(BINDIR) $(CXX) -o $@ $(OBJ_SASIDUMP) -$(SCSIMON): $(OBJ_SCSIMON) +$(SCSIMON): $(OBJ_SCSIMON) $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread clean: - rm -f $(OBJ_ALL) $(BIN_ALL) + rm -rf $(OBJDIR) $(BINDIR) run: sudo ./$(RASCSI) -ID1 /home/pi/HARDDISK2.hda -ID6 /home/pi/marathon.iso diff --git a/src/raspberrypi/controllers/sasidev_ctrl.cpp b/src/raspberrypi/controllers/sasidev_ctrl.cpp new file mode 100644 index 00000000..1f97d867 --- /dev/null +++ b/src/raspberrypi/controllers/sasidev_ctrl.cpp @@ -0,0 +1,1999 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SASI device controller ] +// +//--------------------------------------------------------------------------- +#include "controllers/sasidev_ctrl.h" +#include "filepath.h" +#include "gpiobus.h" +#include "devices/scsi_host_bridge.h" + +//=========================================================================== +// +// SASI Device +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +#ifdef RASCSI +SASIDEV::SASIDEV() +#else +SASIDEV::SASIDEV(Device *dev) +#endif // RASCSI +{ + int i; + +#ifndef RASCSI + // Remember host device + host = dev; +#endif // RASCSI + + // Work initialization + ctrl.phase = BUS::busfree; + ctrl.id = -1; + ctrl.bus = NULL; + memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd)); + ctrl.status = 0x00; + ctrl.message = 0x00; +#ifdef RASCSI + ctrl.execstart = 0; +#endif // RASCSI + ctrl.bufsize = 0x800; + ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); + memset(ctrl.buffer, 0x00, ctrl.bufsize); + ctrl.blocks = 0; + ctrl.next = 0; + ctrl.offset = 0; + ctrl.length = 0; + + // Logical unit initialization + for (i = 0; i < UnitMax; i++) { + ctrl.unit[i] = NULL; + } +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +SASIDEV::~SASIDEV() +{ + // Free the buffer + if (ctrl.buffer) { + free(ctrl.buffer); + ctrl.buffer = NULL; + } +} + +//--------------------------------------------------------------------------- +// +// Device reset +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Reset() +{ + int i; + + ASSERT(this); + + // Work initialization + memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd)); + ctrl.phase = BUS::busfree; + ctrl.status = 0x00; + ctrl.message = 0x00; +#ifdef RASCSI + ctrl.execstart = 0; +#endif // RASCSI + memset(ctrl.buffer, 0x00, ctrl.bufsize); + ctrl.blocks = 0; + ctrl.next = 0; + ctrl.offset = 0; + ctrl.length = 0; + + // Unit initialization + for (i = 0; i < UnitMax; i++) { + if (ctrl.unit[i]) { + ctrl.unit[i]->Reset(); + } + } +} + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Save +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SASIDEV::Save(Fileio *fio, int /*ver*/) +{ + DWORD sz; + + ASSERT(this); + ASSERT(fio); + + // Save size + sz = 2120; + if (!fio->Write(&sz, sizeof(sz))) { + return FALSE; + } + + // Save entity + PROP_EXPORT(fio, ctrl.phase); + PROP_EXPORT(fio, ctrl.id); + PROP_EXPORT(fio, ctrl.cmd); + PROP_EXPORT(fio, ctrl.status); + PROP_EXPORT(fio, ctrl.message); + if (!fio->Write(ctrl.buffer, 0x800)) { + return FALSE; + } + PROP_EXPORT(fio, ctrl.blocks); + PROP_EXPORT(fio, ctrl.next); + PROP_EXPORT(fio, ctrl.offset); + PROP_EXPORT(fio, ctrl.length); + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Load +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SASIDEV::Load(Fileio *fio, int ver) +{ + DWORD sz; + + ASSERT(this); + ASSERT(fio); + + // Not saved before version 3.11 + if (ver <= 0x0311) { + return TRUE; + } + + // Load size and check if the size matches + if (!fio->Read(&sz, sizeof(sz))) { + return FALSE; + } + if (sz != 2120) { + return FALSE; + } + + // Load the entity + PROP_IMPORT(fio, ctrl.phase); + PROP_IMPORT(fio, ctrl.id); + PROP_IMPORT(fio, ctrl.cmd); + PROP_IMPORT(fio, ctrl.status); + PROP_IMPORT(fio, ctrl.message); + if (!fio->Read(ctrl.buffer, 0x800)) { + return FALSE; + } + PROP_IMPORT(fio, ctrl.blocks); + PROP_IMPORT(fio, ctrl.next); + PROP_IMPORT(fio, ctrl.offset); + PROP_IMPORT(fio, ctrl.length); + + return TRUE; +} +#endif // RASCSI + +//--------------------------------------------------------------------------- +// +// Connect the controller +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Connect(int id, BUS *bus) +{ + ASSERT(this); + + ctrl.id = id; + ctrl.bus = bus; +} + +//--------------------------------------------------------------------------- +// +// Get the logical unit +// +//--------------------------------------------------------------------------- +Disk* FASTCALL SASIDEV::GetUnit(int no) +{ + ASSERT(this); + ASSERT(no < UnitMax); + + return ctrl.unit[no]; +} + +//--------------------------------------------------------------------------- +// +// Set the logical unit +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::SetUnit(int no, Disk *dev) +{ + ASSERT(this); + ASSERT(no < UnitMax); + + ctrl.unit[no] = dev; +} + +//--------------------------------------------------------------------------- +// +// Check to see if this has a valid logical unit +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SASIDEV::HasUnit() +{ + int i; + + ASSERT(this); + + for (i = 0; i < UnitMax; i++) { + if (ctrl.unit[i]) { + return TRUE; + } + } + + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// Get internal data +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::GetCTRL(ctrl_t *buffer) +{ + ASSERT(this); + ASSERT(buffer); + + // reference the internal structure + *buffer = ctrl; +} + +//--------------------------------------------------------------------------- +// +// Get a busy unit +// +//--------------------------------------------------------------------------- +Disk* FASTCALL SASIDEV::GetBusyUnit() +{ + DWORD lun; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + return ctrl.unit[lun]; +} + +//--------------------------------------------------------------------------- +// +// Run +// +//--------------------------------------------------------------------------- +BUS::phase_t FASTCALL SASIDEV::Process() +{ + ASSERT(this); + + // Do nothing if not connected + if (ctrl.id < 0 || ctrl.bus == NULL) { + return ctrl.phase; + } + + // Get bus information + ctrl.bus->Aquire(); + + // For the monitor tool, we shouldn't need to reset. We're just logging information + // Reset + if (ctrl.bus->GetRST()) { +#if defined(DISK_LOG) + Log(Log::Normal, "RESET signal received"); +#endif // DISK_LOG + + // Reset the controller + Reset(); + + // Reset the bus + ctrl.bus->Reset(); + return ctrl.phase; + } + + // Phase processing + switch (ctrl.phase) { + // Bus free + case BUS::busfree: + BusFree(); + break; + + // Selection + case BUS::selection: + Selection(); + break; + + // Data out (MCI=000) + case BUS::dataout: + DataOut(); + break; + + // Data in (MCI=001) + case BUS::datain: + DataIn(); + break; + + // Command (MCI=010) + case BUS::command: + Command(); + break; + + // Status (MCI=011) + case BUS::status: + Status(); + break; + + // Msg in (MCI=111) + case BUS::msgin: + MsgIn(); + break; + + // Other + default: + ASSERT(FALSE); + break; + } + + return ctrl.phase; +} + +//--------------------------------------------------------------------------- +// +// Bus free phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::BusFree() +{ + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::busfree) { + +#if defined(DISK_LOG) + Log(Log::Normal, "Bus free phase"); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::busfree; + + // Set Signal lines + ctrl.bus->SetREQ(FALSE); + ctrl.bus->SetMSG(FALSE); + ctrl.bus->SetCD(FALSE); + ctrl.bus->SetIO(FALSE); + ctrl.bus->SetBSY(FALSE); + + // Initialize status and message + ctrl.status = 0x00; + ctrl.message = 0x00; + return; + } + + // Move to selection phase + if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) { + Selection(); + } +} + +//--------------------------------------------------------------------------- +// +// Selection phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Selection() +{ + DWORD id; + + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::selection) { + // Invalid if IDs do not match + id = 1 << ctrl.id; + if ((ctrl.bus->GetDAT() & id) == 0) { + return; + } + + // Return if there is no unit + if (!HasUnit()) { + return; + } + +#if defined(DISK_LOG) + Log(Log::Normal, + "Selection Phase ID=%d (with device)", ctrl.id); +#endif // DISK_LOG + + // Phase change + ctrl.phase = BUS::selection; + + // Raiase BSY and respond + ctrl.bus->SetBSY(TRUE); + return; + } + + // Command phase shifts when selection is completed + if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) { + Command(); + } +} + +//--------------------------------------------------------------------------- +// +// Command phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Command() +{ +#ifdef RASCSI + int count; + int i; +#endif // RASCSI + + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::command) { + +#if defined(DISK_LOG) + Log(Log::Normal, "Command Phase"); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::command; + + // Signal line operated by the target + ctrl.bus->SetMSG(FALSE); + ctrl.bus->SetCD(TRUE); + ctrl.bus->SetIO(FALSE); + + // Data transfer is 6 bytes x 1 block + ctrl.offset = 0; + ctrl.length = 6; + ctrl.blocks = 1; + +#ifdef RASCSI + // Command reception handshake (10 bytes are automatically received at the first command) + count = ctrl.bus->CommandHandShake(ctrl.buffer); + + // If no byte can be received move to the status phase + if (count == 0) { + Error(); + return; + } + + // Check 10-byte CDB + if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) { + ctrl.length = 10; + } + + // If not able to receive all, move to the status phase + if (count != (int)ctrl.length) { + Error(); + return; + } + + // Command data transfer + for (i = 0; i < (int)ctrl.length; i++) { + ctrl.cmd[i] = (DWORD)ctrl.buffer[i]; + } + + // Clear length and block + ctrl.length = 0; + ctrl.blocks = 0; + + // Execution Phase + Execute(); +#else + // Request the command + ctrl.bus->SetREQ(TRUE); + return; +#endif // RASCSI + } +#ifndef RASCSI + // Requesting + if (ctrl.bus->GetREQ()) { + // Sent by the initiator + if (ctrl.bus->GetACK()) { + Receive(); + } + } else { + // Request the initator to + if (!ctrl.bus->GetACK()) { + ReceiveNext(); + } + } +#endif // RASCSI +} + +//--------------------------------------------------------------------------- +// +// Execution Phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Execute() +{ + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "Execution Phase Command %02X", ctrl.cmd[0]); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::execute; + + // Initialization for data transfer + ctrl.offset = 0; + ctrl.blocks = 1; +#ifdef RASCSI + ctrl.execstart = SysTimer::GetTimerLow(); +#endif // RASCSI + + // Process by command + switch (ctrl.cmd[0]) { + // TEST UNIT READY + case 0x00: + CmdTestUnitReady(); + return; + + // REZERO UNIT + case 0x01: + CmdRezero(); + return; + + // REQUEST SENSE + case 0x03: + CmdRequestSense(); + return; + + // FORMAT UNIT + case 0x04: + CmdFormat(); + return; + + // FORMAT UNIT + case 0x06: + CmdFormat(); + return; + + // REASSIGN BLOCKS + case 0x07: + CmdReassign(); + return; + + // READ(6) + case 0x08: + CmdRead6(); + return; + + // WRITE(6) + case 0x0a: + CmdWrite6(); + return; + + // SEEK(6) + case 0x0b: + CmdSeek6(); + return; + + // ASSIGN(SASIのみ) + case 0x0e: + CmdAssign(); + return; + + // SPECIFY(SASIのみ) + case 0xc2: + CmdSpecify(); + return; + } + + // Unsupported command + Log(Log::Warning, "Unsupported command $%02X", ctrl.cmd[0]); + CmdInvalid(); +} + +//--------------------------------------------------------------------------- +// +// Status phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Status() +{ +#ifdef RASCSI + DWORD min_exec_time; + DWORD time; +#endif // RASCSI + + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::status) { + +#ifdef RASCSI + // Minimum execution time + if (ctrl.execstart > 0) { + min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; + time = SysTimer::GetTimerLow() - ctrl.execstart; + if (time < min_exec_time) { + SysTimer::SleepUsec(min_exec_time - time); + } + ctrl.execstart = 0; + } else { + SysTimer::SleepUsec(5); + } +#endif // RASCSI + +#if defined(DISK_LOG) + Log(Log::Normal, "Status phase"); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::status; + + // Signal line operated by the target + ctrl.bus->SetMSG(FALSE); + ctrl.bus->SetCD(TRUE); + ctrl.bus->SetIO(TRUE); + + // Data transfer is 1 byte x 1 block + ctrl.offset = 0; + ctrl.length = 1; + ctrl.blocks = 1; + ctrl.buffer[0] = (BYTE)ctrl.status; + +#ifndef RASCSI + // Request status + ctrl.bus->SetDAT(ctrl.buffer[0]); + ctrl.bus->SetREQ(TRUE); + +#if defined(DISK_LOG) + Log(Log::Normal, "Status Phase $%02X", ctrl.status); +#endif // DISK_LOG +#endif // RASCSI + return; + } + +#ifdef RASCSI + // Send + Send(); +#else + // Requesting + if (ctrl.bus->GetREQ()) { + // Initiator received + if (ctrl.bus->GetACK()) { + SendNext(); + } + } else { + // Initiator requests next + if (!ctrl.bus->GetACK()) { + Send(); + } + } +#endif // RASCSI +} + +//--------------------------------------------------------------------------- +// +// Message in phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::MsgIn() +{ + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::msgin) { + +#if defined(DISK_LOG) + Log(Log::Normal, "Message in phase"); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::msgin; + + // Signal line operated by the target + ctrl.bus->SetMSG(TRUE); + ctrl.bus->SetCD(TRUE); + ctrl.bus->SetIO(TRUE); + + // length, blocks are already set + ASSERT(ctrl.length > 0); + ASSERT(ctrl.blocks > 0); + ctrl.offset = 0; + +#ifndef RASCSI + // Request message + ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); + ctrl.bus->SetREQ(TRUE); + +#if defined(DISK_LOG) + Log(Log::Normal, "Message in phase $%02X", ctrl.buffer[ctrl.offset]); +#endif // DISK_LOG +#endif // RASCSI + return; + } + +#ifdef RASCSI + //Send + Send(); +#else + // Requesting + if (ctrl.bus->GetREQ()) { + // Initator received + if (ctrl.bus->GetACK()) { + SendNext(); + } + } else { + // Initiator requests next + if (!ctrl.bus->GetACK()) { + Send(); + } + } +#endif // RASCSI +} + +//--------------------------------------------------------------------------- +// +// Data-in Phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::DataIn() +{ +#ifdef RASCSI + DWORD min_exec_time; + DWORD time; +#endif // RASCSI + + ASSERT(this); + ASSERT(ctrl.length >= 0); + + // Phase change + if (ctrl.phase != BUS::datain) { + +#ifdef RASCSI + // Minimum execution time + if (ctrl.execstart > 0) { + min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; + time = SysTimer::GetTimerLow() - ctrl.execstart; + if (time < min_exec_time) { + SysTimer::SleepUsec(min_exec_time - time); + } + ctrl.execstart = 0; + } +#endif // RASCSI + + // If the length is 0, go to the status phase + if (ctrl.length == 0) { + Status(); + return; + } + +#if defined(DISK_LOG) + Log(Log::Normal, "Data-in Phase"); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::datain; + + // Signal line operated by the target + ctrl.bus->SetMSG(FALSE); + ctrl.bus->SetCD(FALSE); + ctrl.bus->SetIO(TRUE); + + // length, blocks are already set + ASSERT(ctrl.length > 0); + ASSERT(ctrl.blocks > 0); + ctrl.offset = 0; + +#ifndef RASCSI + // Assert the DAT signal + ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); + + // Request data + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + +#ifdef RASCSI + // Send + Send(); +#else + // Requesting + if (ctrl.bus->GetREQ()) { + // Initator received + if (ctrl.bus->GetACK()) { + SendNext(); + } + } else { + // Initiator requests next + if (!ctrl.bus->GetACK()) { + Send(); + } + } +#endif // RASCSI +} + +//--------------------------------------------------------------------------- +// +// Data out phase +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::DataOut() +{ +#ifdef RASCSI + DWORD min_exec_time; + DWORD time; +#endif // RASCSI + + ASSERT(this); + ASSERT(ctrl.length >= 0); + + // Phase change + if (ctrl.phase != BUS::dataout) { + +#ifdef RASCSI + // Minimum execution time + if (ctrl.execstart > 0) { + min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; + time = SysTimer::GetTimerLow() - ctrl.execstart; + if (time < min_exec_time) { + SysTimer::SleepUsec(min_exec_time - time); + } + ctrl.execstart = 0; + } +#endif // RASCSI + + // If the length is 0, go to the status phase + if (ctrl.length == 0) { + Status(); + return; + } + +#if defined(DISK_LOG) + Log(Log::Normal, "Data out phase"); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::dataout; + + // Signal line operated by the target + ctrl.bus->SetMSG(FALSE); + ctrl.bus->SetCD(FALSE); + ctrl.bus->SetIO(FALSE); + + // length, blocks are already calculated + ASSERT(ctrl.length > 0); + ASSERT(ctrl.blocks > 0); + ctrl.offset = 0; + +#ifndef RASCSI + // Request data + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + +#ifdef RASCSI + // Receive + Receive(); +#else + // Requesting + if (ctrl.bus->GetREQ()) { + // Sent by the initiator + if (ctrl.bus->GetACK()) { + Receive(); + } + } else { + // Request the initator to + if (!ctrl.bus->GetACK()) { + ReceiveNext(); + } + } +#endif // RASCSI +} + +//--------------------------------------------------------------------------- +// +// Error +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Error() +{ + DWORD lun; + + ASSERT(this); + + // Get bus information + ctrl.bus->Aquire(); + + // Reset check + if (ctrl.bus->GetRST()) { + // Reset the controller + Reset(); + + // Reset the bus + ctrl.bus->Reset(); + return; + } + + // Bus free for status phase and message in phase + if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) { + BusFree(); + return; + } + +#if defined(DISK_LOG) + Log(Log::Warning, "Error occured (going to status phase)"); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + + // Set status and message(CHECK CONDITION) + ctrl.status = (lun << 5) | 0x02; + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// TEST UNIT READY +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdTestUnitReady() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "TEST UNIT READY Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->TestUnitReady(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// REZERO UNIT +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdRezero() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "REZERO UNIT Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Rezero(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// REQUEST SENSE +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdRequestSense() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "REQUEST SENSE Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer); + ASSERT(ctrl.length > 0); + +#if defined(DISK_LOG) + Log(Log::Normal, "Sense key $%02X", ctrl.buffer[2]); +#endif // DISK_LOG + + // Read phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// FORMAT UNIT +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdFormat() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "FORMAT UNIT Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Format(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// REASSIGN BLOCKS +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdReassign() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "REASSIGN BLOCKS Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Reassign(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// READ(6) +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdRead6() +{ + DWORD lun; + DWORD record; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Get record number and block number + record = ctrl.cmd[1] & 0x1f; + record <<= 8; + record |= ctrl.cmd[2]; + record <<= 8; + record |= ctrl.cmd[3]; + ctrl.blocks = ctrl.cmd[4]; + if (ctrl.blocks == 0) { + ctrl.blocks = 0x100; + } + +#if defined(DISK_LOG) + Log(Log::Normal, + "READ(6) command record=%06X blocks=%d", record, ctrl.blocks); +#endif // DISK_LOG + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.next = record + 1; + + // Read phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// WRITE(6) +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdWrite6() +{ + DWORD lun; + DWORD record; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Get record number and block number + record = ctrl.cmd[1] & 0x1f; + record <<= 8; + record |= ctrl.cmd[2]; + record <<= 8; + record |= ctrl.cmd[3]; + ctrl.blocks = ctrl.cmd[4]; + if (ctrl.blocks == 0) { + ctrl.blocks = 0x100; + } + +#if defined(DISK_LOG) + Log(Log::Normal, + "WRITE(6) command record=%06X blocks=%d", record, ctrl.blocks); +#endif // DISK_LOG + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->WriteCheck(record); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.next = record + 1; + + // Write phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// SEEK(6) +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdSeek6() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "SEEK(6) Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Seek(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// ASSIGN +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdAssign() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "ASSIGN Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Assign(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // Request 4 bytes of data + ctrl.length = 4; + + // Write phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// SPECIFY +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdSpecify() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "SPECIFY Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Assign(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // Request 10 bytes of data + ctrl.length = 10; + + // Write phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// Unsupported command +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::CmdInvalid() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "Command not supported"); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (ctrl.unit[lun]) { + // Command processing on drive + ctrl.unit[lun]->InvalidCmd(); + } + + // Failure (Error) + Error(); +} + +//=========================================================================== +// +// Data transfer +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Data transmission +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Send() +{ +#ifdef RASCSI + int len; +#endif // RASCSI + BOOL result; + + ASSERT(this); + ASSERT(!ctrl.bus->GetREQ()); + ASSERT(ctrl.bus->GetIO()); + +#ifdef RASCSI + // Check that the length isn't 0 + if (ctrl.length != 0) { + len = ctrl.bus->SendHandShake( + &ctrl.buffer[ctrl.offset], ctrl.length); + + // If you can not send it all, move on to the status phase + if (len != (int)ctrl.length) { + Error(); + return; + } + + // Offset and Length + ctrl.offset += ctrl.length; + ctrl.length = 0; + return; + } +#else + // Offset and Length + ASSERT(ctrl.length >= 1); + ctrl.offset++; + ctrl.length--; + + // Immediately after ACK is asserted, if the data + // has been set by SendNext, raise the request + if (ctrl.length != 0) { + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); + return; + } +#endif // RASCSI + + // Remove block and initialize the result + ctrl.blocks--; + result = TRUE; + + // Process after data collection (read/data-in only) + if (ctrl.phase == BUS::datain) { + if (ctrl.blocks != 0) { + // Set next buffer (set offset, length) + result = XferIn(ctrl.buffer); + //** printf("xfer in: %d \n",result); + +#ifndef RASCSI + ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); +#endif // RASCSI + } + } + + // If result FALSE, move to the status phase + if (!result) { + Error(); + return; + } + + // Continue sending if block != 0 + if (ctrl.blocks != 0){ + ASSERT(ctrl.length > 0); + ASSERT(ctrl.offset == 0); +#ifndef RASCSI + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + + // Move to the next phase + switch (ctrl.phase) { + // Message in phase + case BUS::msgin: + // Bus free phase + BusFree(); + break; + + // Data-in Phase + case BUS::datain: + // status phase + Status(); + break; + + // Status phase + case BUS::status: + // Message in phase + ctrl.length = 1; + ctrl.blocks = 1; + ctrl.buffer[0] = (BYTE)ctrl.message; + MsgIn(); + break; + + // Other (impossible) + default: + ASSERT(FALSE); + break; + } +} + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Continue sending data +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::SendNext() +{ + ASSERT(this); + + // Req is up + ASSERT(ctrl.bus->GetREQ()); + ASSERT(ctrl.bus->GetIO()); + + // Signal line operated by the target + ctrl.bus->SetREQ(FALSE); + + // If there is data in the buffer, set it first. + if (ctrl.length > 1) { + ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset + 1]); + } +} +#endif // RASCSI + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Receive data +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Receive() +{ + DWORD data; + + ASSERT(this); + + // Req is up + ASSERT(ctrl.bus->GetREQ()); + ASSERT(!ctrl.bus->GetIO()); + + // Get data + data = (DWORD)ctrl.bus->GetDAT(); + + // Signal line operated by the target + ctrl.bus->SetREQ(FALSE); + + switch (ctrl.phase) { + // Command phase + case BUS::command: + ctrl.cmd[ctrl.offset] = data; +#if defined(DISK_LOG) + Log(Log::Normal, "Command phase $%02X", data); +#endif // DISK_LOG + + // Set the length again with the first data (offset 0) + if (ctrl.offset == 0) { + if (ctrl.cmd[0] >= 0x20 && ctrl.cmd[0] <= 0x7D) { + // 10 byte CDB + ctrl.length = 10; + } + } + break; + + // Data out phase + case BUS::dataout: + ctrl.buffer[ctrl.offset] = (BYTE)data; + break; + + // Other (impossible) + default: + ASSERT(FALSE); + break; + } +} +#endif // RASCSI + +#ifdef RASCSI +//--------------------------------------------------------------------------- +// +// Receive data +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Receive() +#else +//--------------------------------------------------------------------------- +// +// Continue receiving data +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::ReceiveNext() +#endif // RASCSI +{ +#ifdef RASCSI + int len; +#endif // RASCSI + BOOL result; + + ASSERT(this); + + // REQ is low + ASSERT(!ctrl.bus->GetREQ()); + ASSERT(!ctrl.bus->GetIO()); + +#ifdef RASCSI + // Length != 0 if received + if (ctrl.length != 0) { + // Receive + len = ctrl.bus->ReceiveHandShake( + &ctrl.buffer[ctrl.offset], ctrl.length); + + // If not able to receive all, move to status phase + if (len != (int)ctrl.length) { + Error(); + return; + } + + // Offset and Length + ctrl.offset += ctrl.length; + ctrl.length = 0; + return; + } +#else + // Offset and Length + ASSERT(ctrl.length >= 1); + ctrl.offset++; + ctrl.length--; + + // If length != 0, set req again + if (ctrl.length != 0) { + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); + return; + } +#endif // RASCSI + + // Remove the control block and initialize the result + ctrl.blocks--; + result = TRUE; + + // Process the data out phase + if (ctrl.phase == BUS::dataout) { + if (ctrl.blocks == 0) { + // End with this buffer + result = XferOut(FALSE); + } else { + // Continue to next buffer (set offset, length) + result = XferOut(TRUE); + } + } + + // If result is false, move to the status phase + if (!result) { + Error(); + return; + } + + // Continue to receive is block != 0 + if (ctrl.blocks != 0){ + ASSERT(ctrl.length > 0); + ASSERT(ctrl.offset == 0); +#ifndef RASCSI + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + + // Move to the next phase + switch (ctrl.phase) { +#ifndef RASCSI + // Command phase + case BUS::command: + // Execution Phase + Execute(); + break; +#endif // RASCSI + + // Data out phase + case BUS::dataout: + // Flush + FlushUnit(); + + // status phase + Status(); + break; + + // Other (impossible) + default: + ASSERT(FALSE); + break; + } +} + +//--------------------------------------------------------------------------- +// +// Data transfer IN +// *Reset offset and length +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SASIDEV::XferIn(BYTE *buf) +{ + DWORD lun; + + ASSERT(this); + ASSERT(ctrl.phase == BUS::datain); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + return FALSE; + } + + // Limited to read commands + switch (ctrl.cmd[0]) { + // READ(6) + case 0x08: + // READ(10) + case 0x28: + // Read from disk + ctrl.length = ctrl.unit[lun]->Read(buf, ctrl.next); + ctrl.next++; + + // If there is an error, go to the status phase + if (ctrl.length <= 0) { + // Cancel data-in + return FALSE; + } + + // If things are normal, work setting + ctrl.offset = 0; + break; + + // Other (impossible) + default: + ASSERT(FALSE); + return FALSE; + } + + // Succeeded in setting the buffer + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Data transfer OUT +// *If cont=true, reset the offset and length +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SASIDEV::XferOut(BOOL cont) +{ + DWORD lun; + SCSIBR *bridge; + + ASSERT(this); + ASSERT(ctrl.phase == BUS::dataout); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + return FALSE; + } + + // MODE SELECT or WRITE system + switch (ctrl.cmd[0]) { + // MODE SELECT + case 0x15: + // MODE SELECT(10) + case 0x55: + if (!ctrl.unit[lun]->ModeSelect( + ctrl.cmd, ctrl.buffer, ctrl.offset)) { + // MODE SELECT failed + return FALSE; + } + break; + + // WRITE(6) + case 0x0a: + // WRITE(10) + case 0x2a: + // Replace the host bridge with SEND MESSAGE 10 + if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { + bridge = (SCSIBR*)ctrl.unit[lun]; + if (!bridge->SendMessage10(ctrl.cmd, ctrl.buffer)) { + // write failed + return FALSE; + } + + // If normal, work setting + ctrl.offset = 0; + break; + } + + // WRITE AND VERIFY + case 0x2e: + // Write + if (!ctrl.unit[lun]->Write(ctrl.buffer, ctrl.next - 1)) { + // Write failed + return FALSE; + } + + // If you do not need the next block, end here + ctrl.next++; + if (!cont) { + break; + } + + // Check the next block + ctrl.length = ctrl.unit[lun]->WriteCheck(ctrl.next - 1); + if (ctrl.length <= 0) { + // Cannot write + return FALSE; + } + + // If normal, work setting + ctrl.offset = 0; + break; + + // SPECIFY(SASI only) + case 0xc2: + break; + + default: + ASSERT(FALSE); + break; + } + + // Buffer saved successfully + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Logical unit flush +// +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::FlushUnit() +{ + DWORD lun; + + ASSERT(this); + ASSERT(ctrl.phase == BUS::dataout); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + return; + } + + // WRITE system only + switch (ctrl.cmd[0]) { + // WRITE(6) + case 0x0a: + // WRITE(10) + case 0x2a: + // WRITE AND VERIFY + case 0x2e: + // Flush + if (!ctrl.unit[lun]->IsCacheWB()) { + ctrl.unit[lun]->Flush(); + } + break; + // Mode Select (6) + case 0x15: + // MODE SELECT(10) + case 0x55: + // Debug code related to Issue #2 on github, where we get an unhandled Model select when + // the mac is rebooted + // https://github.com/akuker/RASCSI/issues/2 + Log(Log::Warning, "Received \'Mode Select\'\n"); + Log(Log::Warning, " Operation Code: [%02X]\n", ctrl.cmd[0]); + Log(Log::Warning, " Logical Unit %01X, PF %01X, SP %01X [%02X]\n", ctrl.cmd[1] >> 5, 1 & (ctrl.cmd[1] >> 4), ctrl.cmd[1] & 1, ctrl.cmd[1]); + Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[2]); + Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[3]); + Log(Log::Warning, " Parameter List Len %02X\n", ctrl.cmd[4]); + Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[5]); + Log(Log::Warning, " Ctrl Len: %08X\n",ctrl.length); + + if (!ctrl.unit[lun]->ModeSelect( + ctrl.cmd, ctrl.buffer, ctrl.offset)) { + // MODE SELECT failed + Log(Log::Warning, "Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]); + return; + } + break; + + default: + Log(Log::Warning, "Received an invalid flush command %02X!!!!!\n",ctrl.cmd[0]); + ASSERT(FALSE); + break; + } +} + +#ifdef DISK_LOG +//--------------------------------------------------------------------------- +// +// Get the current phase as a string +// +//--------------------------------------------------------------------------- +void SASIDEV::GetPhaseStr(char *str) +{ + switch(this->GetPhase()) + { + case BUS::busfree: + strcpy(str,"busfree "); + break; + case BUS::arbitration: + strcpy(str,"arbitration"); + break; + case BUS::selection: + strcpy(str,"selection "); + break; + case BUS::reselection: + strcpy(str,"reselection"); + break; + case BUS::command: + strcpy(str,"command "); + break; + case BUS::execute: + strcpy(str,"execute "); + break; + case BUS::datain: + strcpy(str,"datain "); + break; + case BUS::dataout: + strcpy(str,"dataout "); + break; + case BUS::status: + strcpy(str,"status "); + break; + case BUS::msgin: + strcpy(str,"msgin "); + break; + case BUS::msgout: + strcpy(str,"msgout "); + break; + case BUS::reserved: + strcpy(str,"reserved "); + break; + } +} +#endif + +//--------------------------------------------------------------------------- +// +// Log output +// +// TODO: This function needs some cleanup. Its very kludgey +//--------------------------------------------------------------------------- +void FASTCALL SASIDEV::Log(Log::loglevel level, const char *format, ...) +{ +#if !defined(BAREMETAL) +#ifdef DISK_LOG + char buffer[0x200]; + char buffer2[0x250]; + char buffer3[0x250]; + char phase_str[20]; +#endif + va_list args; + va_start(args, format); + + if(this->GetID() != 6) + { + return; + } + +#ifdef RASCSI +#ifndef DISK_LOG + if (level == Log::Warning) { + return; + } +#endif // DISK_LOG +#endif // RASCSI + +#ifdef DISK_LOG + // format + vsprintf(buffer, format, args); + + // end variable length argument + va_end(args); + + // Add the date/timestamp + // current date/time based on current system + time_t now = time(0); + // convert now to string form + char* dt = ctime(&now); + + + strcpy(buffer2, "["); + strcat(buffer2, dt); + // Get rid of the carriage return + buffer2[strlen(buffer2)-1] = '\0'; + strcat(buffer2, "] "); + + // Get the phase + this->GetPhaseStr(phase_str); + sprintf(buffer3, "[%d][%s] ", this->GetID(), phase_str); + strcat(buffer2,buffer3); + strcat(buffer2, buffer); + + + // Log output +#ifdef RASCSI + printf("%s\n", buffer2); +#else + host->GetVM()->GetLog()->Format(level, host, buffer); +#endif // RASCSI +#endif // BAREMETAL +#endif // DISK_LOG +} diff --git a/src/raspberrypi/controllers/sasidev_ctrl.h b/src/raspberrypi/controllers/sasidev_ctrl.h new file mode 100644 index 00000000..fc8ec2d0 --- /dev/null +++ b/src/raspberrypi/controllers/sasidev_ctrl.h @@ -0,0 +1,211 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SASI device controller ] +// +//--------------------------------------------------------------------------- +#pragma once + +#include "os.h" +#include "scsi.h" +#include "fileio.h" +#include "devices/disk.h" +#include "log.h" +#include "xm6.h" + + +//=========================================================================== +// +// SASI Controller +// +//=========================================================================== +class SASIDEV +{ +public: + // Maximum number of logical units + enum { + UnitMax = 8 + }; + +#ifdef RASCSI + // For timing adjustments + enum { + min_exec_time_sasi = 100, // SASI BOOT/FORMAT 30:NG 35:OK + min_exec_time_scsi = 50 + }; +#endif // RASCSI + + // Internal data definition + typedef struct { + // 全般 + BUS::phase_t phase; // Transition phase + int id; // Controller ID (0-7) + BUS *bus; // Bus + + // commands + DWORD cmd[10]; // Command data + DWORD status; // Status data + DWORD message; // Message data + +#ifdef RASCSI + // Run + DWORD execstart; // Execution start time +#endif // RASCSI + + // Transfer + BYTE *buffer; // Transfer data buffer + int bufsize; // Transfer data buffer size + DWORD blocks; // Number of transfer block + DWORD next; // Next record + DWORD offset; // Transfer offset + DWORD length; // Transfer remaining length + + // Logical unit + Disk *unit[UnitMax]; + // Logical Unit + } ctrl_t; + +public: + // Basic Functions +#ifdef RASCSI + SASIDEV(); +#else + SASIDEV(Device *dev); +#endif //RASCSI + + // Constructor + virtual ~SASIDEV(); + // Destructor + virtual void FASTCALL Reset(); + // Device Reset +#ifndef RASCSI + virtual BOOL FASTCALL Save(Fileio *fio, int ver); + // Save + virtual BOOL FASTCALL Load(Fileio *fio, int ver); + // Load +#endif //RASCSI + + // External API + virtual BUS::phase_t FASTCALL Process(); + // Run + + // Connect + void FASTCALL Connect(int id, BUS *sbus); + // Controller connection + Disk* FASTCALL GetUnit(int no); + // Get logical unit + void FASTCALL SetUnit(int no, Disk *dev); + // Logical unit setting + BOOL FASTCALL HasUnit(); + // Has a valid logical unit + + // Other + BUS::phase_t FASTCALL GetPhase() {return ctrl.phase;} + // Get the phase +#ifdef DISK_LOG + // Function to get the current phase as a String. + void FASTCALL GetPhaseStr(char *str); +#endif + + int FASTCALL GetID() {return ctrl.id;} + // Get the ID + void FASTCALL GetCTRL(ctrl_t *buffer); + // Get the internal information + ctrl_t* FASTCALL GetWorkAddr() { return &ctrl; } + // Get the internal information address + virtual BOOL FASTCALL IsSASI() const {return TRUE;} + // SASI Check + virtual BOOL FASTCALL IsSCSI() const {return FALSE;} + // SCSI check + Disk* FASTCALL GetBusyUnit(); + // Get the busy unit + +protected: + // Phase processing + virtual void FASTCALL BusFree(); + // Bus free phase + virtual void FASTCALL Selection(); + // Selection phase + virtual void FASTCALL Command(); + // Command phase + virtual void FASTCALL Execute(); + // Execution phase + void FASTCALL Status(); + // Status phase + void FASTCALL MsgIn(); + // Message in phase + void FASTCALL DataIn(); + // Data in phase + void FASTCALL DataOut(); + // Data out phase + virtual void FASTCALL Error(); + // Common error handling + + // commands + void FASTCALL CmdTestUnitReady(); + // TEST UNIT READY command + void FASTCALL CmdRezero(); + // REZERO UNIT command + void FASTCALL CmdRequestSense(); + // REQUEST SENSE command + void FASTCALL CmdFormat(); + // FORMAT command + void FASTCALL CmdReassign(); + // REASSIGN BLOCKS command + void FASTCALL CmdRead6(); + // READ(6) command + void FASTCALL CmdWrite6(); + // WRITE(6) command + void FASTCALL CmdSeek6(); + // SEEK(6) command + void FASTCALL CmdAssign(); + // ASSIGN command + void FASTCALL CmdSpecify(); + // SPECIFY command + void FASTCALL CmdInvalid(); + // Unsupported command + + // データ転送 + virtual void FASTCALL Send(); + // Send data +#ifndef RASCSI + virtual void FASTCALL SendNext(); + // Continue sending data +#endif // RASCSI + virtual void FASTCALL Receive(); + // Receive data +#ifndef RASCSI + virtual void FASTCALL ReceiveNext(); + // Continue receiving data +#endif // RASCSI + BOOL FASTCALL XferIn(BYTE* buf); + // Data transfer IN + BOOL FASTCALL XferOut(BOOL cont); + // Data transfer OUT + + // Special operations + void FASTCALL FlushUnit(); + // Flush the logical unit + + // Log + void FASTCALL Log(Log::loglevel level, const char *format, ...); + // Log output + +protected: +#ifndef RASCSI + Device *host; + // Host device +#endif // RASCSI + + ctrl_t ctrl; + // Internal data +}; diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp new file mode 100644 index 00000000..c6813c67 --- /dev/null +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -0,0 +1,1869 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI device controller ] +// +//--------------------------------------------------------------------------- +#include "controllers/scsidev_ctrl.h" +#include "gpiobus.h" +#include "devices/scsi_host_bridge.h" + +//=========================================================================== +// +// SCSI Device +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +#ifdef RASCSI +SCSIDEV::SCSIDEV() : SASIDEV() +#else +SCSIDEV::SCSIDEV(Device *dev) : SASIDEV(dev) +#endif +{ + // Synchronous transfer work initialization + scsi.syncenable = FALSE; + scsi.syncperiod = 50; + scsi.syncoffset = 0; + scsi.atnmsg = FALSE; + scsi.msc = 0; + memset(scsi.msb, 0x00, sizeof(scsi.msb)); +} + +//--------------------------------------------------------------------------- +// +// Device reset +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Reset() +{ + ASSERT(this); + + // Work initialization + scsi.atnmsg = FALSE; + scsi.msc = 0; + memset(scsi.msb, 0x00, sizeof(scsi.msb)); + + // Base class + SASIDEV::Reset(); +} + +//--------------------------------------------------------------------------- +// +// Process +// +//--------------------------------------------------------------------------- +BUS::phase_t FASTCALL SCSIDEV::Process() +{ + ASSERT(this); + + // Do nothing if not connected + if (ctrl.id < 0 || ctrl.bus == NULL) { + return ctrl.phase; + } + + // Get bus information + ctrl.bus->Aquire(); + + // Reset + if (ctrl.bus->GetRST()) { +#if defined(DISK_LOG) + Log(Log::Normal, "RESET信号受信"); +#endif // DISK_LOG + + // Reset the controller + Reset(); + + // Reset the bus + ctrl.bus->Reset(); + return ctrl.phase; + } + + // Phase processing + switch (ctrl.phase) { + // Bus free phase + case BUS::busfree: + BusFree(); + break; + + // Selection phase + case BUS::selection: + Selection(); + break; + + // Data out (MCI=000) + case BUS::dataout: + DataOut(); + break; + + // Data in (MCI=001) + case BUS::datain: + DataIn(); + break; + + // Command (MCI=010) + case BUS::command: + Command(); + break; + + // Status (MCI=011) + case BUS::status: + Status(); + break; + + // Message out (MCI=110) + case BUS::msgout: + MsgOut(); + break; + + // Message in (MCI=111) + case BUS::msgin: + MsgIn(); + break; + + // Other + default: + ASSERT(FALSE); + break; + } + + return ctrl.phase; +} + +//--------------------------------------------------------------------------- +// +// Phaes +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Bus free phase +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::BusFree() +{ + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::busfree) { + +#if defined(DISK_LOG) + Log(Log::Normal, "Bus free phase"); +#endif // DISK_LOG + + // Phase setting + ctrl.phase = BUS::busfree; + + // Signal line + ctrl.bus->SetREQ(FALSE); + ctrl.bus->SetMSG(FALSE); + ctrl.bus->SetCD(FALSE); + ctrl.bus->SetIO(FALSE); + ctrl.bus->SetBSY(FALSE); + + // Initialize status and message + ctrl.status = 0x00; + ctrl.message = 0x00; + + // Initialize ATN message reception status + scsi.atnmsg = FALSE; + return; + } + + // Move to selection phase + if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) { + Selection(); + } +} + +//--------------------------------------------------------------------------- +// +// Selection Phase +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Selection() +{ + DWORD id; + + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::selection) { + // invalid if IDs do not match + id = 1 << ctrl.id; + if ((ctrl.bus->GetDAT() & id) == 0) { + return; + } + + // End if there is no valid unit + if (!HasUnit()) { + return; + } + +#if defined(DISK_LOG) + Log(Log::Normal, + "Selection Phase ID=%d (with device)", ctrl.id); +#endif // DISK_LOG + + // Phase setting + ctrl.phase = BUS::selection; + + // Raise BSY and respond + ctrl.bus->SetBSY(TRUE); + return; + } + + // Selection completed + if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) { + // Message out phase if ATN=1, otherwise command phase + if (ctrl.bus->GetATN()) { + MsgOut(); + } else { + Command(); + } + } +} + +//--------------------------------------------------------------------------- +// +// Execution Phase +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Execute() +{ + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "Execution phase command $%02X", ctrl.cmd[0]); +#endif // DISK_LOG + + // Phase Setting + ctrl.phase = BUS::execute; + + // Initialization for data transfer + ctrl.offset = 0; + ctrl.blocks = 1; +#ifdef RASCSI + ctrl.execstart = SysTimer::GetTimerLow(); +#endif // RASCSI + + // Process by command + switch (ctrl.cmd[0]) { + // TEST UNIT READY + case 0x00: + CmdTestUnitReady(); + return; + + // REZERO + case 0x01: + CmdRezero(); + return; + + // REQUEST SENSE + case 0x03: + CmdRequestSense(); + return; + + // FORMAT UNIT + case 0x04: + CmdFormat(); + return; + + // REASSIGN BLOCKS + case 0x07: + CmdReassign(); + return; + + // READ(6) + case 0x08: + CmdRead6(); + return; + + // WRITE(6) + case 0x0a: + CmdWrite6(); + return; + + // SEEK(6) + case 0x0b: + CmdSeek6(); + return; + + // INQUIRY + case 0x12: + CmdInquiry(); + return; + + // MODE SELECT + case 0x15: + CmdModeSelect(); + return; + + // MDOE SENSE + case 0x1a: + CmdModeSense(); + return; + + // START STOP UNIT + case 0x1b: + CmdStartStop(); + return; + + // SEND DIAGNOSTIC + case 0x1d: + CmdSendDiag(); + return; + + // PREVENT/ALLOW MEDIUM REMOVAL + case 0x1e: + CmdRemoval(); + return; + + // READ CAPACITY + case 0x25: + CmdReadCapacity(); + return; + + // READ(10) + case 0x28: + CmdRead10(); + return; + + // WRITE(10) + case 0x2a: + CmdWrite10(); + return; + + // SEEK(10) + case 0x2b: + CmdSeek10(); + return; + + // WRITE and VERIFY + case 0x2e: + CmdWrite10(); + return; + + // VERIFY + case 0x2f: + CmdVerify(); + return; + + // SYNCHRONIZE CACHE + case 0x35: + CmdSynchronizeCache(); + return; + + // READ DEFECT DATA(10) + case 0x37: + CmdReadDefectData10(); + return; + + // READ TOC + case 0x43: + CmdReadToc(); + return; + + // PLAY AUDIO(10) + case 0x45: + CmdPlayAudio10(); + return; + + // PLAY AUDIO MSF + case 0x47: + CmdPlayAudioMSF(); + return; + + // PLAY AUDIO TRACK + case 0x48: + CmdPlayAudioTrack(); + return; + + // MODE SELECT(10) + case 0x55: + CmdModeSelect10(); + return; + + // MDOE SENSE(10) + case 0x5a: + CmdModeSense10(); + return; + + // SPECIFY (SASI only/Suppress warning when using SxSI) + case 0xc2: + CmdInvalid(); + return; + + } + + // No other support + Log(Log::Normal, "Unsupported command received: $%02X", ctrl.cmd[0]); + CmdInvalid(); +} + +//--------------------------------------------------------------------------- +// +// Message out phase +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::MsgOut() +{ + ASSERT(this); + + // Phase change + if (ctrl.phase != BUS::msgout) { + +#if defined(DISK_LOG) + Log(Log::Normal, "Message Out Phase"); +#endif // DISK_LOG + + // Message out phase after selection + // process the IDENTIFY message + if (ctrl.phase == BUS::selection) { + scsi.atnmsg = TRUE; + scsi.msc = 0; + memset(scsi.msb, 0x00, sizeof(scsi.msb)); + } + + // Phase Setting + ctrl.phase = BUS::msgout; + + // Signal line operated by the target + ctrl.bus->SetMSG(TRUE); + ctrl.bus->SetCD(TRUE); + ctrl.bus->SetIO(FALSE); + + // Data transfer is 1 byte x 1 block + ctrl.offset = 0; + ctrl.length = 1; + ctrl.blocks = 1; + +#ifndef RASCSI + // Request message + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + +#ifdef RASCSI + // Receive + Receive(); +#else + // Requesting + if (ctrl.bus->GetREQ()) { + // Sent by the initiator + if (ctrl.bus->GetACK()) { + Receive(); + } + } else { + // Request the initator to + if (!ctrl.bus->GetACK()) { + ReceiveNext(); + } + } +#endif // RASCSI +} + +//--------------------------------------------------------------------------- +// +// Common Error Handling +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Error() +{ + ASSERT(this); + + // Get bus information + ctrl.bus->Aquire(); + + // Reset check + if (ctrl.bus->GetRST()) { + // Reset the controller + Reset(); + + // Reset the bus + ctrl.bus->Reset(); + return; + } + + // Bus free for status phase and message in phase + if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) { + BusFree(); + return; + } + +#if defined(DISK_LOG) + Log(Log::Normal, "Error (to status phase)"); +#endif // DISK_LOG + + // Set status and message(CHECK CONDITION) + ctrl.status = 0x02; + ctrl.message = 0x00; + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// Command +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdInquiry() +{ + Disk *disk; + int lun; + DWORD major; + DWORD minor; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "INQUIRY Command"); +#endif // DISK_LOG + + // Find a valid unit + disk = NULL; + for (lun = 0; lun < UnitMax; lun++) { + if (ctrl.unit[lun]) { + disk = ctrl.unit[lun]; + break; + } + } + + // Processed on the disk side (it is originally processed by the controller) + if (disk) { +#ifdef RASCSI + major = (DWORD)(RASCSI >> 8); + minor = (DWORD)(RASCSI & 0xff); +#else + host->GetVM()->GetVersion(major, minor); +#endif // RASCSI + ctrl.length = + ctrl.unit[lun]->Inquiry(ctrl.cmd, ctrl.buffer, major, minor); + } else { + ctrl.length = 0; + } + + if (ctrl.length <= 0) { + // failure (error) + Error(); + return; + } + + // Add synchronous transfer support information + if (scsi.syncenable) { + ctrl.buffer[7] |= (1 << 4); + } + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// MODE SELECT +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdModeSelect() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "MODE SELECT Command"); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->SelectCheck(ctrl.cmd); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Data out phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// MODE SENSE +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdModeSense() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "MODE SENSE Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->ModeSense(ctrl.cmd, ctrl.buffer); + ASSERT(ctrl.length >= 0); + if (ctrl.length == 0) { + Log(Log::Warning, + "Not supported MODE SENSE page $%02X", ctrl.cmd[2]); + + // Failure (Error) + Error(); + return; + } + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// START STOP UNIT +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdStartStop() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "START STOP UNIT Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->StartStop(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// SEND DIAGNOSTIC +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdSendDiag() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "SEND DIAGNOSTIC Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->SendDiag(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// PREVENT/ALLOW MEDIUM REMOVAL +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdRemoval() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "PREVENT/ALLOW MEDIUM REMOVAL Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Removal(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// READ CAPACITY +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdReadCapacity() +{ + DWORD lun; + int length; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "READ CAPACITY Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + length = ctrl.unit[lun]->ReadCapacity(ctrl.cmd, ctrl.buffer); + ASSERT(length >= 0); + if (length <= 0) { + Error(); + return; + } + + // Length setting + ctrl.length = length; + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// READ(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdRead10() +{ + DWORD lun; + DWORD record; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Receive message if host bridge + if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { + CmdGetMessage10(); + return; + } + + // Get record number and block number + record = ctrl.cmd[2]; + record <<= 8; + record |= ctrl.cmd[3]; + record <<= 8; + record |= ctrl.cmd[4]; + record <<= 8; + record |= ctrl.cmd[5]; + ctrl.blocks = ctrl.cmd[7]; + ctrl.blocks <<= 8; + ctrl.blocks |= ctrl.cmd[8]; + +#if defined(DISK_LOG) + Log(Log::Normal, "READ(10) command record=%08X block=%d", record, ctrl.blocks); +#endif // DISK_LOG + + // Do not process 0 blocks + if (ctrl.blocks == 0) { + Status(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.next = record + 1; + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// WRITE(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdWrite10() +{ + DWORD lun; + DWORD record; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Receive message with host bridge + if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { + CmdSendMessage10(); + return; + } + + // Get record number and block number + record = ctrl.cmd[2]; + record <<= 8; + record |= ctrl.cmd[3]; + record <<= 8; + record |= ctrl.cmd[4]; + record <<= 8; + record |= ctrl.cmd[5]; + ctrl.blocks = ctrl.cmd[7]; + ctrl.blocks <<= 8; + ctrl.blocks |= ctrl.cmd[8]; + +#if defined(DISK_LOG) + Log(Log::Normal, + "WRTIE(10) command record=%08X blocks=%d", record, ctrl.blocks); +#endif // DISK_LOG + + // Do not process 0 blocks + if (ctrl.blocks == 0) { + Status(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->WriteCheck(record); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.next = record + 1; + + // Data out phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// SEEK(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdSeek10() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "SEEK(10) Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->Seek(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// VERIFY +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdVerify() +{ + DWORD lun; + BOOL status; + DWORD record; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Get record number and block number + record = ctrl.cmd[2]; + record <<= 8; + record |= ctrl.cmd[3]; + record <<= 8; + record |= ctrl.cmd[4]; + record <<= 8; + record |= ctrl.cmd[5]; + ctrl.blocks = ctrl.cmd[7]; + ctrl.blocks <<= 8; + ctrl.blocks |= ctrl.cmd[8]; + +#if defined(DISK_LOG) + Log(Log::Normal, + "VERIFY command record=%08X blocks=%d", record, ctrl.blocks); +#endif // DISK_LOG + + // Do not process 0 blocks + if (ctrl.blocks == 0) { + Status(); + return; + } + + // if BytChk=0 + if ((ctrl.cmd[1] & 0x02) == 0) { + // Command processing on drive + status = ctrl.unit[lun]->Seek(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); + return; + } + + // Test loading + ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.next = record + 1; + + // Data out phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// SYNCHRONIZE CACHE +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdSynchronizeCache() +{ + DWORD lun; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Make it do something (not implemented)... + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// READ DEFECT DATA(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdReadDefectData10() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "READ DEFECT DATA(10) Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->ReadDefectData10(ctrl.cmd, ctrl.buffer); + ASSERT(ctrl.length >= 0); + + if (ctrl.length <= 4) { + Error(); + return; + } + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// READ TOC +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdReadToc() +{ + DWORD lun; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->ReadToc(ctrl.cmd, ctrl.buffer); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdPlayAudio10() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->PlayAudio(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO MSF +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdPlayAudioMSF() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->PlayAudioMSF(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO TRACK +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdPlayAudioTrack() +{ + DWORD lun; + BOOL status; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + status = ctrl.unit[lun]->PlayAudioTrack(ctrl.cmd); + if (!status) { + // Failure (Error) + Error(); + return; + } + + // status phase + Status(); +} + +//--------------------------------------------------------------------------- +// +// MODE SELECT10 +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdModeSelect10() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "MODE SELECT10 Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->SelectCheck10(ctrl.cmd); + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Data out phase + DataOut(); +} + +//--------------------------------------------------------------------------- +// +// MODE SENSE(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdModeSense10() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "MODE SENSE(10) Command "); +#endif // DISK_LOG + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Command processing on drive + ctrl.length = ctrl.unit[lun]->ModeSense10(ctrl.cmd, ctrl.buffer); + ASSERT(ctrl.length >= 0); + if (ctrl.length == 0) { + Log(Log::Warning, + "Not supported MODE SENSE(10) page $%02X", ctrl.cmd[2]); + + // Failure (Error) + Error(); + return; + } + + // Data-in Phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// GET MESSAGE(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdGetMessage10() +{ + DWORD lun; + SCSIBR *bridge; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Error if not a host bridge + if (ctrl.unit[lun]->GetID() != MAKEID('S', 'C', 'B', 'R')) { + Error(); + return; + } + + // Reallocate buffer (because it is not transfer for each block) + if (ctrl.bufsize < 0x1000000) { + free(ctrl.buffer); + ctrl.bufsize = 0x1000000; + ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); + } + + // Process with drive + bridge = (SCSIBR*)ctrl.unit[lun]; + ctrl.length = bridge->GetMessage10(ctrl.cmd, ctrl.buffer); + + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.blocks = 1; + ctrl.next = 1; + + // Data in phase + DataIn(); +} + +//--------------------------------------------------------------------------- +// +// SEND MESSAGE(10) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::CmdSendMessage10() +{ + DWORD lun; + + ASSERT(this); + + // Logical Unit + lun = (ctrl.cmd[1] >> 5) & 0x07; + if (!ctrl.unit[lun]) { + Error(); + return; + } + + // Error if not a host bridge + if (ctrl.unit[lun]->GetID() != MAKEID('S', 'C', 'B', 'R')) { + Error(); + return; + } + + // Reallocate buffer (because it is not transfer for each block) + if (ctrl.bufsize < 0x1000000) { + free(ctrl.buffer); + ctrl.bufsize = 0x1000000; + ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); + } + + // Set transfer amount + ctrl.length = ctrl.cmd[6]; + ctrl.length <<= 8; + ctrl.length |= ctrl.cmd[7]; + ctrl.length <<= 8; + ctrl.length |= ctrl.cmd[8]; + + if (ctrl.length <= 0) { + // Failure (Error) + Error(); + return; + } + + // Set next block + ctrl.blocks = 1; + ctrl.next = 1; + + // Light phase + DataOut(); +} + +//=========================================================================== +// +// Data Transfer +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Send data +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Send() +{ +#ifdef RASCSI + int len; +#endif // RASCSI + BOOL result; + + ASSERT(this); + ASSERT(!ctrl.bus->GetREQ()); + ASSERT(ctrl.bus->GetIO()); + +#ifdef RASCSI + //if Length! = 0, send + if (ctrl.length != 0) { + len = ctrl.bus->SendHandShake( + &ctrl.buffer[ctrl.offset], ctrl.length); + + // If you cannot send all, move to status phase + if (len != (int)ctrl.length) { + Error(); + return; + } + + // offset and length + ctrl.offset += ctrl.length; + ctrl.length = 0; + return; + } +#else + // offset and length + ASSERT(ctrl.length >= 1); + ctrl.offset++; + ctrl.length--; + + // Immediately after ACK is asserted, if the data has been + // set by SendNext, raise the request + if (ctrl.length != 0) { + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); + return; + } +#endif // RASCSI + + // Block subtraction, result initialization + ctrl.blocks--; + result = TRUE; + + // Processing after data collection (read/data-in only) + if (ctrl.phase == BUS::datain) { + if (ctrl.blocks != 0) { + // // set next buffer (set offset, length) + result = XferIn(ctrl.buffer); +#ifndef RASCSI + ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); +#endif // RASCSI + } + } + + // If result FALSE, move to status phase + if (!result) { + Error(); + return; + } + + // Continue sending if block !=0 + if (ctrl.blocks != 0){ + ASSERT(ctrl.length > 0); + ASSERT(ctrl.offset == 0); +#ifndef RASCSI + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + + // Move to next phase + switch (ctrl.phase) { + // Message in phase + case BUS::msgin: + // Completed sending response to extended message of IDENTIFY message + if (scsi.atnmsg) { + // flag off + scsi.atnmsg = FALSE; + + // command phase + Command(); + } else { + // Bus free phase + BusFree(); + } + break; + + // Data-in Phase + case BUS::datain: + // status phase + Status(); + break; + + // status phase + case BUS::status: + // Message in phase + ctrl.length = 1; + ctrl.blocks = 1; + ctrl.buffer[0] = (BYTE)ctrl.message; + MsgIn(); + break; + + // Other (impossible) + default: + ASSERT(FALSE); + break; + } +} + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Continue data transmission..... +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::SendNext() +{ + ASSERT(this); + + // REQ is up + ASSERT(ctrl.bus->GetREQ()); + ASSERT(ctrl.bus->GetIO()); + + // Signal line operated by the target + ctrl.bus->SetREQ(FALSE); + + // If there is data in the buffer, set it first + if (ctrl.length > 1) { + ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset + 1]); + } +} +#endif // RASCSI + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Receive data +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Receive() +{ + DWORD data; + + ASSERT(this); + + // Req is up + ASSERT(ctrl.bus->GetREQ()); + ASSERT(!ctrl.bus->GetIO()); + + // Get data + data = (DWORD)ctrl.bus->GetDAT(); + + // Signal line operated by the target + ctrl.bus->SetREQ(FALSE); + + switch (ctrl.phase) { + // Command phase + case BUS::command: + ctrl.cmd[ctrl.offset] = data; +#if defined(DISK_LOG) + Log(Log::Normal, "Command phase $%02X", data); +#endif // DISK_LOG + + // Set the length again with the first data (offset 0) + if (ctrl.offset == 0) { + if (ctrl.cmd[0] >= 0x20) { + // 10バイトCDB + ctrl.length = 10; + } + } + break; + + // Message out phase + case BUS::msgout: + ctrl.message = data; +#if defined(DISK_LOG) + Log(Log::Normal, "Message out phase $%02X", data); +#endif // DISK_LOG + break; + + // Data out phase + case BUS::dataout: + ctrl.buffer[ctrl.offset] = (BYTE)data; + break; + + // Other (impossible) + default: + ASSERT(FALSE); + break; + } +} +#endif // RASCSI + +#ifdef RASCSI +//--------------------------------------------------------------------------- +// +// Receive Data +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::Receive() +#else +//--------------------------------------------------------------------------- +// +// Continue receiving data +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIDEV::ReceiveNext() +#endif // RASCSI +{ +#ifdef RASCSI + int len; +#endif // RASCSI + BOOL result; + int i; + BYTE data; + + ASSERT(this); + + // REQ is low + ASSERT(!ctrl.bus->GetREQ()); + ASSERT(!ctrl.bus->GetIO()); + +#ifdef RASCSI + // Length != 0 if received + if (ctrl.length != 0) { + // Receive + len = ctrl.bus->ReceiveHandShake( + &ctrl.buffer[ctrl.offset], ctrl.length); + + // If not able to receive all, move to status phase + if (len != (int)ctrl.length) { + Error(); + return; + } + + // Offset and Length + ctrl.offset += ctrl.length; + ctrl.length = 0;; + return; + } +#else + // Offset and Length + ASSERT(ctrl.length >= 1); + ctrl.offset++; + ctrl.length--; + + // If length!=0, set req again + if (ctrl.length != 0) { + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); + return; + } +#endif // RASCSI + + // Block subtraction, result initialization + ctrl.blocks--; + result = TRUE; + + // Processing after receiving data (by phase) + switch (ctrl.phase) { + + // Data out phase + case BUS::dataout: + if (ctrl.blocks == 0) { + // End with this buffer + result = XferOut(FALSE); + } else { + // Continue to next buffer (set offset, length) + result = XferOut(TRUE); + } + break; + + // Message out phase + case BUS::msgout: + ctrl.message = ctrl.buffer[0]; + if (!XferMsg(ctrl.message)) { + // Immediately free the bus if message output fails + BusFree(); + return; + } + + // Clear message data in preparation for message-in + ctrl.message = 0x00; + break; + + default: + break; + } + + // If result FALSE, move to status phase + if (!result) { + Error(); + return; + } + + // Continue to receive if block !=0 + if (ctrl.blocks != 0){ + ASSERT(ctrl.length > 0); + ASSERT(ctrl.offset == 0); +#ifndef RASCSI + // Signal line operated by the target + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + + // Move to next phase + switch (ctrl.phase) { + // Command phase + case BUS::command: +#ifdef RASCSI + // Command data transfer + len = 6; + if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) { + // 10 byte CDB + len = 10; + } + for (i = 0; i < len; i++) { + ctrl.cmd[i] = (DWORD)ctrl.buffer[i]; +#if defined(DISK_LOG) + Log(Log::Normal, "Command $%02X", ctrl.cmd[i]); +#endif // DISK_LOG + } +#endif // RASCSI + + // Execution Phase + Execute(); + break; + + // Message out phase + case BUS::msgout: + // Continue message out phase as long as ATN keeps asserting + if (ctrl.bus->GetATN()) { + // Data transfer is 1 byte x 1 block + ctrl.offset = 0; + ctrl.length = 1; + ctrl.blocks = 1; +#ifndef RASCSI + // Request message + ctrl.bus->SetREQ(TRUE); +#endif // RASCSI + return; + } + + // Parsing messages sent by ATN + if (scsi.atnmsg) { + i = 0; + while (i < scsi.msc) { + // Message type + data = scsi.msb[i]; + + // ABORT + if (data == 0x06) { +#if defined(DISK_LOG) + Log(Log::Normal, + "Message code ABORT $%02X", data); +#endif // DISK_LOG + BusFree(); + return; + } + + // BUS DEVICE RESET + if (data == 0x0C) { +#if defined(DISK_LOG) + Log(Log::Normal, + "Message code BUS DEVICE RESET $%02X", data); +#endif // DISK_LOG + scsi.syncoffset = 0; + BusFree(); + return; + } + + // IDENTIFY + if (data >= 0x80) { +#if defined(DISK_LOG) + Log(Log::Normal, + "Message code IDENTIFY $%02X", data); +#endif // DISK_LOG + } + + // Extended Message + if (data == 0x01) { +#if defined(DISK_LOG) + Log(Log::Normal, + "Message code EXTENDED MESSAGE $%02X", data); +#endif // DISK_LOG + + // Check only when synchronous transfer is possible + if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) { + ctrl.length = 1; + ctrl.blocks = 1; + ctrl.buffer[0] = 0x07; + MsgIn(); + return; + } + + // Transfer period factor (limited to 50 x 4 = 200ns) + scsi.syncperiod = scsi.msb[i + 3]; + if (scsi.syncperiod > 50) { + scsi.syncoffset = 50; + } + + // REQ/ACK offset(limited to 16) + scsi.syncoffset = scsi.msb[i + 4]; + if (scsi.syncoffset > 16) { + scsi.syncoffset = 16; + } + + // STDR response message generation + ctrl.length = 5; + ctrl.blocks = 1; + ctrl.buffer[0] = 0x01; + ctrl.buffer[1] = 0x03; + ctrl.buffer[2] = 0x01; + ctrl.buffer[3] = (BYTE)scsi.syncperiod; + ctrl.buffer[4] = (BYTE)scsi.syncoffset; + MsgIn(); + return; + } + + // next + i++; + } + } + + // Initialize ATN message reception status + scsi.atnmsg = FALSE; + + // Command phase + Command(); + break; + + // Data out phase + case BUS::dataout: + // Flush unit + FlushUnit(); + + // status phase + Status(); + break; + + // Other (impossible) + default: + ASSERT(FALSE); + break; + } +} + +//--------------------------------------------------------------------------- +// +// Transfer MSG +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIDEV::XferMsg(DWORD msg) +{ + ASSERT(this); + ASSERT(ctrl.phase == BUS::msgout); + + // Save message out data + if (scsi.atnmsg) { + scsi.msb[scsi.msc] = (BYTE)msg; + scsi.msc++; + scsi.msc %= 256; + } + + return TRUE; +} diff --git a/src/raspberrypi/controllers/scsidev_ctrl.h b/src/raspberrypi/controllers/scsidev_ctrl.h new file mode 100644 index 00000000..f830b2d0 --- /dev/null +++ b/src/raspberrypi/controllers/scsidev_ctrl.h @@ -0,0 +1,142 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI device controller ] +// +//--------------------------------------------------------------------------- +#pragma once +#include "controllers/sasidev_ctrl.h" + +//=========================================================================== +// +// SCSI Device (Interits SASI device) +// +//=========================================================================== +class SCSIDEV : public SASIDEV +{ +public: + // Internal data definition + typedef struct { + // Synchronous transfer + BOOL syncenable; // Synchronous transfer possible + int syncperiod; // Synchronous transfer period + int syncoffset; // Synchronous transfer offset + int syncack; // Number of synchronous transfer ACKs + + // ATN message + BOOL atnmsg; + int msc; + BYTE msb[256]; + } scsi_t; + +public: + // Basic Functions +#ifdef RASCSI + SCSIDEV(); +#else + SCSIDEV(Device *dev); +#endif // RASCSI + // Constructor + + void FASTCALL Reset(); + // Device Reset + + // 外部API + BUS::phase_t FASTCALL Process(); + // Run + + void FASTCALL SyncTransfer(BOOL enable) { scsi.syncenable = enable; } + // Synchronouse transfer enable setting + + // Other + BOOL FASTCALL IsSASI() const {return FALSE;} + // SASI Check + BOOL FASTCALL IsSCSI() const {return TRUE;} + // SCSI check + +private: + // Phase + void FASTCALL BusFree(); + // Bus free phase + void FASTCALL Selection(); + // Selection phase + void FASTCALL Execute(); + // Execution phase + void FASTCALL MsgOut(); + // Message out phase + void FASTCALL Error(); + // Common erorr handling + + // commands + void FASTCALL CmdInquiry(); + // INQUIRY command + void FASTCALL CmdModeSelect(); + // MODE SELECT command + void FASTCALL CmdModeSense(); + // MODE SENSE command + void FASTCALL CmdStartStop(); + // START STOP UNIT command + void FASTCALL CmdSendDiag(); + // SEND DIAGNOSTIC command + void FASTCALL CmdRemoval(); + // PREVENT/ALLOW MEDIUM REMOVAL command + void FASTCALL CmdReadCapacity(); + // READ CAPACITY command + void FASTCALL CmdRead10(); + // READ(10) command + void FASTCALL CmdWrite10(); + // WRITE(10) command + void FASTCALL CmdSeek10(); + // SEEK(10) command + void FASTCALL CmdVerify(); + // VERIFY command + void FASTCALL CmdSynchronizeCache(); + // SYNCHRONIZE CACHE command + void FASTCALL CmdReadDefectData10(); + // READ DEFECT DATA(10) command + void FASTCALL CmdReadToc(); + // READ TOC command + void FASTCALL CmdPlayAudio10(); + // PLAY AUDIO(10) command + void FASTCALL CmdPlayAudioMSF(); + // PLAY AUDIO MSF command + void FASTCALL CmdPlayAudioTrack(); + // PLAY AUDIO TRACK INDEX command + void FASTCALL CmdModeSelect10(); + // MODE SELECT(10) command + void FASTCALL CmdModeSense10(); + // MODE SENSE(10) command + void FASTCALL CmdGetMessage10(); + // GET MESSAGE(10) command + void FASTCALL CmdSendMessage10(); + // SEND MESSAGE(10) command + + // データ転送 + void FASTCALL Send(); + // Send data +#ifndef RASCSI + void FASTCALL SendNext(); + // Continue sending data +#endif // RASCSI + void FASTCALL Receive(); + // Receive data +#ifndef RASCSI + void FASTCALL ReceiveNext(); + // Continue receiving data +#endif // RASCSI + BOOL FASTCALL XferMsg(DWORD msg); + // Data transfer message + + scsi_t scsi; + // Internal data +}; + diff --git a/src/raspberrypi/devices/cfilesystem.cpp b/src/raspberrypi/devices/cfilesystem.cpp new file mode 100644 index 00000000..357ab502 --- /dev/null +++ b/src/raspberrypi/devices/cfilesystem.cpp @@ -0,0 +1,4794 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +// Imported NetBSD support and some optimisation patch by Rin Okuyama. +// Imported sava's bugfix patch(in RASDRV DOS edition). +// +// [ Host File System for the X68000 ] +// +// Note: This functionality is specific to the X68000 +// operating system. +// It is highly unlikely that this will work for other +// platforms. +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "xm6.h" +#include "log.h" +#include "filepath.h" +#include "cfilesystem.h" + +#ifdef BAREMETAL +//--------------------------------------------------------------------------- +// +// FatFs用タイムスタンプ +// +//--------------------------------------------------------------------------- +#define FF_NORTC_HOUR 6 +#define FF_NORTC_MINUTE 8 +#define FF_NORTC_SECOND 0 + +static DWORD fattime = ( + (DWORD)(FF_NORTC_YEAR - 1980) << 25 | + (DWORD)FF_NORTC_MON << 21 | + (DWORD)FF_NORTC_MDAY << 16 | + (DWORD)(FF_NORTC_HOUR << 11) | + (DWORD)(FF_NORTC_MINUTE << 5) | + (DWORD)(FF_NORTC_SECOND) +); + +DWORD get_fattime(void) +{ + return fattime; +} + +void set_fattime(DWORD n) +{ + fattime = n; +} +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// 漢字コード変換 +// +//--------------------------------------------------------------------------- +#define IC_BUF_SIZE 1024 +static char convert_buf[IC_BUF_SIZE]; +#ifndef BAREMETAL +#ifndef __NetBSD__ +// POSIX.1準拠iconv(3)を使用 +#define CONVERT(src, dest, inbuf, outbuf, outsize) \ + convert(src, dest, (char *)inbuf, outbuf, outsize) +static void convert(char const *src, char const *dest, + char *inbuf, char *outbuf, size_t outsize) +#else +// NetBSD版iconv(3)を使用: 第2引数はconst char ** +#define CONVERT(src, dest, inbuf, outbuf, outsize) \ + convert(src, dest, inbuf, outbuf, outsize) +static void convert(char const *src, char const *dest, + const char *inbuf, char *outbuf, size_t outsize) +#endif +{ +#ifndef __APPLE__ + iconv_t cd; + size_t in; + size_t out; + size_t ret; + + *outbuf = '\0'; + in = strlen(inbuf); + out = outsize - 1; + + cd = iconv_open(dest, src); + if (cd == (iconv_t)-1) { + return; + } + + ret = iconv(cd, &inbuf, &in, &outbuf, &out); + if (ret == (size_t)-1) { + return; + } + + iconv_close(cd); + *outbuf = '\0'; +#endif //ifndef __macintosh__ +} +#else +// Newlibの中にiconvが含まれてなかったので無変換 +// ベアメタルのFatFS上はSJISでもOKだと思うよ +#define CONVERT(src, dest, inbuf, outbuf, outsize) \ + convert(src, dest, (char *)inbuf, outbuf, outsize) +static void convert(char const *src, char const *dest, + char *inbuf, char *outbuf, size_t outsize) +{ + strcpy(outbuf, inbuf); + strcpy(convert_buf, inbuf); +} +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// SJIS->UTF8変換 +// +//--------------------------------------------------------------------------- +static char* SJIS2UTF8(const char *sjis, char *utf8, size_t bufsize) +{ + CONVERT("SJIS", "UTF-8", sjis, utf8, bufsize); + return convert_buf; +} + +//--------------------------------------------------------------------------- +// +// UTF8->SJIS変換 +// +//--------------------------------------------------------------------------- +static char* UTF82SJIS(const char *utf8, char *sjis, size_t bufsize) +{ + CONVERT("UTF-8", "SJIS", utf8, sjis, bufsize); + return convert_buf; +} + +//--------------------------------------------------------------------------- +// +// SJIS->UTF8変換(簡易版) +// +//--------------------------------------------------------------------------- +static char* S2U(const char *sjis) +{ + SJIS2UTF8(sjis, convert_buf, IC_BUF_SIZE); + return convert_buf; +} + +//--------------------------------------------------------------------------- +// +// UTF8->SJIS変換(簡易版) +// +//--------------------------------------------------------------------------- +static char* U2S(const char *utf8) +{ + UTF82SJIS(utf8, convert_buf, IC_BUF_SIZE); + return convert_buf; +} + +//--------------------------------------------------------------------------- +// +/// パス名取得 +/// +/// Human68k用namests構造体から、Human68kパス名を取得する。 +/// 書き込み先バッファは66バイト必要。 +// +//--------------------------------------------------------------------------- +void Human68k::namests_t::GetCopyPath(BYTE* szPath) const +{ + ASSERT(this); + ASSERT(szPath); + + BYTE* p = szPath; + for (size_t i = 0; i < 65; i++) { + BYTE c = path[i]; + if (c == '\0') + break; + if (c == 0x09) { + c = '/'; + } + *p++ = c; + } + + *p = '\0'; +} + +//--------------------------------------------------------------------------- +// +/// ファイル名取得 +/// +/// Human68k用namests構造体から、Human68kファイル名を取得する。 +/// 書き込み先バッファは23バイト必要。 +// +//--------------------------------------------------------------------------- +void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const +{ + ASSERT(this); + ASSERT(szFilename); + + size_t i; + BYTE* p = szFilename; + + // ファイル名本体転送 + for (i = 0; i < 8; i++) { + BYTE c = name[i]; + if (c == ' ') { + // ファイル名中にスペースが出現した場合、以降のエントリが続いているかどうか確認 + /// @todo 8+3文字とTewntyOne互換モードで動作を変えるべき + // add[0] が有効な文字なら続ける + if (add[0] != '\0') + goto next_name; + // name[i] より後に空白以外の文字が存在するなら続ける + for (size_t j = i + 1; j < 8; j++) { + if (name[j] != ' ') + goto next_name; + } + // ファイル名終端なら転送終了 + break; + } + next_name: + *p++ = c; + } + // 全ての文字を読み込むと、ここで i >= 8 となる + + // ファイル名本体が8文字以上なら追加部分も加える + if (i >= 8) { + // ファイル名追加部分転送 + for (i = 0; i < 10; i++) { + BYTE c = add[i]; + if (c == '\0') + break; + *p++ = c; + } + // 全ての文字を読み込むと、ここで i >= 10 となる + } + + // 拡張子が存在する場合は転送 + if (ext[0] != ' ' || ext[1] != ' ' || ext[2] != ' ') { + *p++ = '.'; + for (i = 0; i < 3; i++) { + BYTE c = ext[i]; + if (c == ' ') { + // 拡張子中にスペースが出現した場合、以降のエントリが続いているかどうか確認 + /// @todo 8+3文字とTewntyOne互換モードで動作を変えるべき + // ext[i] より後に空白以外の文字が存在するなら続ける + for (size_t j = i + 1; j < 3; j++) { + if (ext[j] != ' ') + goto next_ext; + } + // 拡張子終端なら転送終了 + break; + } + next_ext: + *p++ = c; + } + // 全ての文字を読み込むと、ここで i >= 3 となる + } + + // 番兵追加 + *p = '\0'; +} + +//=========================================================================== +// +// ホスト側ドライブ +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +/// デフォルトコンストラクタ +// +//--------------------------------------------------------------------------- +CHostDrv::CHostDrv() +{ + // 初期化 + m_bWriteProtect = FALSE; + m_bEnable = FALSE; + m_capCache.sectors = 0; + m_bVolumeCache = FALSE; + m_szVolumeCache[0] = _T('\0'); + m_szBase[0] = _T('\0'); + m_nRing = 0; +} + +//--------------------------------------------------------------------------- +// +/// デストラクタ final +// +//--------------------------------------------------------------------------- +CHostDrv::~CHostDrv() +{ + CHostPath* p; + while ((p = (CHostPath*)m_cRing.Next()) != &m_cRing) { + delete p; + ASSERT(m_nRing); + m_nRing--; + } + + // 実体が存在しないことを確認 (念のため) + ASSERT(m_cRing.Next() == &m_cRing); + ASSERT(m_cRing.Prev() == &m_cRing); + ASSERT(m_nRing == 0); +} + +//--------------------------------------------------------------------------- +// +/// 初期化 (デバイス起動とロード) +// +//--------------------------------------------------------------------------- +void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag) +{ + ASSERT(this); + ASSERT(szBase); + ASSERT(strlen(szBase) < FILEPATH_MAX); + ASSERT(m_bWriteProtect == FALSE); + ASSERT(m_bEnable == FALSE); + ASSERT(m_capCache.sectors == 0); + ASSERT(m_bVolumeCache == FALSE); + ASSERT(m_szVolumeCache[0] == _T('\0')); + + // 実体が存在しないことを確認 (念のため) + ASSERT(m_cRing.Next() == &m_cRing); + ASSERT(m_cRing.Prev() == &m_cRing); + ASSERT(m_nRing == 0); + + // パラメータを受け取る + if (nFlag & FSFLAG_WRITE_PROTECT) + m_bWriteProtect = TRUE; + strcpy(m_szBase, szBase); + + // ベースパスの最後のパス区切りマークを削除する + /// @warning Unicode利用時は修正が必要 + TCHAR* pClear = NULL; + TCHAR* p = m_szBase; + for (;;) { + TCHAR c = *p; + if (c == _T('\0')) + break; + if (c == _T('/') || c == _T('\\')) { + pClear = p; + } else { + pClear = NULL; + } + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + p++; + if (*p == _T('\0')) + break; + } + p++; + } + if (pClear) + *pClear = _T('\0'); + + // 状態更新 + m_bEnable = TRUE; +} + +//--------------------------------------------------------------------------- +// +/// メディアチェック +// +//--------------------------------------------------------------------------- +BOOL CHostDrv::isMediaOffline() +{ + ASSERT(this); + + // オフライン状態チェック + return m_bEnable == FALSE; +} + +//--------------------------------------------------------------------------- +// +/// メディアバイトの取得 +// +//--------------------------------------------------------------------------- +BYTE CHostDrv::GetMediaByte() const +{ + ASSERT(this); + + return Human68k::MEDIA_REMOTE; +} + +//--------------------------------------------------------------------------- +// +/// ドライブ状態の取得 +// +//--------------------------------------------------------------------------- +DWORD CHostDrv::GetStatus() const +{ + ASSERT(this); + + return 0x40 | (m_bEnable ? (m_bWriteProtect ? 0x08 : 0) | 0x02 : 0); +} + +//--------------------------------------------------------------------------- +// +/// メディア状態設定 +// +//--------------------------------------------------------------------------- +void CHostDrv::SetEnable(BOOL bEnable) +{ + ASSERT(this); + + m_bEnable = bEnable; + + if (bEnable == FALSE) { + // キャッシュ消去 + m_capCache.sectors = 0; + m_bVolumeCache = FALSE; + m_szVolumeCache[0] = _T('\0'); + } +} + +//--------------------------------------------------------------------------- +// +/// メディア交換チェック +// +//--------------------------------------------------------------------------- +BOOL CHostDrv::CheckMedia() +{ + ASSERT(this); + + // 状態更新 + Update(); + if (m_bEnable == FALSE) + CleanCache(); + + return m_bEnable; +} + +//--------------------------------------------------------------------------- +// +/// メディア状態更新 +// +//--------------------------------------------------------------------------- +void CHostDrv::Update() +{ + ASSERT(this); + + // メディア挿入とみなす + BOOL bEnable = TRUE; + + // メディア状態反映 + SetEnable(bEnable); +} + +//--------------------------------------------------------------------------- +// +/// イジェクト +// +//--------------------------------------------------------------------------- +void CHostDrv::Eject() +{ + ASSERT(this); + + // メディア排出 + CleanCache(); + SetEnable(FALSE); + + // 状態更新 + Update(); +} + +//--------------------------------------------------------------------------- +// +/// ボリュームラベルの取得 +// +//--------------------------------------------------------------------------- +void CHostDrv::GetVolume(TCHAR* szLabel) +{ + ASSERT(this); + ASSERT(szLabel); + ASSERT(m_bEnable); + + // ボリュームラベルの取得 +#ifdef RASCSI + strcpy(m_szVolumeCache, "RASDRV "); + if (m_szBase[0]) { + strcat(m_szVolumeCache, m_szBase); + } else { + strcat(m_szVolumeCache, "/"); + } +#else + m_szVolumeCache[0] = _T('\0'); +#endif + + // キャッシュ更新 + m_bVolumeCache = TRUE; + + // 内容を転送 + strcpy(szLabel, m_szVolumeCache); +} + +//--------------------------------------------------------------------------- +// +/// キャッシュからボリュームラベルを取得 +/// +/// キャッシュされているボリュームラベル情報を転送する。 +/// キャッシュ内容が有効ならTRUEを、無効ならFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostDrv::GetVolumeCache(TCHAR* szLabel) const +{ + ASSERT(this); + ASSERT(szLabel); + + // 内容を転送 + strcpy(szLabel, m_szVolumeCache); + + return m_bVolumeCache; +} + +//--------------------------------------------------------------------------- +// +/// 容量の取得 +// +//--------------------------------------------------------------------------- +DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity) +{ + ASSERT(this); + ASSERT(pCapacity); + ASSERT(m_bEnable); + + DWORD nFree = 0x7FFF8000; + DWORD freearea; + DWORD clusters; + DWORD sectors; + + freearea = 0xFFFF; + clusters = 0xFFFF; + sectors = 64; + + // パラメータ範囲想定 + ASSERT(freearea <= 0xFFFF); + ASSERT(clusters <= 0xFFFF); + ASSERT(sectors <= 64); + + // キャッシュ更新 + m_capCache.freearea = (WORD)freearea; + m_capCache.clusters = (WORD)clusters; + m_capCache.sectors = (WORD)sectors; + m_capCache.bytes = 512; + + // 内容を転送 + memcpy(pCapacity, &m_capCache, sizeof(m_capCache)); + + return nFree; +} + +//--------------------------------------------------------------------------- +// +/// キャッシュから容量を取得 +/// +/// キャッシュされている容量情報を転送する。 +/// キャッシュ内容が有効ならTRUEを、無効ならFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostDrv::GetCapacityCache(Human68k::capacity_t* pCapacity) const +{ + ASSERT(this); + ASSERT(pCapacity); + + // 内容を転送 + memcpy(pCapacity, &m_capCache, sizeof(m_capCache)); + + return m_capCache.sectors != 0; +} + +//--------------------------------------------------------------------------- +// +/// 全てのキャッシュを更新する +// +//--------------------------------------------------------------------------- +void CHostDrv::CleanCache() +{ + ASSERT(this); + + Lock(); + for (CHostPath* p = (CHostPath*)m_cRing.Next(); p != &m_cRing;) { + p->Release(); + p = (CHostPath*)p->Next(); + } + Unlock(); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパスのキャッシュを更新する +// +//--------------------------------------------------------------------------- +void CHostDrv::CleanCache(const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + + Lock(); + CHostPath* p = FindCache(szHumanPath); + if (p) { + p->Restore(); + p->Release(); + } + Unlock(); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパス以下のキャッシュを全て更新する +// +//--------------------------------------------------------------------------- +void CHostDrv::CleanCacheChild(const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + + Lock(); + CHostPath* p = (CHostPath*)m_cRing.Next(); + while (p != &m_cRing) { + if (p->isSameChild(szHumanPath)) + p->Release(); + p = (CHostPath*)p->Next(); + } + Unlock(); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパスのキャッシュを削除する +// +//--------------------------------------------------------------------------- +void CHostDrv::DeleteCache(const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + + Lock(); + CHostPath* p = FindCache(szHumanPath); + if (p) { + delete p; + ASSERT(m_nRing); + m_nRing--; + } + Unlock(); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパスがキャッシュされているか検索する +/// +/// 所有するキャシュバッファの中から完全一致で検索し、見つかればその名称を返す。 +/// ファイル名を除外しておくこと。 +/// 必ず上位で排他制御を行なうこと。 +// +//--------------------------------------------------------------------------- +CHostPath* CHostDrv::FindCache(const BYTE* szHuman) +{ + ASSERT(this); + ASSERT(szHuman); + + // 所持している全てのファイル名の中から完全一致するものを検索 + for (CHostPath* p = (CHostPath*)m_cRing.Next(); p != &m_cRing;) { + if (p->isSameHuman(szHuman)) + return p; + p = (CHostPath*)p->Next(); + } + + return NULL; +} + +//--------------------------------------------------------------------------- +// +/// キャッシュ情報を元に、ホスト側の名称を獲得する +/// +/// パスがキャッシュにあるか確認。なければエラー。 +/// 見つかったキャッシュの更新チェック。更新が必要ならエラー。 +/// 必ず上位で排他制御を行なうこと。 +// +//--------------------------------------------------------------------------- +CHostPath* CHostDrv::CopyCache(CHostFiles* pFiles) +{ + ASSERT(this); + ASSERT(pFiles); + ASSERT(strlen((const char*)pFiles->GetHumanPath()) < HUMAN68K_PATH_MAX); + + // キャッシュ検索 + CHostPath* pPath = FindCache(pFiles->GetHumanPath()); + if (pPath == NULL) { + return NULL; // エラー: キャッシュなし + } + + // リング先頭へ移動 + pPath->Insert(&m_cRing); + + // キャッシュ更新チェック + if (pPath->isRefresh()) { + return NULL; // エラー: キャッシュ更新が必要 + } + + // ホスト側のパス名を保存 + pFiles->SetResult(pPath->GetHost()); + + return pPath; +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称の構築に必要な情報をすべて取得する +/// +/// ファイル名は省略可能。(普通は指定しない) +/// 必ず上位で排他制御を行なうこと。 +/// ベースパス末尾にパス区切り文字をつけないよう注意。 +/// ファイルアクセスが多発する可能性があるときは、VMスレッドの動作を開始させる。 +/// +/// 使いかた: +/// CopyCache()してエラーの場合はMakeCache()する。必ず正しいホスト側のパスが取得できる。 +/// +/// ファイル名とパス名をすべて分離する。 +/// 上位ディレクトリから順に、キャッシュされているかどうか確認。 +/// キャッシュされていれば破棄チェック。破棄した場合未キャッシュ扱いとなる。 +/// キャッシュされていなければキャッシュを構築。 +/// 順番にすべてのディレクトリ・ファイル名に対して行ない終了。 +/// エラーが発生した場合はNULLとなる。 +// +//--------------------------------------------------------------------------- +CHostPath* CHostDrv::MakeCache(CHostFiles* pFiles) +{ + ASSERT(this); + ASSERT(pFiles); + ASSERT(strlen((const char*)pFiles->GetHumanPath()) < HUMAN68K_PATH_MAX); + + ASSERT(m_szBase); + ASSERT(strlen(m_szBase) < FILEPATH_MAX); + + BYTE szHumanPath[HUMAN68K_PATH_MAX]; // ルートから順にパス名が入る + szHumanPath[0] = '\0'; + size_t nHumanPath = 0; + + TCHAR szHostPath[FILEPATH_MAX]; + strcpy(szHostPath, m_szBase); + size_t nHostPath = strlen(szHostPath); + + CHostPath* pPath; + const BYTE* p = pFiles->GetHumanPath(); + for (;;) { + // パス区切りを追加 + if (nHumanPath + 1 >= HUMAN68K_PATH_MAX) + return NULL; // エラー: Human68kパスが長すぎる + szHumanPath[nHumanPath++] = '/'; + szHumanPath[nHumanPath] = '\0'; + if (nHostPath + 1 >= FILEPATH_MAX) + return NULL; // エラー: ホスト側のパスが長すぎる + szHostPath[nHostPath++] = _T('/'); + szHostPath[nHostPath] = _T('\0'); + + // ファイルいっこいれる + BYTE szHumanFilename[24]; // ファイル名部分 + p = SeparateCopyFilename(p, szHumanFilename); + if (p == NULL) + return NULL; // エラー: ファイル名読み込み失敗 + size_t n = strlen((const char*)szHumanFilename); + if (nHumanPath + n >= HUMAN68K_PATH_MAX) + return NULL; // エラー: Human68kパスが長すぎる + + // 該当パスがキャッシュされているか? + pPath = FindCache(szHumanPath); + if (pPath == NULL) { + // キャッシュ最大数チェック + if (m_nRing >= XM6_HOST_DIRENTRY_CACHE_MAX) { + // 最も古いキャッシュを破棄して再利用 + pPath = (CHostPath*)m_cRing.Prev(); + pPath->Clean(); // 全ファイル解放 更新チェック用ハンドルも解放 + } else { + // 新規登録 + pPath = new CHostPath; + ASSERT(pPath); + m_nRing++; + } + pPath->SetHuman(szHumanPath); + pPath->SetHost(szHostPath); + + // 状態更新 + pPath->Refresh(); + } + + // キャッシュ更新チェック + if (pPath->isRefresh()) { + // 更新 + Update(); + + // 状態更新 + pPath->Refresh(); + } + + // リング先頭へ + pPath->Insert(&m_cRing); + + // ファイル名がなければここで終了 + if (n == 0) + break; + + // 次のパスを検索 + // パスの途中ならディレクトリかどうか確認 + const CHostFilename* pFilename; + if (*p != '\0') + pFilename = pPath->FindFilename(szHumanFilename, Human68k::AT_DIRECTORY); + else + pFilename = pPath->FindFilename(szHumanFilename); + if (pFilename == NULL) + return NULL; // エラー: 途中のパス名/ファイル名が見つからない + + // パス名を連結 + strcpy((char*)szHumanPath + nHumanPath, (const char*)szHumanFilename); + nHumanPath += n; + + n = strlen(pFilename->GetHost()); + if (nHostPath + n >= FILEPATH_MAX) + return NULL; // エラー: ホスト側のパスが長すぎる + strcpy(szHostPath + nHostPath, pFilename->GetHost()); + nHostPath += n; + + // PLEASE CONTINUE + if (*p == '\0') + break; + } + + // ホスト側のパス名を保存 + pFiles->SetResult(szHostPath); + + return pPath; +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) +/// +/// あらかじめ全てのHuman68k用パラメータを設定しておくこと。 +// +//--------------------------------------------------------------------------- +BOOL CHostDrv::Find(CHostFiles* pFiles) +{ + ASSERT(this); + ASSERT(pFiles); + + // 排他制御開始 + Lock(); + + // パス名獲得およびキャッシュ構築 + CHostPath* pPath = CopyCache(pFiles); + if (pPath == NULL) { + pPath = MakeCache(pFiles); + if (pPath == NULL) { + Unlock(); + CleanCache(); + return FALSE; // エラー: キャッシュ構築失敗 + } + } + + // ホスト側のパス名を保存 + pFiles->SetResult(pPath->GetHost()); + + // パス名のみなら終了 + if (pFiles->isPathOnly()) { + Unlock(); + return TRUE; // 正常終了: パス名のみ + } + + // ファイル名検索 + const CHostFilename* pFilename = pFiles->Find(pPath); + if (pFilename == NULL) { + Unlock(); + return FALSE; // エラー: ファイル名が獲得できません + } + + // Human68k側の検索結果保存 + pFiles->SetEntry(pFilename); + + // ホスト側のフルパス名保存 + pFiles->AddResult(pFilename->GetHost()); + + // 排他制御終了 + Unlock(); + + return TRUE; +} + +//=========================================================================== +// +// ディレクトリエントリ ファイル名 +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +/// デフォルトコンストラクタ +// +//--------------------------------------------------------------------------- +CHostFilename::CHostFilename() +{ +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称を設定 +// +//--------------------------------------------------------------------------- +void CHostFilename::SetHost(const TCHAR* szHost) +{ + ASSERT(this); + ASSERT(szHost); + ASSERT(strlen(szHost) < FILEPATH_MAX); + + strcpy(m_szHost, szHost); +} + +//--------------------------------------------------------------------------- +// +/// Human68k側のファイル名要素をコピー +// +//--------------------------------------------------------------------------- +BYTE* CHostFilename::CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast) // static +{ + ASSERT(pWrite); + ASSERT(pFirst); + ASSERT(pLast); + + for (const BYTE* p = pFirst; p < pLast; p++) { + *pWrite++ = *p; + } + + return pWrite; +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の名称を変換 +/// +/// あらかじめSetHost()を実行しておくこと。 +/// 18+3の命名規則に従った名前変換を行なう。 +/// ファイル名先頭および末尾の空白は、Human68kで扱えないため自動的に削除される。 +/// ディレクトリエントリの名前部分を、ファイル名変換時の拡張子の位置情報を使って生成する。 +/// その後、ファイル名の異常判定を行なう。(スペース8文字だけのファイル名など) +/// ファイル名の重複判定は行なわないので注意。これらの判定は上位クラスで行なう。 +/// TwentyOne version 1.36c modified +14 patchlevel9以降の拡張子規則に対応させる。 +// +//--------------------------------------------------------------------------- +void CHostFilename::ConvertHuman(int nCount) +{ + char szHost[FILEPATH_MAX]; + + ASSERT(this); + + // 特殊ディレクトリ名の場合は変換しない + if (m_szHost[0] == _T('.') && + (m_szHost[1] == _T('\0') || (m_szHost[1] == _T('.') && m_szHost[2] == _T('\0')))) { + strcpy((char*)m_szHuman, m_szHost); /// @warning Unicode時要修正 → 済 + + m_bCorrect = TRUE; + m_pszHumanLast = m_szHuman + strlen((const char*)m_szHuman); + m_pszHumanExt = m_pszHumanLast; + return; + } + + size_t nMax = 18; // ベース部分(ベース名と拡張子名)のバイト数 + DWORD nOption = CFileSys::GetFileOption(); + if (nOption & WINDRV_OPT_CONVERT_LENGTH) + nMax = 8; + + // ベース名部分の補正準備 + BYTE szNumber[8]; + BYTE* pNumber = NULL; + if (nCount >= 0) { + pNumber = &szNumber[8]; + for (DWORD i = 0; i < 5; i++) { // 最大5+1桁まで (ベース名先頭2バイトは必ず残す) + int n = nCount % 36; + nMax--; + pNumber--; + *pNumber = (BYTE)(n + (n < 10 ? '0' : 'A' - 10)); + nCount /= 36; + if (nCount == 0) + break; + } + nMax--; + pNumber--; + BYTE c = (BYTE)((nOption >> 24) & 0x7F); + if (c == 0) + c = XM6_HOST_FILENAME_MARK; + *pNumber = c; + } + + // 文字変換 + /// @warning Unicode未対応。いずれUnicodeの世界に飮まれた時はここで変換を行なう → 済 + BYTE szHuman[FILEPATH_MAX]; + const BYTE* pFirst = szHuman; + const BYTE* pLast; + const BYTE* pExt = NULL; + + { + strcpy(szHost, m_szHost); + const BYTE* pRead = (const BYTE*)szHost; + BYTE* pWrite = szHuman; + const BYTE* pPeriod = SeparateExt(pRead); + + for (bool bFirst = true;; bFirst = false) { + BYTE c = *pRead++; + switch (c) { + case ' ': + if (nOption & WINDRV_OPT_REDUCED_SPACE) + continue; + if (nOption & WINDRV_OPT_CONVERT_SPACE) + c = '_'; + else if (pWrite == szHuman) + continue; // 先頭の空白は無視 + break; + case '=': + case '+': + if (nOption & WINDRV_OPT_REDUCED_BADCHAR) + continue; + if (nOption & WINDRV_OPT_CONVERT_BADCHAR) + c = '_'; + break; + case '-': + if (bFirst) { + if (nOption & WINDRV_OPT_REDUCED_HYPHEN) + continue; + if (nOption & WINDRV_OPT_CONVERT_HYPHEN) + c = '_'; + break; + } + if (nOption & WINDRV_OPT_REDUCED_HYPHENS) + continue; + if (nOption & WINDRV_OPT_CONVERT_HYPHENS) + c = '_'; + break; + case '.': + if (pRead - 1 == pPeriod) { // Human68k拡張子は例外とする + pExt = pWrite; + break; + } + if (bFirst) { + if (nOption & WINDRV_OPT_REDUCED_PERIOD) + continue; + if (nOption & WINDRV_OPT_CONVERT_PERIOD) + c = '_'; + break; + } + if (nOption & WINDRV_OPT_REDUCED_PERIODS) + continue; + if (nOption & WINDRV_OPT_CONVERT_PERIODS) + c = '_'; + break; + } + *pWrite++ = c; + if (c == '\0') + break; + } + + pLast = pWrite - 1; + } + + // 拡張子補正 + if (pExt) { + // 末尾の空白を削除する + while (pExt < pLast - 1 && *(pLast - 1) == ' ') { + pLast--; + BYTE* p = (BYTE*)pLast; + *p = '\0'; + } + + // 変換後に実体がなくなった場合は削除 + if (pExt + 1 >= pLast) { + pLast = pExt; + BYTE* p = (BYTE*)pLast; + *p = '\0'; // 念のため + } + } else { + pExt = pLast; + } + + // 登場人物紹介 + // + // pFirst: 俺はリーダー。ファイル名先頭 + // pCut: 通称フェイス。最初のピリオドの出現位置 その後ベース名終端位置となる + // pSecond: よぉおまちどう。俺様こそマードック。拡張子名の開始位置。だから何。 + // pExt: B・A・バラカス。Human68k拡張子の天才だ。でも、3文字より長い名前は勘弁な。 + // 最後のピリオドの出現位置 該当しなければpLastと同じ値 + // + // ↓pFirst ↓pStop ↓pSecond ← ↓pExt + // T h i s _ i s _ a . V e r y . L o n g . F i l e n a m e . t x t \0 + // ↑pCut ← ↑pCut初期位置 ↑pLast + // + // 上記の場合、変換後は This.Long.Filename.txt となる + + // 1文字目判定 + const BYTE* pCut = pFirst; + const BYTE* pStop = pExt - nMax; // 拡張子名は最大17バイトとする(ベース名を残す) + if (pFirst < pExt) { + pCut++; // 必ず1バイトはベース名を使う + BYTE c = *pFirst; + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + pCut++; // ベース名 最小2バイト + pStop++; // 拡張子名 最大16バイト + } + } + if (pStop < pFirst) + pStop = pFirst; + + // ベース名判定 + pCut = (BYTE*)strchr((const char*)pCut, '.'); // SJIS2バイト目は必ず0x40以上なので問題ない + if (pCut == NULL) + pCut = pLast; + if ((size_t)(pCut - pFirst) > nMax) + pCut = pFirst + nMax; // 後ほどSJIS2バイト判定/補正を行なう ここで判定してはいけない + + // 拡張子名判定 + const BYTE* pSecond = pExt; + const BYTE* p; + for (p = pExt - 1; pStop < p; p--) { + if (*p == '.') + pSecond = p; // SJIS2バイト目は必ず0x40以上なので問題ない + } + + // ベース名を短縮 + size_t nExt = pExt - pSecond; // 拡張子名部分の長さ + if ((size_t)(pCut - pFirst) + nExt > nMax) + pCut = pFirst + nMax - nExt; + // 2バイト文字の途中ならさらに短縮 + for (p = pFirst; p < pCut; p++) { + BYTE c = *p; + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + p++; + if (p >= pCut) { + pCut--; + break; + } + } + } + + // 名前の結合 + BYTE* pWrite = m_szHuman; + pWrite = CopyName(pWrite, pFirst, pCut); // ベース名を転送 + if (pNumber) + pWrite = CopyName(pWrite, pNumber, &szNumber[8]); // 補正文字を転送 + pWrite = CopyName(pWrite, pSecond, pExt); // 拡張子名を転送 + m_pszHumanExt = pWrite; // 拡張子位置保存 + pWrite = CopyName(pWrite, pExt, pLast); // Human68k拡張子を転送 + m_pszHumanLast = pWrite; // 終端位置保存 + *pWrite = '\0'; + + // 変換結果の確認 + m_bCorrect = TRUE; + + // ファイル名本体が存在しなければ不合格 + if (m_pszHumanExt <= m_szHuman) + m_bCorrect = FALSE; + + // ファイル名本体が1文字以上でかつ空白で終了していれば不合格 + // ファイル名本体が8文字以上の場合、理論上は空白での終了が表現可 + // 能だが、Human68kでは正しく扱えないため、これも不合格とする + else if (m_pszHumanExt[-1] == ' ') + m_bCorrect = FALSE; + + // 変換結果が特殊ディレクトリ名と同じなら不合格 + if (m_szHuman[0] == '.' && + (m_szHuman[1] == '\0' || (m_szHuman[1] == '.' && m_szHuman[2] == '\0'))) + m_bCorrect = FALSE; +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の名称を複製 +/// +/// ファイル名部分の情報を複製し、ConvertHuman()相当の初期化動作を行なう。 +// +//--------------------------------------------------------------------------- +void CHostFilename::CopyHuman(const BYTE* szHuman) +{ + ASSERT(this); + ASSERT(szHuman); + ASSERT(strlen((const char*)szHuman) < 23); + + strcpy((char*)m_szHuman, (const char*)szHuman); + m_bCorrect = TRUE; + m_pszHumanLast = m_szHuman + strlen((const char*)m_szHuman); + m_pszHumanExt = (BYTE*)SeparateExt(m_szHuman); +} + +//--------------------------------------------------------------------------- +// +/// Human68kディレクトリエントリを設定 +/// +/// ConvertHuman()で設定済みのファイル名をディレクトリエントリに反映する。 +// +//--------------------------------------------------------------------------- +void CHostFilename::SetEntryName() +{ + ASSERT(this); + + // ファイル名設定 + BYTE* p = m_szHuman; + size_t i; + for (i = 0; i < 8; i++) { + if (p < m_pszHumanExt) + m_dirHuman.name[i] = *p++; + else + m_dirHuman.name[i] = ' '; + } + + for (i = 0; i < 10; i++) { + if (p < m_pszHumanExt) + m_dirHuman.add[i] = *p++; + else + m_dirHuman.add[i] = '\0'; + } + + if (*p == '.') + p++; + for (i = 0; i < 3; i++) { + BYTE c = *p; + if (c) + p++; + m_dirHuman.ext[i] = c; + } +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の名称が加工されたか調査 +// +//--------------------------------------------------------------------------- +BOOL CHostFilename::isReduce() const +{ + ASSERT(this); + + return strcmp((LPTSTR)m_szHost, (const char*)m_szHuman) != 0; /// @warning Unicode時要修正 → 済 +} + +//--------------------------------------------------------------------------- +// +/// Human68kディレクトリエントリの属性判定 +// +//--------------------------------------------------------------------------- +BOOL CHostFilename::CheckAttribute(DWORD nHumanAttribute) const +{ + ASSERT(this); + + BYTE nAttribute = m_dirHuman.attr; + if ((nAttribute & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0) + nAttribute |= Human68k::AT_ARCHIVE; + + return nAttribute & nHumanAttribute; +} + +//--------------------------------------------------------------------------- +// +/// Human68kファイル名から拡張子を分離 +// +//--------------------------------------------------------------------------- +const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static +{ + // ファイル名の長さを獲得 + size_t nLength = strlen((const char*)szHuman); + const BYTE* pFirst = szHuman; + const BYTE* pLast = pFirst + nLength; + + // Human68k拡張子の位置を確認 + const BYTE* pExt = (BYTE*)strrchr((const char*)pFirst, '.'); // SJIS2バイト目は必ず0x40以上なので問題ない + if (pExt == NULL) + pExt = pLast; + // ファイル名が20~22文字かつ19文字目が'.'かつ'.'で終了というパターンを特別扱いする + if (20 <= nLength && nLength <= 22 && pFirst[18] == '.' && pFirst[nLength - 1] == '.') + pExt = pFirst + 18; + // 拡張子の文字数を計算 (-1:なし 0:ピリオドだけ 1~3:Human68k拡張子 4以上:拡張子名) + size_t nExt = pLast - pExt - 1; + // '.' が文字列先頭以外に存在して、かつ1~3文字の場合のみ拡張子とみなす + if (pExt == pFirst || nExt < 1 || nExt > 3) + pExt = pLast; + + return pExt; +} + +//=========================================================================== +// +// ディレクトリエントリ パス名 +// +//=========================================================================== + +DWORD CHostPath::g_nId; ///< 識別ID生成用カウンタ + +//--------------------------------------------------------------------------- +// +/// デフォルトコンストラクタ +// +//--------------------------------------------------------------------------- +CHostPath::CHostPath() +{ + m_bRefresh = TRUE; + +#ifdef _DEBUG + // 必ず値が更新されるので初期化不要 (デバッグ時の初期動作確認用) + m_nId = 0; +#endif // _DEBUG +} + +//--------------------------------------------------------------------------- +// +/// デストラクタ final +// +//--------------------------------------------------------------------------- +CHostPath::~CHostPath() +{ + Clean(); +} + +//--------------------------------------------------------------------------- +// +/// ファイル名領域確保 +/// +/// ほとんどのケースでは、ホスト側ファイル名の長さはバッファ最大長に +/// 比べて非常に短い。さらにファイル名は大量に生成される可能性がある。 +/// そのため文字数に応じた可変長で確保する。 +// +//--------------------------------------------------------------------------- +CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static +{ + ASSERT(nLength < FILEPATH_MAX); + + size_t n = offsetof(ring_t, f) + CHostFilename::Offset() + (nLength + 1) * sizeof(TCHAR); + ring_t* p = (ring_t*)malloc(n); + ASSERT(p); + + p->r.Init(); // 榛名は大丈夫です! + + return p; +} + +//--------------------------------------------------------------------------- +// +// ファイル名領域解放 +// +//--------------------------------------------------------------------------- +void CHostPath::Free(ring_t* pRing) // static +{ + ASSERT(pRing); + + pRing->~ring_t(); + free(pRing); +} + +//--------------------------------------------------------------------------- +// +/// 再利用のための初期化 +// +//--------------------------------------------------------------------------- +void CHostPath::Clean() +{ + ASSERT(this); + + Release(); + + // 全ファイル名を解放 + ring_t* p; + while ((p = (ring_t*)m_cRing.Next()) != (ring_t*)&m_cRing) { + Free(p); + } +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の名称を直接指定する +// +//--------------------------------------------------------------------------- +void CHostPath::SetHuman(const BYTE* szHuman) +{ + ASSERT(this); + ASSERT(szHuman); + ASSERT(strlen((const char*)szHuman) < HUMAN68K_PATH_MAX); + + strcpy((char*)m_szHuman, (const char*)szHuman); +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称を直接指定する +// +//--------------------------------------------------------------------------- +void CHostPath::SetHost(const TCHAR* szHost) +{ + ASSERT(this); + ASSERT(szHost); + ASSERT(strlen(szHost) < FILEPATH_MAX); + + strcpy(m_szHost, szHost); +} + +//--------------------------------------------------------------------------- +// +/// 文字列比較 (ワイルドカード対応) +// +//--------------------------------------------------------------------------- +int CHostPath::Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast) +{ + ASSERT(pFirst); + ASSERT(pLast); + ASSERT(pBufFirst); + ASSERT(pBufLast); + + // 文字比較 + BOOL bSkip0 = FALSE; + BOOL bSkip1 = FALSE; + for (const BYTE* p = pFirst; p < pLast; p++) { + // 1文字読み込み + BYTE c = *p; + BYTE d = '\0'; + if (pBufFirst < pBufLast) + d = *pBufFirst++; + + // 比較のための文字補正 + if (bSkip0 == FALSE) { + if (bSkip1 == FALSE) { // cもdも1バイト目 + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + bSkip0 = TRUE; + } + if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // 厳密には 0x81~0x9F 0xE0~0xEF + bSkip1 = TRUE; + } + if (c == d) + continue; // 高確率で判定完了する + if ((CFileSys::GetFileOption() & WINDRV_OPT_ALPHABET) == 0) { + if ('A' <= c && c <= 'Z') + c += 'a' - 'A'; // 小文字化 + if ('A' <= d && d <= 'Z') + d += 'a' - 'A'; // 小文字化 + } + // バックスラッシュをスラッシュに統一して比較する + if (c == '\\') { + c = '/'; + } + if (d == '\\') { + d = '/'; + } + } else { // cだけが1バイト目 + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には 0x81~0x9F 0xE0~0xEF + bSkip0 = TRUE; + } + bSkip1 = FALSE; + } + } else { + if (bSkip1 == FALSE) { // dだけが1バイト目 + bSkip0 = FALSE; + if ((0x80 <= d && d <= 0x9F) || 0xE0 <= d) { // 厳密には 0x81~0x9F 0xE0~0xEF + bSkip1 = TRUE; + } + } else { // cもdも2バイト目 + bSkip0 = FALSE; + bSkip1 = FALSE; + } + } + + // 比較 + if (c == d) + continue; + if (c == '?') + continue; + return 1; + } + if (pBufFirst < pBufLast) + return 2; + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の名称を比較する +// +//--------------------------------------------------------------------------- +BOOL CHostPath::isSameHuman(const BYTE* szHuman) const +{ + ASSERT(this); + ASSERT(szHuman); + + // 文字数計算 + size_t nLength = strlen((const char*)m_szHuman); + size_t n = strlen((const char*)szHuman); + + // 文字数チェック + if (nLength != n) + return FALSE; + + // Human68kパス名の比較 + return Compare(m_szHuman, m_szHuman + nLength, szHuman, szHuman + n) == 0; +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の名称を比較する +// +//--------------------------------------------------------------------------- +BOOL CHostPath::isSameChild(const BYTE* szHuman) const +{ + ASSERT(this); + ASSERT(szHuman); + + // 文字数計算 + size_t nLength = strlen((const char*)m_szHuman); + size_t n = strlen((const char*)szHuman); + + // 文字数チェック + if (nLength < n) + return FALSE; + + // Human68kパス名の比較 + return Compare(m_szHuman, m_szHuman + n, szHuman, szHuman + n) == 0; +} + +//--------------------------------------------------------------------------- +// +/// ファイル名を検索 +/// +/// 所有するキャシュバッファの中から検索し、見つかればその名称を返す。 +/// パス名を除外しておくこと。 +/// 必ず上位で排他制御を行なうこと。 +// +//--------------------------------------------------------------------------- +const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAttribute) const +{ + ASSERT(this); + ASSERT(szHuman); + + // 文字数計算 + const BYTE* pFirst = szHuman; + size_t nLength = strlen((const char*)pFirst); + const BYTE* pLast = pFirst + nLength; + + // 所持している全てのファイル名の中から完全一致するものを検索 + const ring_t* p = (ring_t*)m_cRing.Next(); + for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { + // 属性チェック + if (p->f.CheckAttribute(nHumanAttribute) == 0) + continue; + // 文字数計算 + const BYTE* pBufFirst = p->f.GetHuman(); + const BYTE* pBufLast = p->f.GetHumanLast(); + size_t nBufLength = pBufLast - pBufFirst; + // 文字数チェック + if (nLength != nBufLength) + continue; + // ファイル名チェック + if (Compare(pFirst, pLast, pBufFirst, pBufLast) == 0) + return &p->f; + } + + return NULL; +} + +//--------------------------------------------------------------------------- +// +/// ファイル名を検索 (ワイルドカード対応) +/// +/// 所有するバッファの中から検索し、見つかればその名称を返す。 +/// パス名を除外しておくこと。 +/// 必ず上位で排他制御を行なうこと。 +// +//--------------------------------------------------------------------------- +const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const +{ + ASSERT(this); + ASSERT(szHuman); + ASSERT(pFind); + + // 検索ファイル名を本体とHuman68k拡張子に分ける + const BYTE* pFirst = szHuman; + const BYTE* pLast = pFirst + strlen((const char*)pFirst); + const BYTE* pExt = CHostFilename::SeparateExt(pFirst); + + // 開始地点へ移動 + const ring_t* p = (ring_t*)m_cRing.Next(); + if (pFind->count > 0) { + if (pFind->id == m_nId) { + // ディレクトリエントリが同一なら、前回の位置から即継続 + p = pFind->pos; + } else { + // 開始地点をディレクトリエントリ内容から検索する + DWORD n = 0; + for (;; p = (ring_t*)p->r.Next()) { + if (p == (ring_t*)&m_cRing) { + // 同一エントリが見つからなかった場合、回数から推定 (念のため) + p = (ring_t*)m_cRing.Next(); + n = 0; + for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { + if (n >= pFind->count) + break; + n++; + } + break; + } + if (p->f.isSameEntry(&pFind->entry)) { + // 同一エントリを発見 + pFind->count = n; + break; + } + n++; + } + } + } + + // ファイル検索 + for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { + pFind->count++; + + // 属性チェック + if (p->f.CheckAttribute(nHumanAttribute) == 0) + continue; + + // ファイル名を本体とHuman68k拡張子に分ける + const BYTE* pBufFirst = p->f.GetHuman(); + const BYTE* pBufLast = p->f.GetHumanLast(); + const BYTE* pBufExt = p->f.GetHumanExt(); + + // 本体比較 + if (Compare(pFirst, pExt, pBufFirst, pBufExt)) + continue; + + // Human68k拡張子比較 + // 拡張子.???の場合は、Human68k拡張子のピリオドなしにもマッチさせる + if (strcmp((const char*)pExt, ".???") == 0 || + Compare(pExt, pLast, pBufExt, pBufLast) == 0) { + // 次の候補のディレクトリエントリ内容を記録 + const ring_t* pNext = (ring_t*)p->r.Next(); + pFind->id = m_nId; + pFind->pos = pNext; + if (pNext != (ring_t*)&m_cRing) + memcpy(&pFind->entry, pNext->f.GetEntry(), sizeof(pFind->entry)); + else + memset(&pFind->entry, 0, sizeof(pFind->entry)); + return &p->f; + } + } + + pFind->id = m_nId; + pFind->pos = p; + memset(&pFind->entry, 0, sizeof(pFind->entry)); + return NULL; +} + +//--------------------------------------------------------------------------- +// +/// ファイル変更が行なわれたか確認 +// +//--------------------------------------------------------------------------- +BOOL CHostPath::isRefresh() +{ + ASSERT(this); + + return m_bRefresh; +} + +#ifdef BAREMETAL +//--------------------------------------------------------------------------- +// +/// scandirエミュレーション +// +//--------------------------------------------------------------------------- +struct dirent { + char d_name[_MAX_FNAME]; +}; + +int scandir(const char *dirname, +dirent ***ret_namelist, + int(*select)(const dirent *), + int(*compar)(const dirent **, const dirent **)) +{ + FRESULT fr; + DIR dir; + FILINFO fno; + char dirpath[256]; + int len; + dirent went; + int used; + int allocated; + dirent **namelist = NULL; + int i; + dirent *ent; + + // NULLチェック + strcpy(dirpath, dirname); + if (dirpath[0] == '\0') { + return -1; + } + + // '/'はOKだがそれ以外で最後が'/'だとディレクトリと認識されない) + if (dirpath[0] != '/' || dirpath[1] != '\0') { + len = strlen(dirpath); + if (dirpath[len - 1] == '/') { + dirpath[len - 1] = '\0'; + } + } + + // ディレクトリオープン + fr = f_opendir(&dir, dirpath); + if (fr != FR_OK) { + return -1; + } + + // リストを初期値で確保(とりあえず32) + used = 0; + allocated = 32; + namelist = (dirent **)malloc(allocated * sizeof(dirent *)); + if (!namelist) { + goto error; + } + + // 配下のファイルまたはディレクトリを処理 + i = 0; + while (TRUE) { + if (i == 0) { + // "."をFILINFOに見せかけて追加 + strcpy(fno.fname, "."); + i++; + } else if (i == 1) { + // ".."をFILINFOに見せかけて追加 + strcpy(fno.fname, ".."); + i++; + } else if (f_readdir(&dir, &fno) != FR_OK) { + break; + } + + // このケースがあるか不明 + if (fno.fname[0] == 0) { + break; + } + + // direntに見せかける + strcpy(went.d_name, fno.fname); + + // 対象外のフィルタ処理 + if (select != NULL && !select(&went)) { + continue; + } + + // ファイル名の長さに調整したdirentの領域を確保 + len = offsetof(dirent, d_name) + strlen(fno.fname) + 1; + if ((ent = (dirent *)malloc(len)) == NULL) { + goto error; + } + + // ワーク用direntから返却用にコピー + memcpy(ent, &went, len); + + // 使用量が越えそうならリストを再確保 + if (used >= allocated) { + allocated *= 2; + namelist = (dirent **)realloc(namelist, allocated * sizeof(dirent *)); + if (!namelist) { + goto error; + } + } + + // リストに追加 + namelist[used++] = ent; + } + + // ディレクトリクローズ + f_closedir(&dir); + + // ソート処理 + if (compar) { + qsort( + namelist, used, sizeof(dirent *), + (int(*)(const void *, const void *)) compar); + } + + // リストとエントリ数を返却 + *ret_namelist = namelist; + return used; + +error: + // ディレクトリクローズ + f_closedir(&dir); + + // 途中まで確保したバッファをクローズ + if (namelist) { + while (used > 0) { + free(namelist[used - 1]); + used--; + } + free(namelist); + } + return -1; +} +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +/// ASCIIソート関数 +// +//--------------------------------------------------------------------------- +int AsciiSort(const dirent **a, const dirent **b) +{ + return strcmp((*a)->d_name, (*b)->d_name); +} + +//--------------------------------------------------------------------------- +// +/// ファイル再構成 +/// +/// ここで初めて、ホスト側のファイルシステムの観測が行なわれる。 +/// 必ず上位で排他制御を行なうこと。 +// +//--------------------------------------------------------------------------- +void CHostPath::Refresh() +{ + ASSERT(this); + ASSERT(strlen(m_szHost) + 22 < FILEPATH_MAX); + + // タイムスタンプ保存 + Backup(); + + TCHAR szPath[FILEPATH_MAX]; + strcpy(szPath, m_szHost); + + // 更新フラグ変更 + m_bRefresh = FALSE; + + // 以前のキャッシュ内容を保存 + CRing cRingBackup; + m_cRing.InsertRing(&cRingBackup); + + // ファイル名登録 + /// @todo ファイル重複処理をホスト側APIを経由せずに全て自前で処理する。 + BOOL bUpdate = FALSE; + struct dirent **pd = NULL; + int nument = 0; + int maxent = XM6_HOST_DIRENTRY_FILE_MAX; + for (int i = 0; i < maxent; i++) { + TCHAR szFilename[FILEPATH_MAX]; + if (pd == NULL) { + nument = scandir(S2U(szPath), &pd, NULL, AsciiSort); + if (nument == -1) { + pd = NULL; + break; + } + maxent = nument; + } + + // 最上位ディレクトリならカレントとパレントを対象外とする + struct dirent* pe = pd[i]; + if (m_szHuman[0] == '/' && m_szHuman[1] == 0) { + if (strcmp(pe->d_name, ".") == 0 || strcmp(pe->d_name, "..") == 0) { + continue; + } + } + + // ファイル名を獲得 + strcpy(szFilename, U2S(pe->d_name)); + + // ファイル名領域確保 + ring_t* pRing = Alloc(strlen(szFilename)); + CHostFilename* pFilename = &pRing->f; + pFilename->SetHost(szFilename); + + // 以前のキャッシュ内容に該当するファイル名があればそのHuman68k名称を優先する + ring_t* pCache = (ring_t*)cRingBackup.Next(); + for (;;) { + if (pCache == (ring_t*)&cRingBackup) { + pCache = NULL; // 該当するエントリなし + bUpdate = TRUE; // 新規エントリと確定 + pFilename->ConvertHuman(); + break; + } + if (strcmp(pFilename->GetHost(), pCache->f.GetHost()) == 0) { + pFilename->CopyHuman(pCache->f.GetHuman()); // Human68k名称のコピー + break; + } + pCache = (ring_t*)pCache->r.Next(); + } + + // 新規エントリの場合はファイル名重複をチェックする + // ホスト側のファイル名から変更があったか、Human68kで表現できないファイル名の場合は + // 以下のチェックを全てパスするファイル名を新たに生成する + // ・正しいファイル名であること + // ・過去のエントリに同名のものが存在しないこと + // ・同名の実ファイル名が存在しないこと + if (pFilename->isReduce() || !pFilename->isCorrect()) { // ファイル名変更が必要か確認 + for (DWORD n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) { + // 正しいファイル名かどうか確認 + if (pFilename->isCorrect()) { + // 過去のエントリと一致するか確認 + const CHostFilename* pCheck = FindFilename(pFilename->GetHuman()); + if (pCheck == NULL) { + // 一致するものがなければ、実ファイルが存在するか確認 + strcpy(szPath, m_szHost); + strcat(szPath, (const char*)pFilename->GetHuman()); /// @warning Unicode時要修正 → 済 +#ifndef BAREMETAL + struct stat sb; + if (stat(S2U(szPath), &sb)) +#else + if (f_stat(S2U(szPath), NULL) != FR_OK) +#endif // BAREMETAL + break; // 利用可能パターンを発見 + } + } + // 新しい名前を生成 + pFilename->ConvertHuman(n); + } + } + + // ディレクトリエントリ名称 + pFilename->SetEntryName(); + + // 情報取得 + strcpy(szPath, m_szHost); + strcat(szPath, U2S(pe->d_name)); + +#ifndef BAREMETAL + struct stat sb; + if (stat(S2U(szPath), &sb)) + continue; + + // 属性 + BYTE nHumanAttribute = Human68k::AT_ARCHIVE; + if (S_ISDIR(sb.st_mode)) + nHumanAttribute = Human68k::AT_DIRECTORY; + if ((sb.st_mode & 0200) == 0) + nHumanAttribute |= Human68k::AT_READONLY; + pFilename->SetEntryAttribute(nHumanAttribute); + + // サイズ + DWORD nHumanSize = (DWORD)sb.st_size; + pFilename->SetEntrySize(nHumanSize); + + // 日付時刻 + WORD nHumanDate = 0; + WORD nHumanTime = 0; + struct tm* pt = localtime(&sb.st_mtime); + if (pt) { + nHumanDate = (WORD)(((pt->tm_year - 80) << 9) | ((pt->tm_mon + 1) << 5) | pt->tm_mday); + nHumanTime = (WORD)((pt->tm_hour << 11) | (pt->tm_min << 5) | (pt->tm_sec >> 1)); + } + pFilename->SetEntryDate(nHumanDate); + pFilename->SetEntryTime(nHumanTime); +#else + FILINFO fno; + if (f_stat(S2U(szPath), &fno) != FR_OK) + continue; + + // 属性 + BYTE nHumanAttribute = Human68k::AT_ARCHIVE; + if (fno.fattrib & AM_DIR) + nHumanAttribute = Human68k::AT_DIRECTORY; + if (fno.fattrib & AM_RDO) + nHumanAttribute |= Human68k::AT_READONLY; + pFilename->SetEntryAttribute(nHumanAttribute); + + // サイズ + DWORD nHumanSize = (DWORD)fno.fsize; + pFilename->SetEntrySize(nHumanSize); + + // 日付時刻 + pFilename->SetEntryDate(fno.fdate); + pFilename->SetEntryTime(fno.ftime); +#endif // BAREMETAL + + // クラスタ番号設定 + pFilename->SetEntryCluster(0); + + // 以前のキャッシュ内容と比較 + if (pCache) { + if (pCache->f.isSameEntry(pFilename->GetEntry())) { + Free(pRing); // 今回作成したエントリは破棄し + pRing = pCache; // 以前のキャッシュ内容を使う + } else { + Free(pCache); // 次回の検索対象から除外 + bUpdate = TRUE; // 一致しなければ更新あり + } + } + + // リング末尾へ追加 + pRing->r.InsertTail(&m_cRing); + } + + // ディレクトリエントリを解放 + if (pd) { + for (int i = 0; i < nument; i++) { + free(pd[i]); + } + free(pd); + } + + // 残存するキャッシュ内容を削除 + ring_t* p; + while ((p = (ring_t*)cRingBackup.Next()) != (ring_t*)&cRingBackup) { + bUpdate = TRUE; // 削除によってエントリ数の減少が判明 + Free(p); + } + + // 更新が行なわれたら識別IDを変更 + if (bUpdate) + m_nId = ++g_nId; + // ASSERT(m_nId); +} + +//--------------------------------------------------------------------------- +// +/// ホスト側のタイムスタンプを保存 +// +//--------------------------------------------------------------------------- +void CHostPath::Backup() +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT(m_szHost); + ASSERT(strlen(m_szHost) < FILEPATH_MAX); + + TCHAR szPath[FILEPATH_MAX]; + strcpy(szPath, m_szHost); + size_t len = strlen(szPath); + + m_tBackup = 0; + if (len > 1) { // ルートディレクトリの場合は何もしない + len--; + ASSERT(szPath[len] == _T('/')); + szPath[len] = _T('\0'); + struct stat sb; + if (stat(S2U(szPath), &sb) == 0) + m_tBackup = sb.st_mtime; + } +#else + FILINFO fno; + + ASSERT(this); + ASSERT(m_szHost); + ASSERT(strlen(m_szHost) < FILEPATH_MAX); + + TCHAR szPath[FILEPATH_MAX]; + strcpy(szPath, m_szHost); + size_t len = strlen(szPath); + + m_tBackupD = 0; + m_tBackupT = 0; + if (len > 1) { // ルートディレクトリの場合は何もしない + len--; + ASSERT(szPath[len] == _T('/')); + szPath[len] = _T('\0'); + if (f_stat(S2U(szPath), &fno) == FR_OK) { + m_tBackupD = fno.fdate; + m_tBackupT = fno.ftime; + } + } +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ホスト側のタイムスタンプを復元 +// +//--------------------------------------------------------------------------- +void CHostPath::Restore() const +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT(m_szHost); + ASSERT(strlen(m_szHost) < FILEPATH_MAX); + + TCHAR szPath[FILEPATH_MAX]; + strcpy(szPath, m_szHost); + size_t len = strlen(szPath); + + if (m_tBackup) { + ASSERT(len); + len--; + ASSERT(szPath[len] == _T('/')); + szPath[len] = _T('\0'); + + struct utimbuf ut; + ut.actime = m_tBackup; + ut.modtime = m_tBackup; + utime(szPath, &ut); + } +#else + FILINFO fno; + + ASSERT(this); + ASSERT(m_szHost); + ASSERT(strlen(m_szHost) < FILEPATH_MAX); + + TCHAR szPath[FILEPATH_MAX]; + strcpy(szPath, m_szHost); + size_t len = strlen(szPath); + + if (m_tBackupD) { + ASSERT(len); + len--; + ASSERT(szPath[len] == _T('/')); + szPath[len] = _T('\0'); + + fno.fdate = m_tBackupD; + fno.ftime = m_tBackupT; + f_utime(szPath, &fno); + } +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// 更新 +// +//--------------------------------------------------------------------------- +void CHostPath::Release() +{ + ASSERT(this); + + m_bRefresh = TRUE; +} + +//=========================================================================== +// +// ディレクトリエントリ管理 +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +/// デフォルトコンストラクタ +// +//--------------------------------------------------------------------------- +CHostEntry::CHostEntry() +{ + for (size_t n = 0; n < DriveMax; n++) { + m_pDrv[n] = NULL; + } + + m_nTimeout = 0; +} + +//--------------------------------------------------------------------------- +// +/// デストラクタ final +// +//--------------------------------------------------------------------------- +CHostEntry::~CHostEntry() +{ + Clean(); + +#ifdef _DEBUG + // オブジェクト確認 + for (size_t n = 0; n < DriveMax; n++) { + ASSERT(m_pDrv[n] == NULL); + } +#endif // _DEBUG +} + +//--------------------------------------------------------------------------- +// +/// 初期化 (ドライバ組込み時) +// +//--------------------------------------------------------------------------- +void CHostEntry::Init() +{ + ASSERT(this); + +#ifdef _DEBUG + // オブジェクト確認 + for (size_t n = 0; n < DriveMax; n++) { + ASSERT(m_pDrv[n] == NULL); + } +#endif // _DEBUG +} + +//--------------------------------------------------------------------------- +// +/// 解放 (起動・リセット時) +// +//--------------------------------------------------------------------------- +void CHostEntry::Clean() +{ + ASSERT(this); + + // オブジェクト削除 + for (size_t n = 0; n < DriveMax; n++) { + delete m_pDrv[n]; + m_pDrv[n] = NULL; + } +} + +//--------------------------------------------------------------------------- +// +/// 全てのキャッシュを更新する +// +//--------------------------------------------------------------------------- +void CHostEntry::CleanCache() +{ + ASSERT(this); + + for (size_t i = 0; i < DriveMax; i++) { + if (m_pDrv[i]) + m_pDrv[i]->CleanCache(); + } + + CHostPath::InitId(); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたユニットのキャッシュを更新する +// +//--------------------------------------------------------------------------- +void CHostEntry::CleanCache(DWORD nUnit) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + m_pDrv[nUnit]->CleanCache(); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパスのキャッシュを更新する +// +//--------------------------------------------------------------------------- +void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + m_pDrv[nUnit]->CleanCache(szHumanPath); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパス以下のキャッシュを全て更新する +// +//--------------------------------------------------------------------------- +void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + m_pDrv[nUnit]->CleanCacheChild(szHumanPath); +} + +//--------------------------------------------------------------------------- +// +/// 指定されたパスのキャッシュを削除する +// +//--------------------------------------------------------------------------- +void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + m_pDrv[nUnit]->DeleteCache(szHumanPath); +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) +{ + ASSERT(this); + ASSERT(pFiles); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->Find(pFiles); +} + +void CHostEntry::ShellNotify(DWORD, const TCHAR*) {} + +//--------------------------------------------------------------------------- +// +/// ドライブ設定 +// +//--------------------------------------------------------------------------- +void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit] == NULL); + + m_pDrv[nUnit] = pDrv; +} + +//--------------------------------------------------------------------------- +// +/// 書き込み禁止か? +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::isWriteProtect(DWORD nUnit) const +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->isWriteProtect(); +} + +//--------------------------------------------------------------------------- +// +/// アクセス可能か? +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::isEnable(DWORD nUnit) const +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->isEnable(); +} + +//--------------------------------------------------------------------------- +// +/// メディアチェック +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::isMediaOffline(DWORD nUnit) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->isMediaOffline(); +} + +//--------------------------------------------------------------------------- +// +/// メディアバイトの取得 +// +//--------------------------------------------------------------------------- +BYTE CHostEntry::GetMediaByte(DWORD nUnit) const +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->GetMediaByte(); +} + +//--------------------------------------------------------------------------- +// +/// ドライブ状態の取得 +// +//--------------------------------------------------------------------------- +DWORD CHostEntry::GetStatus(DWORD nUnit) const +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->GetStatus(); +} + +//--------------------------------------------------------------------------- +// +/// メディア交換チェック +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::CheckMedia(DWORD nUnit) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->CheckMedia(); +} + +//--------------------------------------------------------------------------- +// +/// イジェクト +// +//--------------------------------------------------------------------------- +void CHostEntry::Eject(DWORD nUnit) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + m_pDrv[nUnit]->Eject(); +} + +//--------------------------------------------------------------------------- +// +/// ボリュームラベルの取得 +// +//--------------------------------------------------------------------------- +void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + m_pDrv[nUnit]->GetVolume(szLabel); +} + +//--------------------------------------------------------------------------- +// +/// キャッシュからボリュームラベルを取得 +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->GetVolumeCache(szLabel); +} + +//--------------------------------------------------------------------------- +// +/// 容量の取得 +// +//--------------------------------------------------------------------------- +DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->GetCapacity(pCapacity); +} + +//--------------------------------------------------------------------------- +// +/// キャッシュからクラスタサイズを取得 +// +//--------------------------------------------------------------------------- +BOOL CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const +{ + ASSERT(this); + ASSERT(nUnit < DriveMax); + ASSERT(m_pDrv[nUnit]); + + return m_pDrv[nUnit]->GetCapacityCache(pCapacity); +} + +//--------------------------------------------------------------------------- +// +/// Human68kフルパス名から先頭の要素を分離・コピー +/// +/// Human68kフルパス名の先頭の要素をパス区切り文字を除外して取得する。 +/// 書き込み先バッファは23バイト必要。 +/// Human68kパスは必ず/で開始すること。 +/// 途中/が2つ以上連続して出現した場合はエラーとする。 +/// 文字列終端が/だけの場合は空の文字列として処理し、エラーにはしない。 +// +//--------------------------------------------------------------------------- +const BYTE* CHostDrv::SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer) // static +{ + ASSERT(szHuman); + ASSERT(szBuffer); + + const size_t nMax = 22; + const BYTE* p = szHuman; + + BYTE c = *p++; // 読み込み + if (c != '/' && c != '\\') + return NULL; // エラー: 不正なパス名 + + // ファイルいっこいれる + size_t i = 0; + for (;;) { + c = *p; // 読み込み + if (c == '\0') + break; // 文字列終端なら終了 (終端位置を返す) + if (c == '/' || c == '\\') { + if (i == 0) + return NULL; // エラー: パス区切り文字が連続している + break; // パスの区切りを読んだら終了 (文字の位置を返す) + } + p++; + + if (i >= nMax) + return NULL; // エラー: 1バイト目がバッファ終端にかかる + szBuffer[i++] = c; // 書き込み + + if ((0x80 <= c && c <= 0x9F) || 0xE0 <= c) { // 厳密には0x81~0x9Fと0xE0~0xEF + c = *p++; // 読み込み + if (c < 0x40) + return NULL; // エラー: 不正なSJIS2バイト目 + + if (i >= nMax) + return NULL; // エラー: 2バイト目がバッファ終端にかかる + szBuffer[i++] = c; // 書き込み + } + } + szBuffer[i] = '\0'; // 書き込み + + return p; +} + +//=========================================================================== +// +// ファイル検索処理 +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +/// 初期化 +// +//--------------------------------------------------------------------------- +void CHostFiles::Init() +{ + ASSERT(this); +} + +//--------------------------------------------------------------------------- +// +/// パス名・ファイル名を内部で生成 +// +//--------------------------------------------------------------------------- +void CHostFiles::SetPath(const Human68k::namests_t* pNamests) +{ + ASSERT(this); + ASSERT(pNamests); + + pNamests->GetCopyPath(m_szHumanPath); + pNamests->GetCopyFilename(m_szHumanFilename); + m_nHumanWildcard = 0; + m_nHumanAttribute = Human68k::AT_ARCHIVE; + m_findNext.Clear(); +} + +//--------------------------------------------------------------------------- +// +/// Human68k側でファイルを検索しホスト側の情報を生成 +// +//--------------------------------------------------------------------------- +BOOL CHostFiles::Find(DWORD nUnit, CHostEntry* pEntry) +{ + ASSERT(this); + ASSERT(pEntry); + + return pEntry->Find(nUnit, this); +} + +//--------------------------------------------------------------------------- +// +/// ファイル名検索 +// +//--------------------------------------------------------------------------- +const CHostFilename* CHostFiles::Find(CHostPath* pPath) +{ + ASSERT(this); + ASSERT(pPath); + + if (m_nHumanWildcard) + return pPath->FindFilenameWildcard(m_szHumanFilename, m_nHumanAttribute, &m_findNext); + + return pPath->FindFilename(m_szHumanFilename, m_nHumanAttribute); +} + +//--------------------------------------------------------------------------- +// +/// Human68k側の検索結果保存 +// +//--------------------------------------------------------------------------- +void CHostFiles::SetEntry(const CHostFilename* pFilename) +{ + ASSERT(this); + ASSERT(pFilename); + + // Human68kディレクトリエントリ保存 + memcpy(&m_dirHuman, pFilename->GetEntry(), sizeof(m_dirHuman)); + + // Human68kファイル名保存 + strcpy((char*)m_szHumanResult, (const char*)pFilename->GetHuman()); +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称を設定 +// +//--------------------------------------------------------------------------- +void CHostFiles::SetResult(const TCHAR* szPath) +{ + ASSERT(this); + ASSERT(szPath); + ASSERT(strlen(szPath) < FILEPATH_MAX); + + strcpy(m_szHostResult, szPath); +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称にファイル名を追加 +// +//--------------------------------------------------------------------------- +void CHostFiles::AddResult(const TCHAR* szPath) +{ + ASSERT(this); + ASSERT(szPath); + ASSERT(strlen(m_szHostResult) + strlen(szPath) < FILEPATH_MAX); + + strcat(m_szHostResult, szPath); +} + +//--------------------------------------------------------------------------- +// +/// ホスト側の名称にHuman68kの新規ファイル名を追加 +// +//--------------------------------------------------------------------------- +void CHostFiles::AddFilename() +{ + ASSERT(this); + ASSERT(strlen(m_szHostResult) + strlen((const char*)m_szHumanFilename) < FILEPATH_MAX); + /// @warning Unicode未対応。いずれUnicodeの世界に飮まれた時はここで変換を行なう → 済 + strncat(m_szHostResult, (const char*)m_szHumanFilename, ARRAY_SIZE(m_szHumanFilename)); +} + +//=========================================================================== +// +// ファイル検索領域 マネージャ +// +//=========================================================================== + +#ifdef _DEBUG +//--------------------------------------------------------------------------- +// +/// デストラクタ final +// +//--------------------------------------------------------------------------- +CHostFilesManager::~CHostFilesManager() +{ + // 実体が存在しないことを確認 (念のため) + ASSERT(m_cRing.Next() == &m_cRing); + ASSERT(m_cRing.Prev() == &m_cRing); +} +#endif // _DEBUG + +//--------------------------------------------------------------------------- +// +/// 初期化 (ドライバ組込み時) +// +//--------------------------------------------------------------------------- +void CHostFilesManager::Init() +{ + ASSERT(this); + + // 実体が存在しないことを確認 (念のため) + ASSERT(m_cRing.Next() == &m_cRing); + ASSERT(m_cRing.Prev() == &m_cRing); + + // メモリ確保 + for (DWORD i = 0; i < XM6_HOST_FILES_MAX; i++) { + ring_t* p = new ring_t; + ASSERT(p); + p->r.Insert(&m_cRing); + } +} + +//--------------------------------------------------------------------------- +// +/// 解放 (起動・リセット時) +// +//--------------------------------------------------------------------------- +void CHostFilesManager::Clean() +{ + ASSERT(this); + + // メモリ解放 + CRing* p; + while ((p = m_cRing.Next()) != &m_cRing) { + delete (ring_t*)p; + } +} + +//--------------------------------------------------------------------------- +// +/// 確保 +// +//--------------------------------------------------------------------------- +CHostFiles* CHostFilesManager::Alloc(DWORD nKey) +{ + ASSERT(this); + ASSERT(nKey); + + // 末尾から選択 + ring_t* p = (ring_t*)m_cRing.Prev(); + + // リング先頭へ移動 + p->r.Insert(&m_cRing); + + // キーを設定 + p->f.SetKey(nKey); + + return &p->f; +} + +//--------------------------------------------------------------------------- +// +/// 検索 +// +//--------------------------------------------------------------------------- +CHostFiles* CHostFilesManager::Search(DWORD nKey) +{ + ASSERT(this); + // ASSERT(nKey); // DPB破損により検索キーが0になることもある + + // 該当するオブジェクトを検索 + ring_t* p = (ring_t*)m_cRing.Next(); + for (; p != (ring_t*)&m_cRing; p = (ring_t*)p->r.Next()) { + if (p->f.isSameKey(nKey)) { + // リング先頭へ移動 + p->r.Insert(&m_cRing); + return &p->f; + } + } + + return NULL; +} + +//--------------------------------------------------------------------------- +// +/// 解放 +// +//--------------------------------------------------------------------------- +void CHostFilesManager::Free(CHostFiles* pFiles) +{ + ASSERT(this); + ASSERT(pFiles); + + // 解放 + pFiles->SetKey(0); + pFiles->Init(); + + // リング末尾へ移動 + ring_t* p = (ring_t*)((size_t)pFiles - offsetof(ring_t, f)); + p->r.InsertTail(&m_cRing); +} + +//=========================================================================== +// +// FCB処理 +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +/// 初期化 +// +//--------------------------------------------------------------------------- +void CHostFcb::Init() +{ + ASSERT(this); + + m_bUpdate = FALSE; +#ifndef BAREMETAL + m_pFile = NULL; +#else + memset(&m_File, 0x00, sizeof(FIL)); +#endif +} + +//--------------------------------------------------------------------------- +// +/// ファイルオープンモードを設定 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::SetMode(DWORD nHumanMode) +{ + ASSERT(this); + +#ifndef BAREMETAL + switch (nHumanMode & Human68k::OP_MASK) { + case Human68k::OP_READ: + m_pszMode = "rb"; + break; + case Human68k::OP_WRITE: + m_pszMode = "wb"; + break; + case Human68k::OP_FULL: + m_pszMode = "r+b"; + break; + default: + return FALSE; + } +#else + switch (nHumanMode & Human68k::OP_MASK) { + case Human68k::OP_READ: + m_Mode = FA_READ; + break; + case Human68k::OP_WRITE: + m_Mode = FA_WRITE; + break; + case Human68k::OP_FULL: + m_Mode = FA_WRITE | FA_READ; + break; + default: + return FALSE; + } +#endif // BAREMETAL + + m_bFlag = (nHumanMode & Human68k::OP_SPECIAL) != 0; + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +/// ファイル名を設定 +// +//--------------------------------------------------------------------------- +void CHostFcb::SetFilename(const TCHAR* szFilename) +{ + ASSERT(this); + ASSERT(szFilename); + ASSERT(strlen(szFilename) < FILEPATH_MAX); + + strcpy(m_szFilename, szFilename); +} + +//--------------------------------------------------------------------------- +// +/// Human68kパス名を設定 +// +//--------------------------------------------------------------------------- +void CHostFcb::SetHumanPath(const BYTE* szHumanPath) +{ + ASSERT(this); + ASSERT(szHumanPath); + ASSERT(strlen((const char*)szHumanPath) < HUMAN68K_PATH_MAX); + + strcpy((char*)m_szHumanPath, (const char*)szHumanPath); +} + +//--------------------------------------------------------------------------- +// +/// ファイル作成 +/// +/// エラーの時はFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT((nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0); + ASSERT(strlen(m_szFilename) > 0); + ASSERT(m_pFile == NULL); + + // 重複チェック + if (bForce == FALSE) { + struct stat sb; + if (stat(S2U(m_szFilename), &sb) == 0) + return FALSE; + } + + // ファイル作成 + m_pFile = fopen(S2U(m_szFilename), "w+b"); /// @warning 理想動作は属性ごと上書き + + return m_pFile != NULL; +#else + FRESULT fr; + + ASSERT(this); + ASSERT((nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0); + ASSERT(strlen(m_szFilename) > 0); + + // 重複チェック + if (bForce == FALSE) { + if (f_stat(S2U(m_szFilename), NULL) == FR_OK) + return FALSE; + } + + // RPIのベアメタルではRTCが無いのでHuman側の時刻を反映させる + DWORD nHumanTime = ((DWORD)pFcb->date) << 16 | ((DWORD)pFcb->time); + set_fattime(nHumanTime); + + // ファイル作成 + fr = f_open(&m_File, S2U(m_szFilename), FA_CREATE_ALWAYS | FA_WRITE | FA_READ); + /// @warning 理想動作は属性ごと上書き + + return fr == FR_OK; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイルオープン +/// +/// エラーの時はFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::Open() +{ +#ifndef BAREMETAL + struct stat st; + + ASSERT(this); + ASSERT(strlen(m_szFilename) > 0); + + // ディレクトリなら失敗 + if (stat(S2U(m_szFilename), &st) == 0) { + if ((st.st_mode & S_IFMT) == S_IFDIR) { + return FALSE || m_bFlag; + } + } + + // ファイルオープン + if (m_pFile == NULL) + m_pFile = fopen(S2U(m_szFilename), m_pszMode); + + return m_pFile != NULL || m_bFlag; +#else + FRESULT fr; + FILINFO fno; + + ASSERT(this); + ASSERT(strlen(m_szFilename) > 0); + + // ディレクトリなら失敗 + if (f_stat(S2U(m_szFilename), &fno) == FR_OK) { + if (fno.fattrib & AM_DIR) { + return FALSE || m_bFlag; + } + } + + // ファイルオープン + fr = FR_DISK_ERR; + if (m_File.obj.fs == NULL) + fr = f_open(&m_File, S2U(m_szFilename), m_Mode); + + return fr == FR_OK || m_bFlag; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイルシーク +/// +/// エラーの時はFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::Rewind(DWORD nOffset) +{ + ASSERT(this); +#ifndef BAREMETAL + ASSERT(m_pFile); + + if (fseek(m_pFile, nOffset, SEEK_SET)) + return FALSE; + + return ftell(m_pFile) != -1L; +#else + if (f_lseek(&m_File, nOffset)) + return FALSE; + + return f_tell(&m_File) != (DWORD)-1L; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイル読み込み +/// +/// 0バイト読み込みでも正常動作とする。 +/// エラーの時は-1を返す。 +// +//--------------------------------------------------------------------------- +DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize) +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT(pBuffer); + ASSERT(m_pFile); + + size_t nResult = fread(pBuffer, sizeof(BYTE), nSize, m_pFile); + if (ferror(m_pFile)) + nResult = (size_t)-1; + + return (DWORD)nResult; +#else + FRESULT fr; + UINT nResult; + + ASSERT(this); + ASSERT(pBuffer); + + fr = f_read(&m_File, pBuffer, nSize, &nResult); + if (fr != FR_OK) + nResult = (UINT)-1; + + return (DWORD)nResult; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイル書き込み +/// +/// 0バイト書き込みでも正常動作とする。 +/// エラーの時は-1を返す。 +// +//--------------------------------------------------------------------------- +DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize) +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT(pBuffer); + ASSERT(m_pFile); + + size_t nResult = fwrite(pBuffer, sizeof(BYTE), nSize, m_pFile); + if (ferror(m_pFile)) + nResult = (size_t)-1; + + return (DWORD)nResult; +#else + FRESULT fr; + UINT nResult; + + ASSERT(this); + ASSERT(pBuffer); + + fr = f_write(&m_File, pBuffer, nSize, &nResult); + if (fr != FR_OK) + nResult = (UINT)-1; + + return (DWORD)nResult; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイル切り詰め +/// +/// エラーの時はFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::Truncate() +{ + ASSERT(this); +#ifndef BAREMETAL + ASSERT(m_pFile); + + return ftruncate(fileno(m_pFile), ftell(m_pFile)) == 0; +#else + return f_truncate(&m_File) == FR_OK; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイルシーク +/// +/// エラーの時は-1を返す。 +// +//--------------------------------------------------------------------------- +DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT(nHumanSeek == Human68k::SK_BEGIN || + nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END); + ASSERT(m_pFile); + + int nSeek; + switch (nHumanSeek) { + case Human68k::SK_BEGIN: + nSeek = SEEK_SET; + break; + case Human68k::SK_CURRENT: + nSeek = SEEK_CUR; + break; + // case SK_END: + default: + nSeek = SEEK_END; + break; + } + if (fseek(m_pFile, nOffset, nSeek)) + return (DWORD)-1; + + return (DWORD)ftell(m_pFile); +#else + FRESULT fr; + + ASSERT(this); + ASSERT(nHumanSeek == Human68k::SK_BEGIN || + nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END); + + switch (nHumanSeek) { + case Human68k::SK_BEGIN: + fr = f_lseek(&m_File, nOffset); + break; + case Human68k::SK_CURRENT: + fr = f_lseek(&m_File, f_tell(&m_File) + nOffset); + break; + // case SK_END: + default: + fr = f_lseek(&m_File, f_size(&m_File) + nOffset); + break; + } + if (fr != FR_OK) + return (DWORD)-1; + + return (DWORD)f_tell(&m_File); +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイル時刻設定 +/// +/// エラーの時はFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::TimeStamp(DWORD nHumanTime) +{ +#ifndef BAREMETAL + ASSERT(this); + ASSERT(m_pFile || m_bFlag); + + struct tm t = { 0 }; + t.tm_year = (nHumanTime >> 25) + 80; + t.tm_mon = ((nHumanTime >> 21) - 1) & 15; + t.tm_mday = (nHumanTime >> 16) & 31; + t.tm_hour = (nHumanTime >> 11) & 31; + t.tm_min = (nHumanTime >> 5) & 63; + t.tm_sec = (nHumanTime & 31) << 1; + time_t ti = mktime(&t); + if (ti == (time_t)-1) + return FALSE; + struct utimbuf ut; + ut.actime = ti; + ut.modtime = ti; + + // クローズ時に更新時刻が上書きされるのを防止するため + // タイムスタンプの更新前にフラッシュして同期させる + fflush(m_pFile); + + return utime(S2U(m_szFilename), &ut) == 0 || m_bFlag; +#else + FILINFO fno; + + ASSERT(this); + ASSERT(m_bFlag); + + // クローズ時に更新時刻が上書きされるのを防止するため + // タイムスタンプの更新前にフラッシュして同期させる + f_sync(&m_File); + + fno.fdate = (WORD)(nHumanTime >> 16); + fno.ftime = (WORD)nHumanTime; + return f_utime(S2U(m_szFilename), &fno) == FR_OK || m_bFlag; +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +/// ファイルクローズ +/// +/// エラーの時はFALSEを返す。 +// +//--------------------------------------------------------------------------- +BOOL CHostFcb::Close() +{ + ASSERT(this); + + BOOL bResult = TRUE; + + // ファイルクローズ + // Close→Free(内部で再度Close)という流れもあるので必ず初期化すること。 +#ifndef BAREMETAL + if (m_pFile) { + fclose(m_pFile); + m_pFile = NULL; + } +#else + f_close(&m_File); +#endif // BAREMETAL + + return bResult; +} + +//=========================================================================== +// +// FCB処理 マネージャ +// +//=========================================================================== + +#ifdef _DEBUG +//--------------------------------------------------------------------------- +// +/// デストラクタ final +// +//--------------------------------------------------------------------------- +CHostFcbManager::~CHostFcbManager() +{ + // 実体が存在しないことを確認 (念のため) + ASSERT(m_cRing.Next() == &m_cRing); + ASSERT(m_cRing.Prev() == &m_cRing); +} +#endif // _DEBUG + +//--------------------------------------------------------------------------- +// +/// 初期化 (ドライバ組込み時) +// +//--------------------------------------------------------------------------- +void CHostFcbManager::Init() +{ + ASSERT(this); + + // 実体が存在しないことを確認 (念のため) + ASSERT(m_cRing.Next() == &m_cRing); + ASSERT(m_cRing.Prev() == &m_cRing); + + // メモリ確保 + for (DWORD i = 0; i < XM6_HOST_FCB_MAX; i++) { + ring_t* p = new ring_t; + ASSERT(p); + p->r.Insert(&m_cRing); + } +} + +//--------------------------------------------------------------------------- +// +/// 解放 (起動・リセット時) +// +//--------------------------------------------------------------------------- +void CHostFcbManager::Clean() +{ + ASSERT(this); + + // メモリ解放 + CRing* p; + while ((p = m_cRing.Next()) != &m_cRing) { + delete (ring_t*)p; + } +} + +//--------------------------------------------------------------------------- +// +/// 確保 +// +//--------------------------------------------------------------------------- +CHostFcb* CHostFcbManager::Alloc(DWORD nKey) +{ + ASSERT(this); + ASSERT(nKey); + + // 末尾から選択 + ring_t* p = (ring_t*)m_cRing.Prev(); + + // 使用中ならエラー (念のため) + if (p->f.isSameKey(0) == FALSE) { + ASSERT(0); + return NULL; + } + + // リング先頭へ移動 + p->r.Insert(&m_cRing); + + // キーを設定 + p->f.SetKey(nKey); + + return &p->f; +} + +//--------------------------------------------------------------------------- +// +/// 検索 +// +//--------------------------------------------------------------------------- +CHostFcb* CHostFcbManager::Search(DWORD nKey) +{ + ASSERT(this); + ASSERT(nKey); + + // 該当するオブジェクトを検索 + ring_t* p = (ring_t*)m_cRing.Next(); + while (p != (ring_t*)&m_cRing) { + if (p->f.isSameKey(nKey)) { + // リング先頭へ移動 + p->r.Insert(&m_cRing); + return &p->f; + } + p = (ring_t*)p->r.Next(); + } + + return NULL; +} + +//--------------------------------------------------------------------------- +// +/// 解放 +// +//--------------------------------------------------------------------------- +void CHostFcbManager::Free(CHostFcb* pFcb) +{ + ASSERT(this); + ASSERT(pFcb); + + // 解放 + pFcb->SetKey(0); + pFcb->Close(); + + // リング末尾へ移動 + ring_t* p = (ring_t*)((size_t)pFcb - offsetof(ring_t, f)); + p->r.InsertTail(&m_cRing); +} + +//=========================================================================== +// +// ホスト側ファイルシステム +// +//=========================================================================== + +DWORD CFileSys::g_nOption; ///< ファイル名変換フラグ + +//--------------------------------------------------------------------------- +// +/// デフォルトコンストラクタ +// +//--------------------------------------------------------------------------- +CFileSys::CFileSys() +{ + // コンフィグデータ初期化 + m_nDrives = 0; + + for (size_t n = 0; n < DriveMax; n++) { + m_nFlag[n] = 0; + m_szBase[n][0] = _T('\0'); + } + + // TwentyOneオプション監視初期化 + m_nKernel = 0; + m_nKernelSearch = 0; + + // 動作フラグ初期化 + m_nOptionDefault = 0; + m_nOption = 0; + ASSERT(g_nOption == 0); + + // 登録したドライブ数は0 + m_nUnits = 0; +} + +//--------------------------------------------------------------------------- +// +/// リセット (全クローズ) +// +//--------------------------------------------------------------------------- +void CFileSys::Reset() +{ + ASSERT(this); + + // 仮想セクタ領域初期化 + m_nHostSectorCount = 0; + memset(m_nHostSectorBuffer, 0, sizeof(m_nHostSectorBuffer)); + + // ファイル検索領域 解放 (起動・リセット時) + m_cFiles.Clean(); + + // FCB操作領域 解放 (起動・リセット時) + m_cFcb.Clean(); + + // ディレクトリエントリ 解放 (起動・リセット時) + m_cEntry.Clean(); + + // TwentyOneオプション監視初期化 + m_nKernel = 0; + m_nKernelSearch = 0; + + // 動作フラグ初期化 + SetOption(m_nOptionDefault); +} + +//--------------------------------------------------------------------------- +// +/// 初期化 (デバイス起動とロード) +// +//--------------------------------------------------------------------------- +void CFileSys::Init() +{ + ASSERT(this); + + // ファイル検索領域 初期化 (デバイス起動・ロード時) + m_cFiles.Init(); + + // FCB操作領域 初期化 (デバイス起動・ロード時) + m_cFcb.Init(); + + // ディレクトリエントリ 初期化 (デバイス起動・ロード時) + m_cEntry.Init(); + + // パス個別設定の有無を判定 + DWORD nDrives = m_nDrives; + if (nDrives == 0) { + // 個別設定を使わずにルートディレクトリを使用する + strcpy(m_szBase[0], _T("/")); + m_nFlag[0] = 0; + nDrives++; + } + + // ファイルシステムを登録 + DWORD nUnit = 0; + for (DWORD n = 0; n < nDrives; n++) { + // ベースパスが存在しないエントリは登録しない + if (m_szBase[n][0] == _T('\0')) + continue; + + // ファイルシステムを1ユニット生成 + CHostDrv* p = new CHostDrv; // std::nothrow + if (p) { + m_cEntry.SetDrv(nUnit, p); + p->Init(m_szBase[n], m_nFlag[n]); + + // 次のユニットへ + nUnit++; + } + } + + // 登録したドライブ数を保存 + m_nUnits = nUnit; +} + +//--------------------------------------------------------------------------- +// +/// $40 - デバイス起動 +// +//--------------------------------------------------------------------------- +DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument) +{ + ASSERT(this); + + // オプション初期化 + InitOption(pArgument); + + // ファイルシステム初期化 + Init(); + + return m_nUnits; +} + +//--------------------------------------------------------------------------- +// +/// $41 - ディレクトリチェック +// +//--------------------------------------------------------------------------- +int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) +{ + ASSERT(this); + ASSERT(pNamests); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + if (f.isRootPath()) + return 0; + f.SetPathOnly(); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_DIRNOTFND; + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $42 - ディレクトリ作成 +// +//--------------------------------------------------------------------------- +int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) +{ + ASSERT(this); + ASSERT(pNamests); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + f.SetPathOnly(); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_INVALIDPATH; + f.AddFilename(); + + // ディレクトリ作成 +#ifndef BAREMETAL + if (mkdir(S2U(f.GetPath()), 0777)) +#else + if (f_mkdir(S2U(f.GetPath())) != FR_OK) +#endif // BAREMETAL + return FS_INVALIDPATH; + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, f.GetHumanPath()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $43 - ディレクトリ削除 +// +//--------------------------------------------------------------------------- +int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) +{ + ASSERT(this); + ASSERT(pNamests); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + f.SetAttribute(Human68k::AT_DIRECTORY); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_DIRNOTFND; + + // キャッシュ削除 + BYTE szHuman[HUMAN68K_PATH_MAX + 24]; + ASSERT(strlen((const char*)f.GetHumanPath()) + + strlen((const char*)f.GetHumanFilename()) < HUMAN68K_PATH_MAX + 24); + strcpy((char*)szHuman, (const char*)f.GetHumanPath()); + strcat((char*)szHuman, (const char*)f.GetHumanFilename()); + strcat((char*)szHuman, "/"); + m_cEntry.DeleteCache(nUnit, szHuman); + + // ディレクトリ削除 +#ifndef BAREMETAL + if (rmdir(S2U(f.GetPath()))) +#else + if (f_rmdir(S2U(f.GetPath())) != FR_OK) +#endif // BAREMETAL + return FS_CANTDELETE; + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, f.GetHumanPath()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $44 - ファイル名変更 +// +//--------------------------------------------------------------------------- +int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) +{ + ASSERT(this); + ASSERT(pNamests); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + f.SetAttribute(Human68k::AT_ALL); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_FILENOTFND; + + CHostFiles fNew; + fNew.SetPath(pNamestsNew); + fNew.SetPathOnly(); + if (fNew.Find(nUnit, &m_cEntry) == FALSE) + return FS_INVALIDPATH; + fNew.AddFilename(); + + // キャッシュ更新 + if (f.GetAttribute() & Human68k::AT_DIRECTORY) + m_cEntry.CleanCacheChild(nUnit, f.GetHumanPath()); + + // ファイル名変更 + char szFrom[FILENAME_MAX]; + char szTo[FILENAME_MAX]; + SJIS2UTF8(f.GetPath(), szFrom, FILENAME_MAX); + SJIS2UTF8(fNew.GetPath(), szTo, FILENAME_MAX); +#ifndef BAREMETAL + if (rename(szFrom, szTo)) { +#else + if (f_rename(szFrom, szTo) != FR_OK) { +#endif // BAREMETAL + return FS_FILENOTFND; + } + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, f.GetHumanPath()); + m_cEntry.CleanCache(nUnit, fNew.GetHumanPath()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $45 - ファイル削除 +// +//--------------------------------------------------------------------------- +int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) +{ + ASSERT(this); + ASSERT(pNamests); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_FILENOTFND; + + // ファイル削除 +#ifndef BAREMETAL + if (unlink(S2U(f.GetPath()))) +#else + if (f_unlink(S2U(f.GetPath())) != FR_OK) +#endif // BAREMETAL + return FS_CANTDELETE; + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, f.GetHumanPath()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $46 - ファイル属性取得/設定 +// +//--------------------------------------------------------------------------- +int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) +{ + ASSERT(this); + ASSERT(pNamests); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + f.SetAttribute(Human68k::AT_ALL); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_FILENOTFND; + + // 属性取得なら終了 + if (nHumanAttribute == 0xFF) + return f.GetAttribute(); + + // 属性チェック + if (nHumanAttribute & Human68k::AT_VOLUME) + return FS_CANTACCESS; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // 属性生成 + DWORD nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | + (f.GetAttribute() & ~Human68k::AT_READONLY); + if (f.GetAttribute() != nAttribute) { +#ifndef BAREMETAL + struct stat sb; + if (stat(S2U(f.GetPath()), &sb)) + return FS_FILENOTFND; + mode_t m = sb.st_mode & 0777; + if (nAttribute & Human68k::AT_READONLY) + m &= 0555; // ugo-w + else + m |= 0200; // u+w + + // 属性設定 + if (chmod(S2U(f.GetPath()), m)) + return FS_FILENOTFND; +#else + if (f_stat(S2U(f.GetPath()), NULL) != FR_OK) + return FS_FILENOTFND; + BYTE m = 0; + if (nAttribute & Human68k::AT_READONLY) + m = AM_RDO; + + // 属性設定 + if (f_chmod(S2U(f.GetPath()), m, AM_RDO)) + return FS_FILENOTFND; +#endif // BAREMETAL + } + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, f.GetHumanPath()); + + // 変更後の属性取得 + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_FILENOTFND; + + return f.GetAttribute(); +} + +//--------------------------------------------------------------------------- +// +/// $47 - ファイル検索 +// +//--------------------------------------------------------------------------- +int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles) +{ + ASSERT(this); + ASSERT(pNamests); + ASSERT(nKey); + ASSERT(pFiles); + + // 既に同じキーを持つ領域があれば解放しておく + CHostFiles* pHostFiles = m_cFiles.Search(nKey); + if (pHostFiles != NULL) { + m_cFiles.Free(pHostFiles); + } + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + + // ボリュームラベルの取得 + /** @note + 直前のメディア交換チェックで正しくエラーを返しているにもかかわら + ず、ボリュームラベルの取得を実行する行儀の悪いアプリでホスト側の + リムーバブルメディア(CD-ROMドライブ等)が白帯を出すのを防ぐため、 + ボリュームラベルの取得はメディアチェックをせずに行なう仕様とした。 + */ + if ((pFiles->fatr & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) + == Human68k::AT_VOLUME) { + // パスチェック + CHostFiles f; + f.SetPath(pNamests); + if (f.isRootPath() == FALSE) + return FS_FILENOTFND; + + // バッファを確保せず、いきなり結果を返す + if (FilesVolume(nUnit, pFiles) == FALSE) + return FS_FILENOTFND; + return 0; + } + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // バッファ確保 + pHostFiles = m_cFiles.Alloc(nKey); + if (pHostFiles == NULL) + return FS_OUTOFMEM; + + // ディレクトリチェック + pHostFiles->SetPath(pNamests); + if (pHostFiles->isRootPath() == FALSE) { + pHostFiles->SetPathOnly(); + if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { + m_cFiles.Free(pHostFiles); + return FS_DIRNOTFND; + } + } + + // ワイルドカード使用可能に設定 + pHostFiles->SetPathWildcard(); + pHostFiles->SetAttribute(pFiles->fatr); + + // ファイル検索 + if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { + m_cFiles.Free(pHostFiles); + return FS_FILENOTFND; + } + + // 検索結果を格納 + pFiles->attr = (BYTE)pHostFiles->GetAttribute(); + pFiles->date = pHostFiles->GetDate(); + pFiles->time = pHostFiles->GetTime(); + pFiles->size = pHostFiles->GetSize(); + strcpy((char*)pFiles->full, (const char*)pHostFiles->GetHumanResult()); + + // 擬似ディレクトリエントリを指定 + pFiles->sector = nKey; + pFiles->offset = 0; + + // ファイル名にワイルドカードがなければ、この時点でバッファを解放可能 + if (pNamests->wildcard == 0) { + // しかし、仮想セクタのエミュレーションで使う可能性があるため、すぐには解放しない + // m_cFiles.Free(pHostFiles); + } + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $48 - ファイル次検索 +// +//--------------------------------------------------------------------------- +int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles) +{ + ASSERT(this); + ASSERT(nKey); + ASSERT(pFiles); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // バッファ検索 + CHostFiles* pHostFiles = m_cFiles.Search(nKey); + if (pHostFiles == NULL) + return FS_INVALIDPTR; + + // ファイル検索 + if (pHostFiles->Find(nUnit, &m_cEntry) == FALSE) { + m_cFiles.Free(pHostFiles); + return FS_FILENOTFND; + } + + ASSERT(pFiles->sector == nKey); + ASSERT(pFiles->offset == 0); + + // 検索結果を格納 + pFiles->attr = (BYTE)pHostFiles->GetAttribute(); + pFiles->date = pHostFiles->GetDate(); + pFiles->time = pHostFiles->GetTime(); + pFiles->size = pHostFiles->GetSize(); + strcpy((char*)pFiles->full, (const char*)pHostFiles->GetHumanResult()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $49 - ファイル新規作成 +// +//--------------------------------------------------------------------------- +int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce) +{ + ASSERT(this); + ASSERT(pNamests); + ASSERT(nKey); + ASSERT(pFcb); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // 既に同じキーを持つ領域があればエラーとする + if (m_cFcb.Search(nKey) != NULL) + return FS_INVALIDPTR; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + f.SetPathOnly(); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_INVALIDPATH; + f.AddFilename(); + + // 属性チェック + if (nHumanAttribute & (Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) + return FS_CANTACCESS; + + // パス名保存 + CHostFcb* pHostFcb = m_cFcb.Alloc(nKey); + if (pHostFcb == NULL) + return FS_OUTOFMEM; + pHostFcb->SetFilename(f.GetPath()); + pHostFcb->SetHumanPath(f.GetHumanPath()); + + // オープンモード設定 + pFcb->mode = (WORD)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL); + if (pHostFcb->SetMode(pFcb->mode) == FALSE) { + m_cFcb.Free(pHostFcb); + return FS_ILLEGALMOD; + } + + // ファイル作成 + if (pHostFcb->Create(pFcb, nHumanAttribute, bForce) == FALSE) { + m_cFcb.Free(pHostFcb); + return FS_FILEEXIST; + } + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, f.GetHumanPath()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $4A - ファイルオープン +// +//--------------------------------------------------------------------------- +int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb) +{ + ASSERT(this); + ASSERT(pNamests); + ASSERT(nKey); + ASSERT(pFcb); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + switch (pFcb->mode & Human68k::OP_MASK) { + case Human68k::OP_WRITE: + case Human68k::OP_FULL: + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + } + + // 既に同じキーを持つ領域があればエラーとする + if (m_cFcb.Search(nKey) != NULL) + return FS_INVALIDPRM; + + // パス名生成 + CHostFiles f; + f.SetPath(pNamests); + f.SetAttribute(Human68k::AT_ALL); + if (f.Find(nUnit, &m_cEntry) == FALSE) + return FS_FILENOTFND; + + // タイムスタンプ + pFcb->date = f.GetDate(); + pFcb->time = f.GetTime(); + + // ファイルサイズ + pFcb->size = f.GetSize(); + + // パス名保存 + CHostFcb* pHostFcb = m_cFcb.Alloc(nKey); + if (pHostFcb == NULL) + return FS_OUTOFMEM; + pHostFcb->SetFilename(f.GetPath()); + pHostFcb->SetHumanPath(f.GetHumanPath()); + + // オープンモード設定 + if (pHostFcb->SetMode(pFcb->mode) == FALSE) { + m_cFcb.Free(pHostFcb); + return FS_ILLEGALMOD; + } + + // ファイルオープン + if (pHostFcb->Open() == FALSE) { + m_cFcb.Free(pHostFcb); + return FS_INVALIDPATH; + } + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $4B - ファイルクローズ +// +//--------------------------------------------------------------------------- +int CFileSys::Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* /* pFcb */) +{ + ASSERT(this); + ASSERT(nKey); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 既に同じキーを持つ領域がなければエラーとする + CHostFcb* pHostFcb = m_cFcb.Search(nKey); + if (pHostFcb == NULL) + return FS_INVALIDPRM; + + // ファイルクローズと領域解放 + m_cFcb.Free(pHostFcb); + + // キャッシュ更新 + if (pHostFcb->isUpdate()) + m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); + + /// @note クローズ時のFCBの状態を他のデバイスと合わせたい + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $4C - ファイル読み込み +/// +/// 0バイト読み込みでも正常終了する。 +// +//--------------------------------------------------------------------------- +int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize) +{ + ASSERT(this); + ASSERT(nKey); + ASSERT(pFcb); + // ASSERT(pBuffer); // 必要時のみ判定 + ASSERT(nSize <= 0xFFFFFF); // クリップ済 + + // 既に同じキーを持つ領域がなければエラーとする + CHostFcb* pHostFcb = m_cFcb.Search(nKey); + if (pHostFcb == NULL) + return FS_NOTOPENED; + + // バッファ存在確認 + if (pBuffer == NULL) { + m_cFcb.Free(pHostFcb); + return FS_INVALIDFUNC; + } + + // 読み込み + DWORD nResult; + nResult = pHostFcb->Read(pBuffer, nSize); + if (nResult == (DWORD)-1) { + m_cFcb.Free(pHostFcb); + return FS_INVALIDFUNC; /// @note これに加えてエラーコード10(読み込みエラー)を返すべき + } + ASSERT(nResult <= nSize); + + // ファイルポインタ更新 + pFcb->fileptr += nResult; /// @note オーバーフロー確認は必要じゃろうか? + + return nResult; +} + +//--------------------------------------------------------------------------- +// +/// $4D - ファイル書き込み +/// +/// 0バイト書き込みの場合はファイルを切り詰める。 +// +//--------------------------------------------------------------------------- +int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWORD nSize) +{ + ASSERT(this); + ASSERT(nKey); + ASSERT(pFcb); + // ASSERT(pBuffer); // 必要時のみ判定 + ASSERT(nSize <= 0xFFFFFF); // クリップ済 + + // 既に同じキーを持つ領域がなければエラーとする + CHostFcb* pHostFcb = m_cFcb.Search(nKey); + if (pHostFcb == NULL) + return FS_NOTOPENED; + + DWORD nResult; + if (nSize == 0) { + // 切り詰め + if (pHostFcb->Truncate() == FALSE) { + m_cFcb.Free(pHostFcb); + return FS_CANTSEEK; + } + + // ファイルサイズ更新 + pFcb->size = pFcb->fileptr; + + nResult = 0; + } else { + // バッファ存在確認 + if (pBuffer == NULL) { + m_cFcb.Free(pHostFcb); + return FS_INVALIDFUNC; + } + + // 書き込み + nResult = pHostFcb->Write(pBuffer, nSize); + if (nResult == (DWORD)-1) { + m_cFcb.Free(pHostFcb); + return FS_CANTWRITE; /// @note これに加えてエラーコード11(書き込みエラー)を返すべき + } + ASSERT(nResult <= nSize); + + // ファイルポインタ更新 + pFcb->fileptr += nResult; /// @note オーバーフロー確認は必要じゃろうか? + + // ファイルサイズ更新 + if (pFcb->size < pFcb->fileptr) + pFcb->size = pFcb->fileptr; + } + + // フラグ更新 + pHostFcb->SetUpdate(); + + return nResult; +} + +//--------------------------------------------------------------------------- +// +/// $4E - ファイルシーク +// +//--------------------------------------------------------------------------- +int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset) +{ + ASSERT(this); + ASSERT(pFcb); + + // 既に同じキーを持つ領域がなければエラーとする + CHostFcb* pHostFcb = m_cFcb.Search(nKey); + if (pHostFcb == NULL) + return FS_NOTOPENED; + + // パラメータチェック + if (nSeek > Human68k::SK_END) { + m_cFcb.Free(pHostFcb); + return FS_INVALIDPRM; + } + + // ファイルシーク + DWORD nResult = pHostFcb->Seek(nOffset, nSeek); + if (nResult == (DWORD)-1) { + m_cFcb.Free(pHostFcb); + return FS_CANTSEEK; + } + + // ファイルポインタ更新 + pFcb->fileptr = nResult; + + return nResult; +} + +//--------------------------------------------------------------------------- +// +/// $4F - ファイル時刻取得/設定 +/// +/// 結果の上位16Bitが$FFFFだとエラー。 +// +//--------------------------------------------------------------------------- +DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime) +{ + ASSERT(this); + ASSERT(nKey); + ASSERT(pFcb); + + // 取得のみ + if (nHumanTime == 0) + return ((DWORD)pFcb->date << 16) | pFcb->time; + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // 既に同じキーを持つ領域がなければエラーとする + CHostFcb* pHostFcb = m_cFcb.Search(nKey); + if (pHostFcb == NULL) + return FS_NOTOPENED; + + // 時刻設定 + if (pHostFcb->TimeStamp(nHumanTime) == FALSE) { + m_cFcb.Free(pHostFcb); + return FS_INVALIDPRM; + } + pFcb->date = (WORD)(nHumanTime >> 16); + pFcb->time = (WORD)nHumanTime; + + // キャッシュ更新 + m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath()); + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $50 - 容量取得 +// +//--------------------------------------------------------------------------- +int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) +{ + ASSERT(this); + ASSERT(pCapacity); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 容量取得 + return m_cEntry.GetCapacity(nUnit, pCapacity); +} + +//--------------------------------------------------------------------------- +// +/// $51 - ドライブ状態検査/制御 +// +//--------------------------------------------------------------------------- +int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) +{ + ASSERT(this); + ASSERT(pCtrlDrive); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + + switch (pCtrlDrive->status) { + case 0: // 状態検査 + case 9: // 状態検査2 + pCtrlDrive->status = (BYTE)m_cEntry.GetStatus(nUnit); + return pCtrlDrive->status; + + case 1: // イジェクト + case 2: // イジェクト禁止1 (未実装) + case 3: // イジェクト許可1 (未実装) + case 4: // メディア未挿入時にLED点滅 (未実装) + case 5: // メディア未挿入時にLED消灯 (未実装) + case 6: // イジェクト禁止2 (未実装) + case 7: // イジェクト許可2 (未実装) + return 0; + + case 8: // イジェクト検査 + return 1; + } + + return FS_INVALIDFUNC; +} + +//--------------------------------------------------------------------------- +// +/// $52 - DPB取得 +/// +/// レジューム後にDPBが取得できないとHuman68k内部でドライブが消滅するため、 +/// 範囲外のユニットでもとにかく正常系として処理する。 +// +//--------------------------------------------------------------------------- +int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) +{ + ASSERT(this); + ASSERT(pDpb); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + + Human68k::capacity_t cap; + BYTE media = Human68k::MEDIA_REMOTE; + if (nUnit < m_nUnits) { + media = m_cEntry.GetMediaByte(nUnit); + + // セクタ情報獲得 + if (m_cEntry.GetCapacityCache(nUnit, &cap) == FALSE) { + // 手動イジェクトだとメディアチェックをすり抜けるためここで捕捉 + if (m_cEntry.isEnable(nUnit) == FALSE) + goto none; + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + goto none; + + // ドライブ状態取得 + m_cEntry.GetCapacity(nUnit, &cap); + } + } else { + none: + cap.clusters = 4; // まっったく問題ないッスよ? + cap.sectors = 64; + cap.bytes = 512; + } + + // シフト数計算 + DWORD nSize = 1; + DWORD nShift = 0; + for (;;) { + if (nSize >= cap.sectors) + break; + nSize <<= 1; + nShift++; + } + + // セクタ番号計算 + // + // 以下の順に並べる。 + // クラスタ0: 未使用 + // クラスタ1: FAT + // クラスタ2: ルートディレクトリ + // クラスタ3: データ領域(擬似セクタ) + DWORD nFat = cap.sectors; + DWORD nRoot = cap.sectors * 2; + DWORD nData = cap.sectors * 3; + + // DPB設定 + pDpb->sector_size = (WORD)cap.bytes; // 1セクタ当りのバイト数 + pDpb->cluster_size = + (BYTE)(cap.sectors - 1); // 1クラスタ当りのセクタ数 - 1 + pDpb->shift = (BYTE)nShift; // クラスタ→セクタのシフト数 + pDpb->fat_sector = (WORD)nFat; // FAT の先頭セクタ番号 + pDpb->fat_max = 1; // FAT 領域の個数 + pDpb->fat_size = (BYTE)cap.sectors; // FAT の占めるセクタ数(複写分を除く) + pDpb->file_max = + (WORD)(cap.sectors * cap.bytes / 0x20); // ルートディレクトリに入るファイルの個数 + pDpb->data_sector = (WORD)nData; // データ領域の先頭セクタ番号 + pDpb->cluster_max = (WORD)cap.clusters; // 総クラスタ数 + 1 + pDpb->root_sector = (WORD)nRoot; // ルートディレクトリの先頭セクタ番号 + pDpb->media = media; // メディアバイト + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $53 - セクタ読み込み +/// +/// セクタは疑似的に構築したものを使用する。 +/// バッファサイズは$200バイト固定。 +// +//--------------------------------------------------------------------------- +int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize) +{ + ASSERT(this); + ASSERT(pBuffer); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // レジューム後に無効なドライブ上で発生 + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // セクタ数1以外の場合はエラー + if (nSize != 1) + return FS_INVALIDPRM; + + // セクタ情報獲得 + Human68k::capacity_t cap; + if (m_cEntry.GetCapacityCache(nUnit, &cap) == FALSE) { + // ドライブ状態取得 + m_cEntry.GetCapacity(nUnit, &cap); + } + + // 擬似ディレクトリエントリへのアクセス + CHostFiles* pHostFiles = m_cFiles.Search(nSector); + if (pHostFiles) { + // 擬似ディレクトリエントリを生成 + Human68k::dirent_t* dir = (Human68k::dirent_t*)pBuffer; + memcpy(pBuffer, pHostFiles->GetEntry(), sizeof(*dir)); + memset(pBuffer + sizeof(*dir), 0xE5, 0x200 - sizeof(*dir)); + + // 擬似ディレクトリエントリ内にファイル実体を指す擬似セクタ番号を記録 + // なお、lzdsysでは以下の式で読み込みセクタ番号を算出している。 + // (dirent.cluster - 2) * (dpb.cluster_size + 1) + dpb.data_sector + /// @warning リトルエンディアン専用 + dir->cluster = (WORD)(m_nHostSectorCount + 2); // 擬似セクタ番号 + m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // 擬似セクタの指す実体 + m_nHostSectorCount++; + m_nHostSectorCount %= XM6_HOST_PSEUDO_CLUSTER_MAX; + + return 0; + } + + // クラスタ番号からセクタ番号を算出 + DWORD n = nSector - (3 * cap.sectors); + DWORD nMod = 1; + if (cap.sectors) { + // メディアが存在しない場合はcap.sectorsが0になるので注意 + nMod = n % cap.sectors; + n /= cap.sectors; + } + + // ファイル実体へのアクセス + if (nMod == 0 && n < XM6_HOST_PSEUDO_CLUSTER_MAX) { + pHostFiles = m_cFiles.Search(m_nHostSectorBuffer[n]); // 実体を検索 + if (pHostFiles) { + // 擬似セクタを生成 + CHostFcb f; + f.SetFilename(pHostFiles->GetPath()); + f.SetMode(Human68k::OP_READ); + if (f.Open() == FALSE) + return FS_INVALIDPRM; + memset(pBuffer, 0, 0x200); + DWORD nResult = f.Read(pBuffer, 0x200); + f.Close(); + if (nResult == (DWORD)-1) + return FS_INVALIDPRM; + + return 0; + } + } + + return FS_INVALIDPRM; +} + +//--------------------------------------------------------------------------- +// +/// $54 - セクタ書き込み +// +//--------------------------------------------------------------------------- +int CFileSys::DiskWrite(DWORD nUnit) +{ + ASSERT(this); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 書き込み禁止チェック + if (m_cEntry.isWriteProtect(nUnit)) + return FS_FATAL_WRITEPROTECT; + + // 現実を突きつける + return FS_INVALIDPRM; +} + +//--------------------------------------------------------------------------- +// +/// $55 - IOCTRL +// +//--------------------------------------------------------------------------- +int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl) +{ + ASSERT(this); + ASSERT(pIoctrl); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + + switch (nFunction) { + case 0: + // メディアバイトの獲得 + pIoctrl->media = m_cEntry.GetMediaByte(nUnit); + return 0; + + case 1: + // Human68k互換のためのダミー + pIoctrl->param = (unsigned long)-1; + return 0; + + case 2: + switch (pIoctrl->param) { + case (DWORD)-1: + // メディア再認識 + m_cEntry.isMediaOffline(nUnit); + return 0; + + case 0: + case 1: + // Human68k互換のためのダミー + return 0; + } + break; + + case (DWORD)-1: + // 常駐判定 + memcpy(pIoctrl->buffer, "WindrvXM", 8); + return 0; + + case (DWORD)-2: + // オプション設定 + SetOption(pIoctrl->param); + return 0; + + case (DWORD)-3: + // オプション獲得 + pIoctrl->param = GetOption(); + return 0; + } + + return FS_NOTIOCTRL; +} + +//--------------------------------------------------------------------------- +// +/// $56 - フラッシュ +// +//--------------------------------------------------------------------------- +int CFileSys::Flush(DWORD nUnit) +{ + ASSERT(this); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_INVALIDFUNC; // レジューム後に無効なドライブでmintからコマンドを実行し戻る時に白帯を出さないよう改良 + + // 常に成功 + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $57 - メディア交換チェック +// +//--------------------------------------------------------------------------- +int CFileSys::CheckMedia(DWORD nUnit) +{ + ASSERT(this); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + if (nUnit >= m_nUnits) + return FS_INVALIDFUNC; // レジューム後に無効なドライブでmint操作時に白帯を出さないよう改良 + + // メディア交換チェック + BOOL bResult = m_cEntry.CheckMedia(nUnit); + + // メディア未挿入ならエラーとする + if (bResult == FALSE) { + return FS_INVALIDFUNC; + } + + return 0; +} + +//--------------------------------------------------------------------------- +// +/// $58 - 排他制御 +// +//--------------------------------------------------------------------------- +int CFileSys::Lock(DWORD nUnit) +{ + ASSERT(this); + + // ユニットチェック + if (nUnit >= DriveMax) + return FS_FATAL_INVALIDUNIT; + ASSERT(nUnit < m_nUnits); + if (nUnit >= m_nUnits) + return FS_FATAL_MEDIAOFFLINE; // 念のため + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FS_FATAL_MEDIAOFFLINE; + + // 常に成功 + return 0; +} + +//--------------------------------------------------------------------------- +// +/// オプション設定 +// +//--------------------------------------------------------------------------- +void CFileSys::SetOption(DWORD nOption) +{ + ASSERT(this); + + // オプション設定変更でキャッシュクリア + if (m_nOption ^ nOption) + m_cEntry.CleanCache(); + + m_nOption = nOption; + g_nOption = nOption; +} + +//--------------------------------------------------------------------------- +// +/// オプション初期化 +// +//--------------------------------------------------------------------------- +void CFileSys::InitOption(const Human68k::argument_t* pArgument) +{ + ASSERT(this); + ASSERT(pArgument); + + // ドライブ数を初期化 + m_nDrives = 0; + + const BYTE* pp = pArgument->buf; + pp += strlen((const char*)pp) + 1; + + DWORD nOption = m_nOptionDefault; + for (;;) { + ASSERT(pp < pArgument->buf + sizeof(*pArgument)); + const BYTE* p = pp; + BYTE c = *p++; + if (c == '\0') + break; + + DWORD nMode; + if (c == '+') { + nMode = 1; + } else if (c == '-') { + nMode = 0; + } else if (c == '/') { + // デフォルトベースパスの指定 + if (m_nDrives < DriveMax) { + p--; + strcpy(m_szBase[m_nDrives], (const char *)p); + m_nDrives++; + } + pp += strlen((const char*)pp) + 1; + continue; + } else { + // オプション指定ではないので次へ + pp += strlen((const char*)pp) + 1; + continue; + } + + for (;;) { + c = *p++; + if (c == '\0') + break; + + DWORD nBit = 0; + switch (c) { + case 'A': case 'a': nBit = WINDRV_OPT_CONVERT_LENGTH; break; + case 'T': case 't': nBit = WINDRV_OPT_COMPARE_LENGTH; nMode ^= 1; break; + case 'C': case 'c': nBit = WINDRV_OPT_ALPHABET; break; + + case 'E': nBit = WINDRV_OPT_CONVERT_PERIOD; break; + case 'P': nBit = WINDRV_OPT_CONVERT_PERIODS; break; + case 'N': nBit = WINDRV_OPT_CONVERT_HYPHEN; break; + case 'H': nBit = WINDRV_OPT_CONVERT_HYPHENS; break; + case 'X': nBit = WINDRV_OPT_CONVERT_BADCHAR; break; + case 'S': nBit = WINDRV_OPT_CONVERT_SPACE; break; + + case 'e': nBit = WINDRV_OPT_REDUCED_PERIOD; break; + case 'p': nBit = WINDRV_OPT_REDUCED_PERIODS; break; + case 'n': nBit = WINDRV_OPT_REDUCED_HYPHEN; break; + case 'h': nBit = WINDRV_OPT_REDUCED_HYPHENS; break; + case 'x': nBit = WINDRV_OPT_REDUCED_BADCHAR; break; + case 's': nBit = WINDRV_OPT_REDUCED_SPACE; break; + } + + if (nMode) + nOption |= nBit; + else + nOption &= ~nBit; + } + + pp = p; + } + + // オプション設定 + if (nOption != m_nOption) { + SetOption(nOption); + } +} + +//--------------------------------------------------------------------------- +// +/// ボリュームラベル取得 +// +//--------------------------------------------------------------------------- +BOOL CFileSys::FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) +{ + ASSERT(this); + ASSERT(pFiles); + + // ボリュームラベル取得 + TCHAR szVolume[32]; + BOOL bResult = m_cEntry.GetVolumeCache(nUnit, szVolume); + if (bResult == FALSE) { + // 手動イジェクトだとメディアチェックをすり抜けるためここで捕捉 + if (m_cEntry.isEnable(nUnit) == FALSE) + return FALSE; + + // メディアチェック + if (m_cEntry.isMediaOffline(nUnit)) + return FALSE; + + // ボリュームラベル取得 + m_cEntry.GetVolume(nUnit, szVolume); + } + if (szVolume[0] == _T('\0')) + return FALSE; + + pFiles->attr = Human68k::AT_VOLUME; + pFiles->time = 0; + pFiles->date = 0; + pFiles->size = 0; + + CHostFilename fname; + fname.SetHost(szVolume); + fname.ConvertHuman(); + strcpy((char*)pFiles->full, (const char*)fname.GetHuman()); + + return TRUE; +} diff --git a/src/raspberrypi/devices/cfilesystem.h b/src/raspberrypi/devices/cfilesystem.h new file mode 100644 index 00000000..42cfa146 --- /dev/null +++ b/src/raspberrypi/devices/cfilesystem.h @@ -0,0 +1,1184 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// [ Host File System for the X68000 ] +// +// Note: This functionality is specific to the X68000 operating system. +// It is highly unlikely that this will work for other platforms. +//--------------------------------------------------------------------------- + +#ifndef cfilesystem_h +#define cfilesystem_h + +#ifdef BAREMETAL +#include "ffconf.h" +#include "ff.h" +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// ステータスコード定義 +// +//--------------------------------------------------------------------------- +#define FS_INVALIDFUNC 0xFFFFFFFF ///< 無効なファンクションコードを実行した +#define FS_FILENOTFND 0xFFFFFFFE ///< 指定したファイルが見つからない +#define FS_DIRNOTFND 0xFFFFFFFD ///< 指定したディレクトリが見つからない +#define FS_OVEROPENED 0xFFFFFFFC ///< オープンしているファイルが多すぎる +#define FS_CANTACCESS 0xFFFFFFFB ///< ディレクトリやボリュームラベルはアクセス不可 +#define FS_NOTOPENED 0xFFFFFFFA ///< 指定したハンドルはオープンされていない +#define FS_INVALIDMEM 0xFFFFFFF9 ///< メモリ管理領域が破壊された +#define FS_OUTOFMEM 0xFFFFFFF8 ///< 実行に必要なメモリがない +#define FS_INVALIDPTR 0xFFFFFFF7 ///< 無効なメモリ管理ポインタを指定した +#define FS_INVALIDENV 0xFFFFFFF6 ///< 不正な環境を指定した +#define FS_ILLEGALFMT 0xFFFFFFF5 ///< 実行ファイルのフォーマットが異常 +#define FS_ILLEGALMOD 0xFFFFFFF4 ///< オープンのアクセスモードが異常 +#define FS_INVALIDPATH 0xFFFFFFF3 ///< ファイル名の指定に誤りがある +#define FS_INVALIDPRM 0xFFFFFFF2 ///< 無効なパラメータでコールした +#define FS_INVALIDDRV 0xFFFFFFF1 ///< ドライブ指定に誤りがある +#define FS_DELCURDIR 0xFFFFFFF0 ///< カレントディレクトリは削除できない +#define FS_NOTIOCTRL 0xFFFFFFEF ///< IOCTRLできないデバイス +#define FS_LASTFILE 0xFFFFFFEE ///< これ以上ファイルが見つからない +#define FS_CANTWRITE 0xFFFFFFED ///< 指定のファイルは書き込みできない +#define FS_DIRALREADY 0xFFFFFFEC ///< 指定のディレクトリは既に登録されている +#define FS_CANTDELETE 0xFFFFFFEB ///< ファイルがあるので削除できない +#define FS_CANTRENAME 0xFFFFFFEA ///< ファイルがあるのでリネームできない +#define FS_DISKFULL 0xFFFFFFE9 ///< ディスクが一杯でファイルが作れない +#define FS_DIRFULL 0xFFFFFFE8 ///< ディレクトリが一杯でファイルが作れない +#define FS_CANTSEEK 0xFFFFFFE7 ///< 指定の位置にはシークできない +#define FS_SUPERVISOR 0xFFFFFFE6 ///< スーパーバイザ状態でスーパバイザ指定した +#define FS_THREADNAME 0xFFFFFFE5 ///< 同じスレッド名が存在する +#define FS_BUFWRITE 0xFFFFFFE4 ///< プロセス間通信のバッファが書込み禁止 +#define FS_BACKGROUND 0xFFFFFFE3 ///< バックグラウンドプロセスを起動できない +#define FS_OUTOFLOCK 0xFFFFFFE0 ///< ロック領域が足りない +#define FS_LOCKED 0xFFFFFFDF ///< ロックされていてアクセスできない +#define FS_DRIVEOPENED 0xFFFFFFDE ///< 指定のドライブはハンドラがオープンされている +#define FS_LINKOVER 0xFFFFFFDD ///< シンボリックリンクネストが16回を超えた +#define FS_FILEEXIST 0xFFFFFFB0 ///< ファイルが存在する + +#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< メディアが入っていない +#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< 書き込み禁止違反 +#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< 不正なコマンド番号 +#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< 不正なユニット番号 + +#define HUMAN68K_PATH_MAX 96 ///< Human68kのパス最大長 + +//=========================================================================== +// +/// Human68k 名前空間 +// +//=========================================================================== +namespace Human68k { + /// ファイル属性ビット + enum attribute_t { + AT_READONLY = 0x01, ///< 読み込み専用属性 + AT_HIDDEN = 0x02, ///< 隠し属性 + AT_SYSTEM = 0x04, ///< システム属性 + AT_VOLUME = 0x08, ///< ボリュームラベル属性 + AT_DIRECTORY = 0x10, ///< ディレクトリ属性 + AT_ARCHIVE = 0x20, ///< アーカイブ属性 + AT_ALL = 0xFF, ///< 全ての属性ビットが1 + }; + + /// ファイルオープンモード + enum open_t { + OP_READ = 0, ///< 読み込み + OP_WRITE = 1, ///< 書き込み + OP_FULL = 2, ///< 読み書き + OP_MASK = 0x0F, ///< 判定用マスク + OP_SHARE_NONE = 0x10, ///< 共有禁止 + OP_SHARE_READ = 0x20, ///< 読み込み共有 + OP_SHARE_WRITE = 0x30, ///< 書き込み共有 + OP_SHARE_FULL = 0x40, ///< 読み書き共有 + OP_SHARE_MASK = 0x70, ///< 共有判定用マスク + OP_SPECIAL = 0x100, ///< 辞書アクセス + }; + + /// シーク種類 + enum seek_t { + SK_BEGIN = 0, ///< ファイル先頭から + SK_CURRENT = 1, ///< 現在位置から + SK_END = 2, ///< ファイル末尾から + }; + + /// メディアバイト + enum media_t { + MEDIA_2DD_10 = 0xE0, ///< 2DD/10セクタ + MEDIA_1D_9 = 0xE5, ///< 1D/9セクタ + MEDIA_2D_9 = 0xE6, ///< 2D/9セクタ + MEDIA_1D_8 = 0xE7, ///< 1D/8セクタ + MEDIA_2D_8 = 0xE8, ///< 2D/8セクタ + MEDIA_2HT = 0xEA, ///< 2HT + MEDIA_2HS = 0xEB, ///< 2HS + MEDIA_2HDE = 0xEC, ///< 2DDE + MEDIA_1DD_9 = 0xEE, ///< 1DD/9セクタ + MEDIA_1DD_8 = 0xEF, ///< 1DD/8セクタ + MEDIA_MANUAL = 0xF1, ///< リモートドライブ (手動イジェクト) + MEDIA_REMOVABLE = 0xF2, ///< リモートドライブ (リムーバブル) + MEDIA_REMOTE = 0xF3, ///< リモートドライブ + MEDIA_DAT = 0xF4, ///< SCSI-DAT + MEDIA_CDROM = 0xF5, ///< SCSI-CDROM + MEDIA_MO = 0xF6, ///< SCSI-MO + MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD + MEDIA_SASI_HD = 0xF8, ///< SASI-HD + MEDIA_RAMDISK = 0xF9, ///< RAMディスク + MEDIA_2HQ = 0xFA, ///< 2HQ + MEDIA_2DD_8 = 0xFB, ///< 2DD/8セクタ + MEDIA_2DD_9 = 0xFC, ///< 2DD/9セクタ + MEDIA_2HC = 0xFD, ///< 2HC + MEDIA_2HD = 0xFE, ///< 2HD + }; + + /// namests構造体 + struct namests_t { + BYTE wildcard; ///< ワイルドカード文字数 + BYTE drive; ///< ドライブ番号 + BYTE path[65]; ///< パス(サブディレクトリ+/) + BYTE name[8]; ///< ファイル名 (PADDING 0x20) + BYTE ext[3]; ///< 拡張子 (PADDING 0x20) + BYTE add[10]; ///< ファイル名追加 (PADDING 0x00) + + // 文字列取得 + void FASTCALL GetCopyPath(BYTE* szPath) const; + ///< パス名取得 + void FASTCALL GetCopyFilename(BYTE* szFilename) const; + ///< ファイル名取得 + }; + + /// files構造体 + struct files_t { + BYTE fatr; ///< + 0 検索する属性 読込専用 + // BYTE drive; ///< + 1 ドライブ番号 読込専用 + DWORD sector; ///< + 2 ディレクトリのセクタ DOS _FILES先頭アドレスで代用 + // WORD cluster; ///< + 6 ディレクトリのクラスタ 詳細不明 (未使用) + WORD offset; ///< + 8 ディレクトリエントリ 書込専用 + // BYTE name[8]; ///< +10 作業用ファイル名 読込専用 (未使用) + // BYTE ext[3]; ///< +18 作業用拡張子 読込専用 (未使用) + BYTE attr; ///< +21 ファイル属性 書込専用 + WORD time; ///< +22 最終変更時刻 書込専用 + WORD date; ///< +24 最終変更月日 書込専用 + DWORD size; ///< +26 ファイルサイズ 書込専用 + BYTE full[23]; ///< +30 フルファイル名 書込専用 + }; + + /// FCB構造体 + struct fcb_t { + // BYTE pad00[6]; ///< + 0~+ 5 (未使用) + DWORD fileptr; ///< + 6~+ 9 ファイルポインタ + // BYTE pad01[4]; ///< +10~+13 (未使用) + WORD mode; ///< +14~+15 オープンモード + // BYTE pad02[16]; ///< +16~+31 (未使用) + // DWORD zero; ///< +32~+35 オープンのとき0が書き込まれている (未使用) + // BYTE name[8]; ///< +36~+43 ファイル名 (PADDING 0x20) (未使用) + // BYTE ext[3]; ///< +44~+46 拡張子 (PADDING 0x20) (未使用) + BYTE attr; ///< +47 ファイル属性 + // BYTE add[10]; ///< +48~+57 ファイル名追加 (PADDING 0x00) (未使用) + WORD time; ///< +58~+59 最終変更時刻 + WORD date; ///< +60~+61 最終変更月日 + // WORD cluster; ///< +62~+63 クラスタ番号 (未使用) + DWORD size; ///< +64~+67 ファイルサイズ + // BYTE pad03[28]; ///< +68~+95 FATキャッシュ (未使用) + }; + + /// capacity構造体 + struct capacity_t { + WORD freearea; ///< + 0 使用可能なクラスタ数 + WORD clusters; ///< + 2 総クラスタ数 + WORD sectors; ///< + 4 クラスタあたりのセクタ数 + WORD bytes; ///< + 6 セクタ当たりのバイト数 + }; + + /// ctrldrive構造体 + struct ctrldrive_t { + BYTE status; ///< +13 状態 + BYTE pad[3]; ///< Padding + }; + + /// DPB構造体 + struct dpb_t { + WORD sector_size; ///< + 0 1セクタ当りのバイト数 + BYTE cluster_size; ///< + 2 1クラスタ当りのセクタ数-1 + BYTE shift; ///< + 3 クラスタ→セクタのシフト数 + WORD fat_sector; ///< + 4 FATの先頭セクタ番号 + BYTE fat_max; ///< + 6 FAT領域の個数 + BYTE fat_size; ///< + 7 FATの占めるセクタ数(複写分を除く) + WORD file_max; ///< + 8 ルートディレクトリに入るファイルの個数 + WORD data_sector; ///< +10 データ領域の先頭セクタ番号 + WORD cluster_max; ///< +12 総クラスタ数+1 + WORD root_sector; ///< +14 ルートディレクトリの先頭セクタ番号 + // DWORD driverentry; ///< +16 デバイスドライバへのポインタ (未使用) + BYTE media; ///< +20 メディア識別子 + // BYTE flag; ///< +21 DPB使用フラグ (未使用) + }; + + /// ディレクトリエントリ構造体 + struct dirent_t { + BYTE name[8]; ///< + 0 ファイル名 (PADDING 0x20) + BYTE ext[3]; ///< + 8 拡張子 (PADDING 0x20) + BYTE attr; ///< +11 ファイル属性 + BYTE add[10]; ///< +12 ファイル名追加 (PADDING 0x00) + WORD time; ///< +22 最終変更時刻 + WORD date; ///< +24 最終変更月日 + WORD cluster; ///< +26 クラスタ番号 + DWORD size; ///< +28 ファイルサイズ + }; + + /// IOCTRLパラメータ共用体 + union ioctrl_t { + BYTE buffer[8]; ///< バイト単位でのアクセス + DWORD param; ///< パラメータ(先頭4バイト) + WORD media; ///< メディアバイト(先頭2バイト) + }; + + /// コマンドライン引数構造体 + /** + 先頭にドライバ自身のパスが含まれるためHUMAN68K_PATH_MAX以上のサイズにする。 + */ + struct argument_t { + BYTE buf[256]; ///< コマンドライン引数 + }; +} + +/// FILES用バッファ個数 +/** +通常は数個で十分だが、Human68kの複数のプロセスがマルチタスクで同時に +深い階層に渡って作業する時などはこの値を増やす必要がある。 + +デフォルトは20個。 +*/ +#define XM6_HOST_FILES_MAX 20 + +/// FCB用バッファ個数 +/** +同時にオープンできるファイル数はこれで決まる。 + +デフォルトは100ファイル。 +*/ +#define XM6_HOST_FCB_MAX 100 + +/// 仮想セクタ/クラスタ 最大個数 +/** +ファイル実体の先頭セクタへのアクセスに対応するための仮想セクタの個数。 +lzdsysによるアクセスを行なうスレッドの数より多めに確保する。 + +デフォルトは10セクタ。 +*/ +#define XM6_HOST_PSEUDO_CLUSTER_MAX 10 + +/// ディレクトリエントリ キャッシュ個数 +/** +Human68kは、サブディレクトリ内で処理を行なう際にディレクトリエントリ +のチェックを大量に発行する。この応答を高速化するための簡易キャッシュ +の個数を指定する。キャッシュは各ドライブ毎に確保される。 +多いほど高速になるが、増やしすぎるとホストOS側に負担がかかるので注意。 + +デフォルトは16個。 +*/ +#define XM6_HOST_DIRENTRY_CACHE_MAX 16 + +/// 1ディレクトリに収納できるエントリの最大数 +/** +ディレクトリ内にファイルが大量に存在すると、当時のアプリケーションが +想定していない大量のデータを返してしまうことになる。アプリによっては +一部しか認識されなかったり、速度が大幅に低下したり、メモリ不足で停止 +するなどの危険性が存在する。このため上限を設定することで対処する。 +例えばとあるファイラの場合、2560ファイルが上限となっている。この数を +一つの目安とするのが良い。 + +デフォルトは約6万エントリ。(FATのルートディレクトリでの上限値) +*/ +#define XM6_HOST_DIRENTRY_FILE_MAX 65535 + +/// ファイル名の重複除外パターンの最大数 +/** +Human68k側のファイル名は、ホスト側のファイルシステムの名称をもとに自 +動生成されるが、Human68k側のファイル名よりもホスト側のファイル名の名 +称のほうが長いため、同名のファイル名が生成されてしまう可能性がある。 +その時、Human68k側からファイル名を区別できるようにするため、WindrvXM +独自の命名規則に従って別名を生成して解決している。 +理論上は約6千万(36の5乗)通りの別名を生成できる方式を取っているが、実 +際には数百パターン以上の重複判定が発生すると処理に時間がかかってしま +うため、重複の上限を設定することで速度を維持する。常識的な運用であれ +ば、代替名は数パターンもあれば十分運用できるはずであり、この値を可能 +な限り小さい値にすることでパフォーマンスの改善が期待できる。 +この個数を超えるファイル名が重複してしまった場合は、同名のエントリが +複数生成される。この場合、ファイル一覧では見えるがファイル名で指定す +ると最初のエントリのみ扱える状態となる。 + +デフォルトは36パターン。 +*/ +#define XM6_HOST_FILENAME_PATTERN_MAX 36 + +/// ファイル名重複防止マーク +/** +ホスト側のファイル名とHuman68k側ファイル名の名称の区別をつけるときに +使う文字。コマンドシェル等のエスケープ文字と重ならないものを選ぶと吉。 + +デフォルトは「@」。 +*/ +#define XM6_HOST_FILENAME_MARK '@' + +/// WINDRV動作フラグ +/** +通常は0にする。ファイル削除にOSのごみ箱機能を利用する場合は1にする。 +それ以外の値は将来のための予約とする。 +内部動作フラグとメディアバイト偽装などを見越した将来の拡張用。 +*/ +enum { + WINDRV_OPT_REMOVE = 0x00000001, + ///< Bit 0: ファイル削除処理 0:直接 1:ごみ箱 + WINDRV_OPT_ALPHABET = 0x00000020, + ///< Bit 5: ファイル名比較 Alphabet区別 0:なし 1:あり 0:-C 1:+C + WINDRV_OPT_COMPARE_LENGTH = 0x00000040, + ///< Bit 6: ファイル名比較 文字数(未実装) 0:18+3 1:8+3 0:+T 1:-T + WINDRV_OPT_CONVERT_LENGTH = 0x00000080, + ///< Bit 7: ファイル名変換 文字数 0:18+3 1:8+3 0:-A 1:+A + + WINDRV_OPT_CONVERT_SPACE = 0x00000100, + ///< Bit 8: ファイル名変換 スペース 0:なし 1:'_' + WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, + ///< Bit 9: ファイル名変換 無効な文字 0:なし 1:'_' + WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, + ///< Bit10: ファイル名変換 中間のハイフン 0:なし 1:'_' + WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, + ///< Bit11: ファイル名変換 先頭のハイフン 0:なし 1:'_' + WINDRV_OPT_CONVERT_PERIODS = 0x00001000, + ///< Bit12: ファイル名変換 中間のピリオド 0:なし 1:'_' + WINDRV_OPT_CONVERT_PERIOD = 0x00002000, + ///< Bit13: ファイル名変換 先頭のピリオド 0:なし 1:'_' + + WINDRV_OPT_REDUCED_SPACE = 0x00010000, + ///< Bit16: ファイル名短縮 スペース 0:なし 1:短縮 + WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, + ///< Bit17: ファイル名短縮 無効な文字 0:なし 1:短縮 + WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, + ///< Bit18: ファイル名短縮 中間のハイフン 0:なし 1:短縮 + WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, + ///< Bit19: ファイル名短縮 先頭のハイフン 0:なし 1:短縮 + WINDRV_OPT_REDUCED_PERIODS = 0x00100000, + ///< Bit20: ファイル名短縮 中間のピリオド 0:なし 1:短縮 + WINDRV_OPT_REDUCED_PERIOD = 0x00200000, + ///< Bit21: ファイル名短縮 先頭のピリオド 0:なし 1:短縮 + + // Bit24~30 ファイル重複防止マーク 0:自動 1~127:文字 +}; + +/// ファイルシステム動作フラグ +/** +通常は0にする。リードオンリーでマウントしたいドライブの場合は1にする。 +それ以外の値は将来のための予約とする。 +判定が困難なデバイス(自作USBストレージとか)のための保険用。 +*/ +enum { + FSFLAG_WRITE_PROTECT = 0x00000001, + ///< Bit0: 強制書き込み禁止 + FSFLAG_REMOVABLE = 0x00000002, + ///< Bit1: 強制リムーバブルメディア + FSFLAG_MANUAL = 0x00000004, + ///< Bit2: 強制手動イジェクト +}; + +//=========================================================================== +// +/// まるっとリングリスト +/// +/// 先頭(root.next)が最も新しいオブジェクト。 +/// 末尾(root.prev)が最も古い/未使用オブジェクト。 +/// コード効率追求のため、delete時は必ずポインタをアップキャストすること。 +// +//=========================================================================== +class CRing { +public: + // 基本ファンクション + CRing() { Init(); } + ///< デフォルトコンストラクタ + ~CRing() { Remove(); } + ///< デストラクタ final + void Init() { ASSERT(this); next = prev = this; } + ///< 初期化 + + CRing* Next() const { ASSERT(this); return next; } + ///< 次の要素を取得 + CRing* Prev() const { ASSERT(this); return prev; } + ///< 前の要素を取得 + + void Insert(CRing* pRoot) + { + ASSERT(this); + // 該当オブジェクトを切り離し + ASSERT(next); + ASSERT(prev); + next->prev = prev; + prev->next = next; + // リング先頭へ挿入 + ASSERT(pRoot); + ASSERT(pRoot->next); + next = pRoot->next; + prev = pRoot; + pRoot->next->prev = this; + pRoot->next = this; + } + ///< オブジェクト切り離し & リング先頭へ挿入 + + void InsertTail(CRing* pRoot) + { + ASSERT(this); + // 該当オブジェクトを切り離し + ASSERT(next); + ASSERT(prev); + next->prev = prev; + prev->next = next; + // リング末尾へ挿入 + ASSERT(pRoot); + ASSERT(pRoot->prev); + next = pRoot; + prev = pRoot->prev; + pRoot->prev->next = this; + pRoot->prev = this; + } + ///< オブジェクト切り離し & リング末尾へ挿入 + + void InsertRing(CRing* pRoot) + { + ASSERT(this); + if (next == prev) return; + + // リング先頭へ挿入 + ASSERT(pRoot); + ASSERT(pRoot->next); + pRoot->next->prev = prev; + prev->next = pRoot->next; + pRoot->next = next; + next->prev = pRoot; + + // 自分自身を空にする + next = prev = this; + } + ///< 自分以外のオブジェクト切り離し & リング先頭へ挿入 + + void Remove() + { + ASSERT(this); + // 該当オブジェクトを切り離し + ASSERT(next); + ASSERT(prev); + next->prev = prev; + prev->next = next; + // 安全のため自分自身を指しておく (何度切り離しても問題ない) + next = prev = this; + } + ///< オブジェクト切り離し + +private: + CRing* next; ///< 次の要素 + CRing* prev; ///< 前の要素 +}; + +//=========================================================================== +// +/// ディレクトリエントリ ファイル名 +// +//=========================================================================== +class CHostFilename { +public: + // 基本ファンクション + CHostFilename(); + ///< デフォルトコンストラクタ + static size_t Offset() { return offsetof(CHostFilename, m_szHost); } + ///< オフセット位置取得 + + void SetHost(const TCHAR* szHost); + ///< ホスト側の名称を設定 + const TCHAR* GetHost() const { ASSERT(this); return m_szHost; } + ///< ホスト側の名称を取得 + void ConvertHuman(int nCount = -1); + ///< Human68k側の名称を変換 + void CopyHuman(const BYTE* szHuman); + ///< Human68k側の名称を複製 + BOOL isReduce() const; + ///< Human68k側の名称が加工されたか調査 + BOOL isCorrect() const { ASSERT(this); return m_bCorrect; } + ///< Human68k側のファイル名規則に合致しているか調査 + const BYTE* GetHuman() const { ASSERT(this); return m_szHuman; } + ///< Human68kファイル名を取得 + const BYTE* GetHumanLast() const { ASSERT(this); return m_pszHumanLast; } + ///< Human68kファイル名を取得 + const BYTE* GetHumanExt() const { ASSERT(this); return m_pszHumanExt; } + ///< Human68kファイル名を取得 + void SetEntryName(); + ///< Human68kディレクトリエントリを設定 + void SetEntryAttribute(BYTE nHumanAttribute) + { ASSERT(this); m_dirHuman.attr = nHumanAttribute; } + ///< Human68kディレクトリエントリを設定 + void SetEntrySize(DWORD nHumanSize) + { ASSERT(this); m_dirHuman.size = nHumanSize; } + ///< Human68kディレクトリエントリを設定 + void SetEntryDate(WORD nHumanDate) + { ASSERT(this); m_dirHuman.date = nHumanDate; } + ///< Human68kディレクトリエントリを設定 + void SetEntryTime(WORD nHumanTime) + { ASSERT(this); m_dirHuman.time = nHumanTime; } + ///< Human68kディレクトリエントリを設定 + void SetEntryCluster(WORD nHumanCluster) + { ASSERT(this); m_dirHuman.cluster = nHumanCluster; } + ///< Human68kディレクトリエントリを設定 + const Human68k::dirent_t* GetEntry() const { ASSERT(this); return &m_dirHuman; } + ///< Human68kディレクトリエントリを取得 + BOOL CheckAttribute(DWORD nHumanAttribute) const; + ///< Human68kディレクトリエントリの属性判定 + BOOL isSameEntry(const Human68k::dirent_t* pdirHuman) const + { ASSERT(this); ASSERT(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; } + ///< Human68kディレクトリエントリの一致判定 + + // パス名操作 + static const BYTE* SeparateExt(const BYTE* szHuman); + ///< Human68kファイル名から拡張子を分離 + +private: + static BYTE* CopyName(BYTE* pWrite, const BYTE* pFirst, const BYTE* pLast); + ///< Human68k側のファイル名要素をコピー + + const BYTE* m_pszHumanLast; ///< 該当エントリのHuman68k内部名の終端位置 + const BYTE* m_pszHumanExt; ///< 該当エントリのHuman68k内部名の拡張子位置 + BOOL m_bCorrect; ///< 該当エントリのHuman68k内部名が正しければ真 + BYTE m_szHuman[24]; ///< 該当エントリのHuman68k内部名 + Human68k::dirent_t m_dirHuman; ///< 該当エントリのHuman68k全情報 + TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 (可変長) +}; + +//=========================================================================== +// +/// ディレクトリエントリ パス名 +/// +/// Human68k側のパス名は、必ず先頭が/で始まり、末尾が/で終わる。 +/// ユニット番号は持たない。 +/// 高速化のため、ホスト側の名称にはベースパス部分も含む。 +// +//=========================================================================== +/** @note +ほとんどのHuman68kのアプリは、ファイルの更新等によってディレクトリエ +ントリに変更が生じた際、親ディレクトリのタイムスタンプは変化しないも +のと想定して作られている。 +ところがホスト側のファイルシステムでは親ディレクトリのタイムスタンプ +も変化してしまうものが主流となってしまっている。 + +このため、ディレクトリのコピー等において、アプリ側は正確にタイムスタ +ンプ情報などを設定しているにもかかわらず、実行結果では時刻情報が上書 +きされてしまうという惨劇が起きてしまう。 + +そこでディレクトリキャッシュ内にFATタイムスタンプのエミュレーション +機能を実装した。ホスト側のファイルシステムの更新時にタイムスタンプ情 +報を復元することでHuman68k側の期待する結果と一致させる。 +*/ +class CHostPath: public CRing { + /// メモリ管理用 + struct ring_t { + CRing r; ///< 円環 + CHostFilename f; ///< 実体 + }; + +public: + /// 検索用バッファ + struct find_t { + DWORD count; ///< 検索実行回数 + 1 (0のときは以下の値は無効) + DWORD id; ///< 次回検索を続行するパスのエントリ識別ID + const ring_t* pos; ///< 次回検索を続行する位置 (識別ID一致時) + Human68k::dirent_t entry; ///< 次回検索を続行するエントリ内容 + + void Clear() { count = 0; } + ///< 初期化 + }; + + // 基本ファンクション + CHostPath(); + ///< デフォルトコンストラクタ + ~CHostPath(); + ///< デストラクタ final + void Clean(); + ///< 再利用のための初期化 + + void SetHuman(const BYTE* szHuman); + ///< Human68k側の名称を直接指定する + void SetHost(const TCHAR* szHost); + ///< ホスト側の名称を直接指定する + BOOL isSameHuman(const BYTE* szHuman) const; + ///< Human68k側の名称を比較する + BOOL isSameChild(const BYTE* szHuman) const; + ///< Human68k側の名称を比較する + const TCHAR* GetHost() const { ASSERT(this); return m_szHost; } + ///< ホスト側の名称の獲得 + const CHostFilename* FindFilename(const BYTE* szHuman, DWORD nHumanAttribute = Human68k::AT_ALL) const; + ///< ファイル名を検索 + const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const; + ///< ファイル名を検索 (ワイルドカード対応) + BOOL isRefresh(); + ///< ファイル変更が行なわれたか確認 + void Refresh(); + ///< ファイル再構成 + void Backup(); + /// ホスト側のタイムスタンプを保存 + void Restore() const; + /// ホスト側のタイムスタンプを復元 + void Release(); + ///< 更新 + + // CHostEntryが利用する外部API + static void InitId() { g_nId = 0; } + ///< 識別ID生成用カウンタ初期化 + +private: + static ring_t* Alloc(size_t nLength); + ///< ファイル名領域確保 + static void Free(ring_t* pRing); + ///< ファイル名領域解放 + static int Compare(const BYTE* pFirst, const BYTE* pLast, const BYTE* pBufFirst, const BYTE* pBufLast); + ///< 文字列比較 (ワイルドカード対応) + + CRing m_cRing; ///< CHostFilename連結用 +#ifndef BAREMETAL + time_t m_tBackup; ///< 時刻復元用 +#else + WORD m_tBackupD; ///< 時刻復元用 + WORD m_tBackupT; ///< 時刻復元用 +#endif // BAREMETAL + BOOL m_bRefresh; ///< 更新フラグ + DWORD m_nId; ///< 識別ID (値が変化した場合は更新を意味する) + BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< 該当エントリのHuman68k内部名 + TCHAR m_szHost[FILEPATH_MAX]; ///< 該当エントリのホスト側の名称 + + static DWORD g_nId; ///< 識別ID生成用カウンタ +}; + +//=========================================================================== +// +/// ファイル検索処理 +/// +/// Human68k側のファイル名を内部Unicodeで処理するのは正直キツい。と +/// いうわけで、全てBYTEに変換して処理する。変換処理はディレクトリエ +/// ントリキャッシュが一手に担い、WINDRV側はすべてシフトJISのみで扱 +/// えるようにする。 +/// また、Human68k側名称は、完全にベースパス指定から独立させる。 +/// +/// ファイルを扱う直前に、ディレクトリエントリのキャッシュを生成する。 +/// ディレクトリエントリの生成処理は高コストのため、一度生成したエントリは +/// 可能な限り維持して使い回す。 +/// +/// ファイル検索は3方式。すべてCHostFiles::Find()で処理する。 +/// 1. パス名のみ検索 属性はディレクトリのみ _CHKDIR _CREATE +/// 2. パス名+ファイル名+属性の検索 _OPEN +/// 3. パス名+ワイルドカード+属性の検索 _FILES _NFILES +/// 検索結果は、ディレクトリエントリ情報として保持しておく。 +// +//=========================================================================== +class CHostFiles { +public: + // 基本ファンクション + CHostFiles() { SetKey(0); Init(); } + ///< デフォルトコンストラクタ + void Init(); + ///< 初期化 + + void SetKey(DWORD nKey) { ASSERT(this); m_nKey = nKey; } + ///< 検索キー設定 + BOOL isSameKey(DWORD nKey) const { ASSERT(this); return m_nKey == nKey; } + ///< 検索キー比較 + void SetPath(const Human68k::namests_t* pNamests); + ///< パス名・ファイル名を内部で生成 + BOOL isRootPath() const { return m_szHumanPath[1] == '\0'; } + ///< ルートディレクトリ判定 + void SetPathWildcard() { m_nHumanWildcard = 1; } + ///< ワイルドカードによるファイル検索を有効化 + void SetPathOnly() { m_nHumanWildcard = 0xFF; } + ///< パス名のみを有効化 + BOOL isPathOnly() const { return m_nHumanWildcard == 0xFF; } + ///< パス名のみ設定か判定 + void SetAttribute(DWORD nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } + ///< 検索属性を設定 + BOOL Find(DWORD nUnit, class CHostEntry* pEntry); + ///< Human68k側でファイルを検索しホスト側の情報を生成 + const CHostFilename* Find(CHostPath* pPath); + ///< ファイル名検索 + void SetEntry(const CHostFilename* pFilename); + ///< Human68k側の検索結果保存 + void SetResult(const TCHAR* szPath); + ///< ホスト側の名称を設定 + void AddResult(const TCHAR* szPath); + ///< ホスト側の名称にファイル名を追加 + void AddFilename(); + ///< ホスト側の名称にHuman68kの新規ファイル名を追加 + + const TCHAR* GetPath() const { ASSERT(this); return m_szHostResult; } + ///< ホスト側の名称を取得 + const Human68k::dirent_t* GetEntry() const { ASSERT(this); return &m_dirHuman; } + ///< Human68kディレクトリエントリを取得 + DWORD GetAttribute() const { ASSERT(this); return m_dirHuman.attr; } + ///< Human68k属性を取得 + WORD GetDate() const { ASSERT(this); return m_dirHuman.date; } + ///< Human68k日付を取得 + WORD GetTime() const { ASSERT(this); return m_dirHuman.time; } + ///< Human68k時刻を取得 + DWORD GetSize() const { ASSERT(this); return m_dirHuman.size; } + ///< Human68kファイルサイズを取得 + const BYTE* GetHumanFilename() const { ASSERT(this); return m_szHumanFilename; } + ///< Human68kファイル名を取得 + const BYTE* GetHumanResult() const { ASSERT(this); return m_szHumanResult; } + ///< Human68kファイル名検索結果を取得 + const BYTE* GetHumanPath() const { ASSERT(this); return m_szHumanPath; } + ///< Human68kパス名を取得 + +private: + DWORD m_nKey; ///< Human68kのFILESバッファアドレス 0なら未使用 + DWORD m_nHumanWildcard; ///< Human68kのワイルドカード情報 + DWORD m_nHumanAttribute; ///< Human68kの検索属性 + CHostPath::find_t m_findNext; ///< 次回検索位置情報 + Human68k::dirent_t m_dirHuman; ///< 検索結果 Human68kファイル情報 + BYTE m_szHumanFilename[24]; ///< Human68kのファイル名 + BYTE m_szHumanResult[24]; ///< 検索結果 Human68kファイル名 + BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; + ///< Human68kのパス名 + TCHAR m_szHostResult[FILEPATH_MAX]; ///< 検索結果 ホスト側のフルパス名 +}; + +//=========================================================================== +// +/// ファイル検索領域 マネージャ +// +//=========================================================================== +class CHostFilesManager { +public: +#ifdef _DEBUG + // 基本ファンクション + ~CHostFilesManager(); + ///< デストラクタ final +#endif // _DEBUG + void Init(); + ///< 初期化 (ドライバ組込み時) + void Clean(); + ///< 解放 (起動・リセット時) + + CHostFiles* Alloc(DWORD nKey); + ///< 確保 + CHostFiles* Search(DWORD nKey); + ///< 検索 + void Free(CHostFiles* pFiles); + ///< 解放 +private: + /// メモリ管理用 + struct ring_t { + CRing r; ///< 円環 + CHostFiles f; ///< 実体 + }; + + CRing m_cRing; ///< CHostFiles連結用 +}; + +//=========================================================================== +// +/// FCB処理 +// +//=========================================================================== +class CHostFcb { +public: + // 基本ファンクション + CHostFcb() { SetKey(0); Init(); } + ///< デフォルトコンストラクタ + ~CHostFcb() { Close(); } + ///< デストラクタ final + void Init(); + ///< 初期化 + + void SetKey(DWORD nKey) { ASSERT(this); m_nKey = nKey; } + ///< 検索キー設定 + BOOL isSameKey(DWORD nKey) const { ASSERT(this); return m_nKey == nKey; } + ///< 検索キー比較 + void SetUpdate() { ASSERT(this); m_bUpdate = TRUE; } + ///< 更新 + BOOL isUpdate() const { ASSERT(this); return m_bUpdate; } + ///< 更新状態取得 + BOOL SetMode(DWORD nHumanMode); + ///< ファイルオープンモードを設定 + void SetFilename(const TCHAR* szFilename); + ///< ファイル名を設定 + void SetHumanPath(const BYTE* szHumanPath); + ///< Human68kパス名を設定 + const BYTE* GetHumanPath() const { ASSERT(this); return m_szHumanPath; } + ///< Human68kパス名を取得 + + BOOL Create(Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); + ///< ファイル作成 + BOOL Open(); + ///< ファイルオープン + BOOL Rewind(DWORD nOffset); + ///< ファイルシーク + DWORD Read(BYTE* pBuffer, DWORD nSize); + ///< ファイル読み込み + DWORD Write(const BYTE* pBuffer, DWORD nSize); + ///< ファイル書き込み + BOOL Truncate(); + ///< ファイル切り詰め + DWORD Seek(DWORD nOffset, DWORD nHumanSeek); + ///< ファイルシーク + BOOL TimeStamp(DWORD nHumanTime); + ///< ファイル時刻設定 + BOOL Close(); + ///< ファイルクローズ + +private: + DWORD m_nKey; ///< Human68kのFCBバッファアドレス (0なら未使用) + BOOL m_bUpdate; ///< 更新フラグ +#ifndef BAREMETAL + FILE* m_pFile; ///< ホスト側のファイルオブジェクト + const char* m_pszMode; ///< ホスト側のファイルオープンモード +#else + FIL m_File; ///< ホスト側のファイルオブジェクト + BYTE m_Mode; ///< ホスト側のファイルオープンモード +#endif // BAREMETAL + bool m_bFlag; ///< ホスト側のファイルオープンフラグ + BYTE m_szHumanPath[HUMAN68K_PATH_MAX]; + ///< Human68kのパス名 + TCHAR m_szFilename[FILEPATH_MAX]; ///< ホスト側のファイル名 +}; + +//=========================================================================== +// +/// FCB処理 マネージャ +// +//=========================================================================== +class CHostFcbManager { +public: +#ifdef _DEBUG + // 基本ファンクション + ~CHostFcbManager(); + ///< デストラクタ final +#endif // _DEBUG + void Init(); + ///< 初期化 (ドライバ組込み時) + void Clean(); + ///< 解放 (起動・リセット時) + + CHostFcb* Alloc(DWORD nKey); + ///< 確保 + CHostFcb* Search(DWORD nKey); + ///< 検索 + void Free(CHostFcb* p); + ///< 解放 + +private: + /// メモリ管理用 + struct ring_t { + CRing r; ///< 円環 + CHostFcb f; ///< 実体 + }; + + CRing m_cRing; ///< CHostFcb連結用 +}; + +//=========================================================================== +// +/// ホスト側ドライブ +/// +/// ドライブ毎に必要な情報の保持に専念し、管理はCHostEntryで行なう。 +// +//=========================================================================== +class CHostDrv +{ +public: + // 基本ファンクション + CHostDrv(); + ///< デフォルトコンストラクタ + ~CHostDrv(); + ///< デストラクタ final + void Init(const TCHAR* szBase, DWORD nFlag); + ///< 初期化 (デバイス起動とロード) + + BOOL isWriteProtect() const { ASSERT(this); return m_bWriteProtect; } + ///< 書き込み禁止か? + BOOL isEnable() const { ASSERT(this); return m_bEnable; } + ///< アクセス可能か? + BOOL isMediaOffline(); + ///< メディアチェック + BYTE GetMediaByte() const; + ///< メディアバイトの取得 + DWORD GetStatus() const; + ///< ドライブ状態の取得 + void SetEnable(BOOL bEnable); + ///< メディア状態設定 + BOOL CheckMedia(); + ///< メディア交換チェック + void Update(); + ///< メディア状態更新 + void Eject(); + ///< イジェクト + void GetVolume(TCHAR* szLabel); + ///< ボリュームラベルの取得 + BOOL GetVolumeCache(TCHAR* szLabel) const; + ///< キャッシュからボリュームラベルを取得 + DWORD GetCapacity(Human68k::capacity_t* pCapacity); + ///< 容量の取得 + BOOL GetCapacityCache(Human68k::capacity_t* pCapacity) const; + ///< キャッシュから容量を取得 + + // キャッシュ操作 + void CleanCache(); + ///< 全てのキャッシュを更新する + void CleanCache(const BYTE* szHumanPath); + ///< 指定されたパスのキャッシュを更新する + void CleanCacheChild(const BYTE* szHumanPath); + ///< 指定されたパス以下のキャッシュを全て更新する + void DeleteCache(const BYTE* szHumanPath); + ///< 指定されたパスのキャッシュを削除する + CHostPath* FindCache(const BYTE* szHuman); + ///< 指定されたパスがキャッシュされているか検索する + CHostPath* CopyCache(CHostFiles* pFiles); + ///< キャッシュ情報を元に、ホスト側の名称を獲得する + CHostPath* MakeCache(CHostFiles* pFiles); + ///< ホスト側の名称の構築に必要な情報をすべて取得する + BOOL Find(CHostFiles* pFiles); + ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) + +private: + // パス名操作 + static const BYTE* SeparateCopyFilename(const BYTE* szHuman, BYTE* szBuffer); + ///< Human68kフルパス名から先頭の要素を分離・コピー + + // 排他制御 + void Lock() {} + void Unlock() {} + + /// メモリ管理用 + struct ring_t { + CRing r; ///< 円環 + CHostPath f; ///< 実体 + }; + + BOOL m_bWriteProtect; ///< 書き込み禁止ならTRUE + BOOL m_bEnable; ///< メディアが利用可能ならTRUE + DWORD m_nRing; ///< パス名保持数 + CRing m_cRing; ///< CHostPath連結用 + Human68k::capacity_t m_capCache; ///< セクタ情報キャッシュ sectors == 0 なら未キャッシュ + BOOL m_bVolumeCache; ///< ボリュームラベル読み込み済みならTRUE + TCHAR m_szVolumeCache[24]; ///< ボリュームラベルキャッシュ + TCHAR m_szBase[FILEPATH_MAX]; ///< ベースパス +}; + +//=========================================================================== +// +/// ディレクトリエントリ管理 +// +//=========================================================================== +class CHostEntry { +public: + // 基本ファンクション + CHostEntry(); + ///< デフォルトコンストラクタ + ~CHostEntry(); + ///< デストラクタ final + void Init(); + ///< 初期化 (ドライバ組込み時) + void Clean(); + ///< 解放 (起動・リセット時) + + // キャッシュ操作 + void CleanCache(); + ///< 全てのキャッシュを更新する + void CleanCache(DWORD nUnit); + ///< 指定されたユニットのキャッシュを更新する + void CleanCache(DWORD nUnit, const BYTE* szHumanPath); + ///< 指定されたパスのキャッシュを更新する + void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath); + ///< 指定されたパス以下のキャッシュを全て更新する + void DeleteCache(DWORD nUnit, const BYTE* szHumanPath); + ///< 指定されたパスのキャッシュを削除する + BOOL Find(DWORD nUnit, CHostFiles* pFiles); + ///< ホスト側の名称を検索 (パス名+ファイル名(省略可)+属性) + void ShellNotify(DWORD nEvent, const TCHAR* szPath); + ///< ホスト側ファイルシステム状態変化通知 + + // ドライブオブジェクト操作 + void SetDrv(DWORD nUnit, CHostDrv* pDrv); + ///< ドライブ設定 + BOOL isWriteProtect(DWORD nUnit) const; + ///< 書き込み禁止か? + BOOL isEnable(DWORD nUnit) const; + ///< アクセス可能か? + BOOL isMediaOffline(DWORD nUnit); + ///< メディアチェック + BYTE GetMediaByte(DWORD nUnit) const; + ///< メディアバイトの取得 + DWORD GetStatus(DWORD nUnit) const; + ///< ドライブ状態の取得 + BOOL CheckMedia(DWORD nUnit); + ///< メディア交換チェック + void Eject(DWORD nUnit); + ///< イジェクト + void GetVolume(DWORD nUnit, TCHAR* szLabel); + ///< ボリュームラベルの取得 + BOOL GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; + ///< キャッシュからボリュームラベルを取得 + DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); + ///< 容量の取得 + BOOL GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const; + ///< キャッシュからクラスタサイズを取得 + + /// 定数 + enum { + DriveMax = 10 ///< ドライブ最大候補数 + }; + +private: + CHostDrv* m_pDrv[DriveMax]; ///< ホスト側ドライブオブジェクト + DWORD m_nTimeout; ///< 最後にタイムアウトチェックを行なった時刻 +}; + +//=========================================================================== +// +/// ホスト側ファイルシステム +// +//=========================================================================== +/** @note +現在の見解。 + +XM6の設計思想とは反するが、class Windrvまたはclass CWindrvに直接 +class CFileSysへのポインタを持たせる方法を模索するべきである。 +これにより、以下のメリットが得られる。 + +メリットその1。 +コマンドハンドラの大量のメソッド群を一ヶ所で集中管理できる。 +コマンドハンドラはホスト側の仕様変更などの要因によって激しく変化する +可能性が高いため、メソッドの追加削除や引数の変更などのメンテナンスが +大幅に楽になる。 + +メリットその2。 +仮想関数のテーブル生成・参照処理に関する処理コードを駆逐できる。 +XM6では複数のファイルシステムオブジェクトを同時に使うような実装は +ありえない。つまりファイルシステムオブジェクトにポリモーフィズムは +まったく必要ないどころか、ただクロックの無駄となっているだけである。 + +試しに変えてみた。実際効率上がった。 +windrv.h内のFILESYS_FAST_STRUCTUREの値を変えてコンパイラの吐くソース +を比較すれば一目瞭然。何故私がこんな長文を書こうと思ったのかを理解で +きるはず。 + +一方ロシアはclass CWindrv内にclass CFileSysを直接設置した。 +(本当はclass CHostを廃止して直接置きたい……良い方法はないものか……) +*/ +class CFileSys +{ +public: + // 基本ファンクション + CFileSys(); + ///< デフォルトコンストラクタ + virtual ~CFileSys() {}; + ///< デストラクタ + + // 初期化・終了 + void Reset(); + ///< リセット (全クローズ) + void Init(); + ///< 初期化 (デバイス起動とロード) + + // コマンドハンドラ + DWORD InitDevice(const Human68k::argument_t* pArgument); + ///< $40 - デバイス起動 + int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests); + ///< $41 - ディレクトリチェック + int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests); + ///< $42 - ディレクトリ作成 + int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests); + ///< $43 - ディレクトリ削除 + int Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew); + ///< $44 - ファイル名変更 + int Delete(DWORD nUnit, const Human68k::namests_t* pNamests); + ///< $45 - ファイル削除 + int Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute); + ///< $46 - ファイル属性取得/設定 + int Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles); + ///< $47 - ファイル検索 + int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); + ///< $48 - ファイル次検索 + int Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, BOOL bForce); + ///< $49 - ファイル作成 + int Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb); + ///< $4A - ファイルオープン + int Close(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb); + ///< $4B - ファイルクローズ + int Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, DWORD nSize); + ///< $4C - ファイル読み込み + int Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, DWORD nSize); + ///< $4D - ファイル書き込み + int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); + ///< $4E - ファイルシーク + DWORD TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime); + ///< $4F - ファイル時刻取得/設定 + int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity); + ///< $50 - 容量取得 + int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive); + ///< $51 - ドライブ状態検査/制御 + int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb); + ///< $52 - DPB取得 + int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); + ///< $53 - セクタ読み込み + int DiskWrite(DWORD nUnit); + ///< $54 - セクタ書き込み + int Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl); + ///< $55 - IOCTRL + int Flush(DWORD nUnit); + ///< $56 - フラッシュ + int CheckMedia(DWORD nUnit); + ///< $57 - メディア交換チェック + int Lock(DWORD nUnit); + ///< $58 - 排他制御 + + void SetOption(DWORD nOption); + ///< オプション設定 + DWORD GetOption() const { ASSERT(this); return m_nOption; } + ///< オプション取得 + DWORD GetDefault() const { ASSERT(this); return m_nOptionDefault; } + ///< デフォルトオプション取得 + static DWORD GetFileOption() { return g_nOption; } + ///< ファイル名変換オプション取得 + void ShellNotify(DWORD nEvent, const TCHAR* szPath) + { ASSERT(this); m_cEntry.ShellNotify(nEvent, szPath); } + ///< ホスト側ファイルシステム状態変化通知 + + /// 定数 + enum { + DriveMax = CHostEntry::DriveMax ///< ドライブ最大候補数 + }; + +private: + // 内部補助用 + void InitOption(const Human68k::argument_t* pArgument); + ///< オプション初期化 + BOOL FilesVolume(DWORD nUnit, Human68k::files_t* pFiles); + ///< ボリュームラベル取得 + + DWORD m_nUnits; ///< 現在のドライブオブジェクト数 (レジューム毎に変化) + + DWORD m_nOption; ///< 現在の動作フラグ + DWORD m_nOptionDefault; ///< リセット時の動作フラグ + + DWORD m_nDrives; ///< ベースパス状態復元用の候補数 (0なら毎回スキャン) + + DWORD m_nKernel; ///< カーネルチェック用カウンタ + DWORD m_nKernelSearch; ///< NULデバイスの先頭アドレス + + DWORD m_nHostSectorCount; ///< 擬似セクタ番号 + + CHostFilesManager m_cFiles; ///< ファイル検索領域 + CHostFcbManager m_cFcb; ///< FCB操作領域 + CHostEntry m_cEntry; ///< ドライブオブジェクトとディレクトリエントリ + + DWORD m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX]; + ///< 擬似セクタの指すファイル実体 + + DWORD m_nFlag[DriveMax]; ///< ベースパス状態復元用の動作フラグ候補 + TCHAR m_szBase[DriveMax][FILEPATH_MAX]; + ///< ベースパス状態復元用の候補 + static DWORD g_nOption; ///< ファイル名変換フラグ +}; + +#endif // cfilesystem_h diff --git a/src/raspberrypi/devices/ctapdriver.cpp b/src/raspberrypi/devices/ctapdriver.cpp new file mode 100644 index 00000000..47536574 --- /dev/null +++ b/src/raspberrypi/devices/ctapdriver.cpp @@ -0,0 +1,209 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +// Imported NetBSD support and some optimisation patch by Rin Okuyama. +// +// [ TAP Driver ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "xm6.h" +#include "ctapdriver.h" + +//--------------------------------------------------------------------------- +// +// コンストラクタ +// +//--------------------------------------------------------------------------- +CTapDriver::CTapDriver() +{ + // 初期化 + m_hTAP = -1; + memset(&m_MacAddr, 0, sizeof(m_MacAddr)); +} + +//--------------------------------------------------------------------------- +// +// 初期化 +// +//--------------------------------------------------------------------------- +#ifdef __linux__ +BOOL FASTCALL CTapDriver::Init() +{ + char dev[IFNAMSIZ] = "ras0"; + struct ifreq ifr; + int ret; + + ASSERT(this); + + // TAPデバイス初期化 + if ((m_hTAP = open("/dev/net/tun", O_RDWR)) < 0) { + printf("Error: can't open tun\n"); + return FALSE; + } + + // IFF_NO_PI for no extra packet information + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if ((ret = ioctl(m_hTAP, TUNSETIFF, (void *)&ifr)) < 0) { + printf("Error: can't ioctl TUNSETIFF\n"); + close(m_hTAP); + return FALSE; + } + + // MACアドレス取得 + ifr.ifr_addr.sa_family = AF_INET; + if ((ret = ioctl(m_hTAP, SIOCGIFHWADDR, &ifr)) < 0) { + printf("Error: can't ioctl SIOCGIFHWADDR\n"); + close(m_hTAP); + return FALSE; + } + + // MACアドレス保存 + memcpy(m_MacAddr, ifr.ifr_hwaddr.sa_data, sizeof(m_MacAddr)); + return TRUE; +} +#endif // __linux__ + +#ifdef __NetBSD__ +BOOL FASTCALL CTapDriver::Init() +{ + struct ifreq ifr; + struct ifaddrs *ifa, *a; + + ASSERT(this); + + // TAPデバイス初期化 + if ((m_hTAP = open("/dev/tap", O_RDWR)) < 0) { + printf("Error: can't open tap\n"); + return FALSE; + } + + // デバイス名取得 + if (ioctl(m_hTAP, TAPGIFNAME, (void *)&ifr) < 0) { + printf("Error: can't ioctl TAPGIFNAME\n"); + close(m_hTAP); + return FALSE; + } + + // MACアドレス取得 + if (getifaddrs(&ifa) == -1) { + printf("Error: can't getifaddrs\n"); + close(m_hTAP); + return FALSE; + } + for (a = ifa; a != NULL; a = a->ifa_next) + if (strcmp(ifr.ifr_name, a->ifa_name) == 0 && + a->ifa_addr->sa_family == AF_LINK) + break; + if (a == NULL) { + printf("Error: can't get MAC address\n"); + close(m_hTAP); + return FALSE; + } + + // MACアドレス保存 + memcpy(m_MacAddr, LLADDR((struct sockaddr_dl *)a->ifa_addr), + sizeof(m_MacAddr)); + freeifaddrs(ifa); + + printf("Tap device : %s\n", ifr.ifr_name); + + return TRUE; +} +#endif // __NetBSD__ + +//--------------------------------------------------------------------------- +// +// クリーンアップ +// +//--------------------------------------------------------------------------- +void FASTCALL CTapDriver::Cleanup() +{ + ASSERT(this); + + // TAPデバイス解放 + if (m_hTAP != -1) { + close(m_hTAP); + m_hTAP = -1; + } +} + +//--------------------------------------------------------------------------- +// +// MACアドレス取得 +// +//--------------------------------------------------------------------------- +void FASTCALL CTapDriver::GetMacAddr(BYTE *mac) +{ + ASSERT(this); + ASSERT(mac); + + memcpy(mac, m_MacAddr, sizeof(m_MacAddr)); +} + +//--------------------------------------------------------------------------- +// +// 受信 +// +//--------------------------------------------------------------------------- +int FASTCALL CTapDriver::Rx(BYTE *buf) +{ + struct pollfd fds; + DWORD dwReceived; + + ASSERT(this); + ASSERT(m_hTAP != -1); + + // 受信可能なデータがあるか調べる + fds.fd = m_hTAP; + fds.events = POLLIN | POLLERR; + fds.revents = 0; + poll(&fds, 1, 0); + if (!(fds.revents & POLLIN)) { + return 0; + } + + // 受信 + dwReceived = read(m_hTAP, buf, ETH_FRAME_LEN); + if (dwReceived == (DWORD)-1) { + return 0; + } + + // 受信が有効であれば + if (dwReceived > 0) { + // FCSを除く最小フレームサイズ(60バイト)にパディング + if (dwReceived < 60) { + memset(buf + dwReceived, 0, 60 - dwReceived); + dwReceived = 60; + } + + // ダミーのFCSを付加する + memset(buf + dwReceived, 0, 4); + dwReceived += 4; + } + + // バイト数を返却 + return dwReceived; +} + +//--------------------------------------------------------------------------- +// +// 送信 +// +//--------------------------------------------------------------------------- +int FASTCALL CTapDriver::Tx(BYTE *buf, int len) +{ + ASSERT(this); + ASSERT(m_hTAP != -1); + + // 送信開始 + return write(m_hTAP, buf, len); +} diff --git a/src/raspberrypi/devices/ctapdriver.h b/src/raspberrypi/devices/ctapdriver.h new file mode 100644 index 00000000..c4735c77 --- /dev/null +++ b/src/raspberrypi/devices/ctapdriver.h @@ -0,0 +1,53 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// +// Imported NetBSD support and some optimisation patch by Rin Okuyama. +// +// [ TAP Driver ] +// +//--------------------------------------------------------------------------- + +#if !defined(ctapdriver_h) +#define ctapdriver_h + +#ifndef ETH_FRAME_LEN +#define ETH_FRAME_LEN 1514 +#endif + +//=========================================================================== +// +// Tapドライバ +// +//=========================================================================== +class CTapDriver +{ +public: + // 基本ファンクション + CTapDriver(); + // コンストラクタ + BOOL FASTCALL Init(); + // 初期化 + void FASTCALL Cleanup(); + // クリーンアップ + void FASTCALL GetMacAddr(BYTE *mac); + // MACアドレス取得 + int FASTCALL Rx(BYTE *buf); + // 受信 + int FASTCALL Tx(BYTE *buf, int len); + // 送信 + +private: + BYTE m_MacAddr[6]; + // MACアドレス + BOOL m_bTxValid; + // 送信有効フラグ + int m_hTAP; + // ディスクプリタ +}; + +#endif // ctapdriver_h diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp new file mode 100644 index 00000000..a05f27ac --- /dev/null +++ b/src/raspberrypi/devices/disk.cpp @@ -0,0 +1,2346 @@ +//--------------------------------------------------------------------------- +// +// X68000 EMULATOR "XM6" +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// +// XM6i +// Copyright (C) 2010-2015 isaki@NetBSD.org +// Copyright (C) 2010 Y.Sugahara +// +// Imported sava's Anex86/T98Next image and MO format support patch. +// Imported NetBSD support and some optimisation patch by Rin Okuyama. +// Comments translated to english by akuker. +// +// [ Disk ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "xm6.h" +#include "filepath.h" +#include "fileio.h" +#ifdef RASCSI +#include "gpiobus.h" +#ifndef BAREMETAL +#include "ctapdriver.h" +#endif // BAREMETAL +#include "cfilesystem.h" +#include "disk.h" +#else +#include "vm.h" +#include "disk.h" +#include "windrv.h" +#include "ctapdriver.h" +#include "mfc_com.h" +#include "mfc_host.h" +#endif // RASCSI + +//=========================================================================== +// +// Disk +// +//=========================================================================== + + +//=========================================================================== +// +// Disk Track +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +DiskTrack::DiskTrack() +{ + // Initialization of internal information + dt.track = 0; + dt.size = 0; + dt.sectors = 0; + dt.raw = FALSE; + dt.init = FALSE; + dt.changed = FALSE; + dt.length = 0; + dt.buffer = NULL; + dt.maplen = 0; + dt.changemap = NULL; + dt.imgoffset = 0; +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +DiskTrack::~DiskTrack() +{ + // Release memory, but do not save automatically + if (dt.buffer) { + free(dt.buffer); + dt.buffer = NULL; + } + if (dt.changemap) { + free(dt.changemap); + dt.changemap = NULL; + } +} + +//--------------------------------------------------------------------------- +// +// Initialization +// +//--------------------------------------------------------------------------- +void FASTCALL DiskTrack::Init( + int track, int size, int sectors, BOOL raw, off64_t imgoff) +{ + ASSERT(track >= 0); + ASSERT((size >= 8) && (size <= 11)); + ASSERT((sectors > 0) && (sectors <= 0x100)); + ASSERT(imgoff >= 0); + + // Set Parameters + dt.track = track; + dt.size = size; + dt.sectors = sectors; + dt.raw = raw; + + // Not initialized (needs to be loaded) + dt.init = FALSE; + + // Not Changed + dt.changed = FALSE; + + // Offset to actual data + dt.imgoffset = imgoff; +} + +//--------------------------------------------------------------------------- +// +// Load +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskTrack::Load(const Filepath& path) +{ + Fileio fio; + off64_t offset; + int i; + int length; + + ASSERT(this); + + // Not needed if already loaded + if (dt.init) { + ASSERT(dt.buffer); + ASSERT(dt.changemap); + return TRUE; + } + + // Calculate offset (previous tracks are considered to + // hold 256 sectors) + offset = ((off64_t)dt.track << 8); + if (dt.raw) { + ASSERT(dt.size == 11); + offset *= 0x930; + offset += 0x10; + } else { + offset <<= dt.size; + } + + // Add offset to real image + offset += dt.imgoffset; + + // Calculate length (data size of this track) + length = dt.sectors << dt.size; + + // Allocate buffer memory + ASSERT((dt.size >= 8) && (dt.size <= 11)); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + + if (dt.buffer == NULL) { +#if defined(RASCSI) && !defined(BAREMETAL) + posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512); +#else + dt.buffer = (BYTE *)malloc(length * sizeof(BYTE)); +#endif // RASCSI && !BAREMETAL + dt.length = length; + } + + if (!dt.buffer) { + return FALSE; + } + + // Reallocate if the buffer length is different + if (dt.length != (DWORD)length) { + free(dt.buffer); +#if defined(RASCSI) && !defined(BAREMETAL) + posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512); +#else + dt.buffer = (BYTE *)malloc(length * sizeof(BYTE)); +#endif // RASCSI && !BAREMETAL + dt.length = length; + } + + // Reserve change map memory + if (dt.changemap == NULL) { + dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); + dt.maplen = dt.sectors; + } + + if (!dt.changemap) { + return FALSE; + } + + // Reallocate if the buffer length is different + if (dt.maplen != (DWORD)dt.sectors) { + free(dt.changemap); + dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); + dt.maplen = dt.sectors; + } + + // Clear changemap + memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); + + // Read from File +#if defined(RASCSI) && !defined(BAREMETAL) + if (!fio.OpenDIO(path, Fileio::ReadOnly)) { +#else + if (!fio.Open(path, Fileio::ReadOnly)) { +#endif // RASCSI && !BAREMETAL + return FALSE; + } + if (dt.raw) { + // Split Reading + for (i = 0; i < dt.sectors; i++) { + // Seek + if (!fio.Seek(offset)) { + fio.Close(); + return FALSE; + } + + // Read + if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) { + fio.Close(); + return FALSE; + } + + // Next offset + offset += 0x930; + } + } else { + // Continuous reading + if (!fio.Seek(offset)) { + fio.Close(); + return FALSE; + } + if (!fio.Read(dt.buffer, length)) { + fio.Close(); + return FALSE; + } + } + fio.Close(); + + // Set a flag and end normally + dt.init = TRUE; + dt.changed = FALSE; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Save +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskTrack::Save(const Filepath& path) +{ + off64_t offset; + int i; + int j; + Fileio fio; + int length; + int total; + + ASSERT(this); + + // Not needed if not initialized + if (!dt.init) { + return TRUE; + } + + // Not needed unless changed + if (!dt.changed) { + return TRUE; + } + + // Need to write + ASSERT(dt.buffer); + ASSERT(dt.changemap); + ASSERT((dt.size >= 8) && (dt.size <= 11)); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + + // Writing in RAW mode is not allowed + ASSERT(!dt.raw); + + // Calculate offset (previous tracks are considered to hold 256 + offset = ((off64_t)dt.track << 8); + offset <<= dt.size; + + // Add offset to real image + offset += dt.imgoffset; + + // Calculate length per sector + length = 1 << dt.size; + + // Open file + if (!fio.Open(path, Fileio::ReadWrite)) { + return FALSE; + } + + // Partial write loop + for (i = 0; i < dt.sectors;) { + // If changed + if (dt.changemap[i]) { + // Initialize write size + total = 0; + + // Seek + if (!fio.Seek(offset + ((off64_t)i << dt.size))) { + fio.Close(); + return FALSE; + } + + // Consectutive sector length + for (j = i; j < dt.sectors; j++) { + // end when interrupted + if (!dt.changemap[j]) { + break; + } + + // Add one sector + total += length; + } + + // Write + if (!fio.Write(&dt.buffer[i << dt.size], total)) { + fio.Close(); + return FALSE; + } + + // To unmodified sector + i = j; + } else { + // Next Sector + i++; + } + } + + // Close + fio.Close(); + + // Drop the change flag and exit + memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); + dt.changed = FALSE; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Read Sector +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskTrack::Read(BYTE *buf, int sec) const +{ + ASSERT(this); + ASSERT(buf); + ASSERT((sec >= 0) & (sec < 0x100)); + + // Error if not initialized + if (!dt.init) { + return FALSE; + } + + // // Error if the number of sectors exceeds the valid number + if (sec >= dt.sectors) { + return FALSE; + } + + // Copy + ASSERT(dt.buffer); + ASSERT((dt.size >= 8) && (dt.size <= 11)); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + memcpy(buf, &dt.buffer[(off64_t)sec << dt.size], (off64_t)1 << dt.size); + + // Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Write Sector +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskTrack::Write(const BYTE *buf, int sec) +{ + int offset; + int length; + + ASSERT(this); + ASSERT(buf); + ASSERT((sec >= 0) & (sec < 0x100)); + ASSERT(!dt.raw); + + // Error if not initialized + if (!dt.init) { + return FALSE; + } + + // // Error if the number of sectors exceeds the valid number + if (sec >= dt.sectors) { + return FALSE; + } + + // Calculate offset and length + offset = sec << dt.size; + length = 1 << dt.size; + + // Compare + ASSERT(dt.buffer); + ASSERT((dt.size >= 8) && (dt.size <= 11)); + ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); + if (memcmp(buf, &dt.buffer[offset], length) == 0) { + // 同じものを書き込もうとしているので、正常終了 + return TRUE; + } + + // Copy, change + memcpy(&dt.buffer[offset], buf, length); + dt.changemap[sec] = TRUE; + dt.changed = TRUE; + + // Success + return TRUE; +} + +//=========================================================================== +// +// Disk Cache +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +DiskCache::DiskCache( + const Filepath& path, int size, int blocks, off64_t imgoff) +{ + int i; + + ASSERT((size >= 8) && (size <= 11)); + ASSERT(blocks > 0); + ASSERT(imgoff >= 0); + + // Cache work + for (i = 0; i < CacheMax; i++) { + cache[i].disktrk = NULL; + cache[i].serial = 0; + } + + // Other + serial = 0; + sec_path = path; + sec_size = size; + sec_blocks = blocks; + cd_raw = FALSE; + imgoffset = imgoff; +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +DiskCache::~DiskCache() +{ + // Clear the track + Clear(); +} + +//--------------------------------------------------------------------------- +// +// RAW Mode Setting +// +//--------------------------------------------------------------------------- +void FASTCALL DiskCache::SetRawMode(BOOL raw) +{ + ASSERT(this); + ASSERT(sec_size == 11); + + // Configuration + cd_raw = raw; +} + +//--------------------------------------------------------------------------- +// +// Save +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskCache::Save() +{ + int i; + + ASSERT(this); + + // Save track + for (i = 0; i < CacheMax; i++) { + // Is it a valid track? + if (cache[i].disktrk) { + // Save + if (!cache[i].disktrk->Save(sec_path)) { + return FALSE; + } + } + } + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Get disk cache information +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskCache::GetCache(int index, int& track, DWORD& aserial) const +{ + ASSERT(this); + ASSERT((index >= 0) && (index < CacheMax)); + + // FALSE if unused + if (!cache[index].disktrk) { + return FALSE; + } + + // Set track and serial + track = cache[index].disktrk->GetTrack(); + aserial = cache[index].serial; + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Clear +// +//--------------------------------------------------------------------------- +void FASTCALL DiskCache::Clear() +{ + int i; + + ASSERT(this); + + // Free the cache + for (i = 0; i < CacheMax; i++) { + if (cache[i].disktrk) { + delete cache[i].disktrk; + cache[i].disktrk = NULL; + } + } +} + +//--------------------------------------------------------------------------- +// +// Sector Read +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskCache::Read(BYTE *buf, int block) +{ + int track; + DiskTrack *disktrk; + + ASSERT(this); + ASSERT(sec_size != 0); + + // Update first + Update(); + + // Calculate track (fixed to 256 sectors/track) + track = block >> 8; + + // Get the track data + disktrk = Assign(track); + if (!disktrk) { + return FALSE; + } + + // Read the track data to the cache + return disktrk->Read(buf, (BYTE)block); +} + +//--------------------------------------------------------------------------- +// +// Sector write +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskCache::Write(const BYTE *buf, int block) +{ + int track; + DiskTrack *disktrk; + + ASSERT(this); + ASSERT(sec_size != 0); + + // Update first + Update(); + + // Calculate track (fixed to 256 sectors/track) + track = block >> 8; + + // Get that track data + disktrk = Assign(track); + if (!disktrk) { + return FALSE; + } + + // Write the data to the cache + return disktrk->Write(buf, (BYTE)block); +} + +//--------------------------------------------------------------------------- +// +// Track Assignment +// +//--------------------------------------------------------------------------- +DiskTrack* FASTCALL DiskCache::Assign(int track) +{ + int i; + int c; + DWORD s; + DiskTrack *disktrk; + + ASSERT(this); + ASSERT(sec_size != 0); + ASSERT(track >= 0); + + // First, check if it is already assigned + for (i = 0; i < CacheMax; i++) { + if (cache[i].disktrk) { + if (cache[i].disktrk->GetTrack() == track) { + // Track match + cache[i].serial = serial; + return cache[i].disktrk; + } + } + } + + // Next, check for empty + for (i = 0; i < CacheMax; i++) { + if (!cache[i].disktrk) { + // Try loading + if (Load(i, track)) { + // Success loading + cache[i].serial = serial; + return cache[i].disktrk; + } + + // Load failed + return NULL; + } + } + + // Finally, find the youngest serial number and delete it + + // Set index 0 as candidate c + s = cache[0].serial; + c = 0; + + // Compare candidate with serial and update to smaller one + for (i = 0; i < CacheMax; i++) { + ASSERT(cache[i].disktrk); + + // Compare and update the existing serial + if (cache[i].serial < s) { + s = cache[i].serial; + c = i; + } + } + + // Save this track + if (!cache[c].disktrk->Save(sec_path)) { + return NULL; + } + + // Delete this track + disktrk = cache[c].disktrk; + cache[c].disktrk = NULL; + + // Load + if (Load(c, track, disktrk)) { + // Successful loading + cache[c].serial = serial; + return cache[c].disktrk; + } + + // Load failed + return NULL; +} + +//--------------------------------------------------------------------------- +// +// Load cache +// +//--------------------------------------------------------------------------- +BOOL FASTCALL DiskCache::Load(int index, int track, DiskTrack *disktrk) +{ + int sectors; + + ASSERT(this); + ASSERT((index >= 0) && (index < CacheMax)); + ASSERT(track >= 0); + ASSERT(!cache[index].disktrk); + + // Get the number of sectors on this track + sectors = sec_blocks - (track << 8); + ASSERT(sectors > 0); + if (sectors > 0x100) { + sectors = 0x100; + } + + // Create a disk track + if (disktrk == NULL) { + disktrk = new DiskTrack(); + } + + // Initialize disk track + disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset); + + // Try loading + if (!disktrk->Load(sec_path)) { + // 失敗 + delete disktrk; + return FALSE; + } + + // Allocation successful, work set + cache[index].disktrk = disktrk; + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Update serial number +// +//--------------------------------------------------------------------------- +void FASTCALL DiskCache::Update() +{ + int i; + + ASSERT(this); + + // Update and do nothing except 0 + serial++; + if (serial != 0) { + return; + } + + // Clear serial of all caches (loop in 32bit) + for (i = 0; i < CacheMax; i++) { + cache[i].serial = 0; + } +} + + +//=========================================================================== +// +// Disk +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +Disk::Disk() +{ + // Work initialization + disk.id = MAKEID('N', 'U', 'L', 'L'); + disk.ready = FALSE; + disk.writep = FALSE; + disk.readonly = FALSE; + disk.removable = FALSE; + disk.lock = FALSE; + disk.attn = FALSE; + disk.reset = FALSE; + disk.size = 0; + disk.blocks = 0; + disk.lun = 0; + disk.code = 0; + disk.dcache = NULL; + disk.imgoffset = 0; + + // Other + cache_wb = TRUE; +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +Disk::~Disk() +{ + // Save disk cache + if (disk.ready) { + // Only if ready... + if (disk.dcache) { + disk.dcache->Save(); + } + } + + // Clear disk cache + if (disk.dcache) { + delete disk.dcache; + disk.dcache = NULL; + } +} + +//--------------------------------------------------------------------------- +// +// Reset +// +//--------------------------------------------------------------------------- +void FASTCALL Disk::Reset() +{ + ASSERT(this); + + // no lock, no attention, reset + disk.lock = FALSE; + disk.attn = FALSE; + disk.reset = TRUE; +} + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Save +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Save(Fileio *fio, int ver) +{ + DWORD sz; + DWORD padding; + + ASSERT(this); + ASSERT(fio); + + // Save size + sz = 52; + if (!fio->Write(&sz, sizeof(sz))) { + return FALSE; + } + + // Save entity + PROP_EXPORT(fio, disk.id); + PROP_EXPORT(fio, disk.ready); + PROP_EXPORT(fio, disk.writep); + PROP_EXPORT(fio, disk.readonly); + PROP_EXPORT(fio, disk.removable); + PROP_EXPORT(fio, disk.lock); + PROP_EXPORT(fio, disk.attn); + PROP_EXPORT(fio, disk.reset); + PROP_EXPORT(fio, disk.size); + PROP_EXPORT(fio, disk.blocks); + PROP_EXPORT(fio, disk.lun); + PROP_EXPORT(fio, disk.code); + PROP_EXPORT(fio, padding); + + // Save the path + if (!diskpath.Save(fio, ver)) { + return FALSE; + } + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Load +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Load(Fileio *fio, int ver) +{ + DWORD sz; + disk_t buf; + DWORD padding; + Filepath path; + + ASSERT(this); + ASSERT(fio); + + // Prior to version 2.03, the disk was not saved + if (ver <= 0x0202) { + return TRUE; + } + + // Delete the current disk cache + if (disk.dcache) { + disk.dcache->Save(); + delete disk.dcache; + disk.dcache = NULL; + } + + // Load size + if (!fio->Read(&sz, sizeof(sz))) { + return FALSE; + } + if (sz != 52) { + return FALSE; + } + + // Load into buffer + PROP_IMPORT(fio, buf.id); + PROP_IMPORT(fio, buf.ready); + PROP_IMPORT(fio, buf.writep); + PROP_IMPORT(fio, buf.readonly); + PROP_IMPORT(fio, buf.removable); + PROP_IMPORT(fio, buf.lock); + PROP_IMPORT(fio, buf.attn); + PROP_IMPORT(fio, buf.reset); + PROP_IMPORT(fio, buf.size); + PROP_IMPORT(fio, buf.blocks); + PROP_IMPORT(fio, buf.lun); + PROP_IMPORT(fio, buf.code); + PROP_IMPORT(fio, padding); + + // Load path + if (!path.Load(fio, ver)) { + return FALSE; + } + + // Move only if IDs match + if (disk.id == buf.id) { + // Do nothing if null + if (IsNULL()) { + return TRUE; + } + + // Same type of device as when saving + disk.ready = FALSE; + if (Open(path)) { + // Disk cache is created in Open + // move only properties + if (!disk.readonly) { + disk.writep = buf.writep; + } + disk.lock = buf.lock; + disk.attn = buf.attn; + disk.reset = buf.reset; + disk.lun = buf.lun; + disk.code = buf.code; + + // Loaded successfully + return TRUE; + } + } + + // Disk cache rebuild + if (!IsReady()) { + disk.dcache = NULL; + } else { + disk.dcache = new DiskCache(diskpath, disk.size, disk.blocks); + } + + return TRUE; +} +#endif // RASCSI + +//--------------------------------------------------------------------------- +// +// NULL Check +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::IsNULL() const +{ + ASSERT(this); + + if (disk.id == MAKEID('N', 'U', 'L', 'L')) { + return TRUE; + } + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// SASI Check +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::IsSASI() const +{ + ASSERT(this); + + if (disk.id == MAKEID('S', 'A', 'H', 'D')) { + return TRUE; + } + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// SCSI Check +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::IsSCSI() const +{ + ASSERT(this); + // If this isn't SASI, then it must be SCSI. + return (this->IsSASI()) ? FALSE : TRUE; +} + +//--------------------------------------------------------------------------- +// +// Open +// * Call as a post-process after successful opening in a derived class +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Open(const Filepath& path, BOOL /*attn*/) +{ + Fileio fio; + + ASSERT(this); + ASSERT((disk.size >= 8) && (disk.size <= 11)); + ASSERT(disk.blocks > 0); + + // Ready + disk.ready = TRUE; + + // Cache initialization + ASSERT(!disk.dcache); + disk.dcache = + new DiskCache(path, disk.size, disk.blocks, disk.imgoffset); + + // Can read/write open + if (fio.Open(path, Fileio::ReadWrite)) { + // Write permission, not read only + disk.writep = FALSE; + disk.readonly = FALSE; + fio.Close(); + } else { + // Write protected, read only + disk.writep = TRUE; + disk.readonly = TRUE; + } + + // Not locked + disk.lock = FALSE; + + // Save path + diskpath = path; + + // Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Eject +// +//--------------------------------------------------------------------------- +void FASTCALL Disk::Eject(BOOL force) +{ + ASSERT(this); + + // Can only be ejected if it is removable + if (!disk.removable) { + return; + } + + // If you're not ready, you don't need to eject + if (!disk.ready) { + return; + } + + // Must be unlocked if there is no force flag + if (!force) { + if (disk.lock) { + return; + } + } + + // Remove disk cache + disk.dcache->Save(); + delete disk.dcache; + disk.dcache = NULL; + + // Not ready, no attention + disk.ready = FALSE; + disk.writep = FALSE; + disk.readonly = FALSE; + disk.attn = FALSE; +} + +//--------------------------------------------------------------------------- +// +// Write Protected +// +//--------------------------------------------------------------------------- +void FASTCALL Disk::WriteP(BOOL writep) +{ + ASSERT(this); + + // be ready + if (!disk.ready) { + return; + } + + // Read Only, protect only + if (disk.readonly) { + ASSERT(disk.writep); + return; + } + + // Write protect flag setting + disk.writep = writep; +} + +//--------------------------------------------------------------------------- +// +// Get Disk +// +//--------------------------------------------------------------------------- +void FASTCALL Disk::GetDisk(disk_t *buffer) const +{ + ASSERT(this); + ASSERT(buffer); + + // Assign internal buffer + *buffer = disk; +} + +//--------------------------------------------------------------------------- +// +// Get Path +// +//--------------------------------------------------------------------------- +void FASTCALL Disk::GetPath(Filepath& path) const +{ + path = diskpath; +} + +//--------------------------------------------------------------------------- +// +// Flush +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Flush() +{ + ASSERT(this); + + // Do nothing if there's nothing cached + if (!disk.dcache) { + return TRUE; + } + + // Save cache + return disk.dcache->Save(); +} + +//--------------------------------------------------------------------------- +// +// Check Ready +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::CheckReady() +{ + ASSERT(this); + + // Not ready if reset + if (disk.reset) { + disk.code = DISK_DEVRESET; + disk.reset = FALSE; + return FALSE; + } + + // Not ready if it needs attention + if (disk.attn) { + disk.code = DISK_ATTENTION; + disk.attn = FALSE; + return FALSE; + } + + // Return status if not ready + if (!disk.ready) { + disk.code = DISK_NOTREADY; + return FALSE; + } + + // Initialization with no error + disk.code = DISK_NOERROR; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// INQUIRY +// *You need to be successful at all times +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::Inquiry( + const DWORD* /*cdb*/, BYTE* /*buf*/, DWORD /*major*/, DWORD /*minor*/) +{ + ASSERT(this); + + // default is INQUIRY failure + disk.code = DISK_INVALIDCMD; + return 0; +} + +//--------------------------------------------------------------------------- +// +// REQUEST SENSE +// *SASI is a separate process +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::RequestSense(const DWORD *cdb, BYTE *buf) +{ + int size; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + + // Return not ready only if there are no errors + if (disk.code == DISK_NOERROR) { + if (!disk.ready) { + disk.code = DISK_NOTREADY; + } + } + + // Size determination (according to allocation length) + size = (int)cdb[4]; + ASSERT((size >= 0) && (size < 0x100)); + + // For SCSI-1, transfer 4 bytes when the size is 0 + // (Deleted this specification for SCSI-2) + if (size == 0) { + size = 4; + } + + // Clear the buffer + memset(buf, 0, size); + + // Set 18 bytes including extended sense data + buf[0] = 0x70; + buf[2] = (BYTE)(disk.code >> 16); + buf[7] = 10; + buf[12] = (BYTE)(disk.code >> 8); + buf[13] = (BYTE)disk.code; + + // Clear the code + disk.code = 0x00; + + return size; +} + +//--------------------------------------------------------------------------- +// +// MODE SELECT check +// *Not affected by disk.code +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::SelectCheck(const DWORD *cdb) +{ + int length; + + ASSERT(this); + ASSERT(cdb); + + // Error if save parameters are set instead of SCSIHD + if (disk.id != MAKEID('S', 'C', 'H', 'D')) { + // Error if save parameters are set + if (cdb[1] & 0x01) { + disk.code = DISK_INVALIDCDB; + return 0; + } + } + + // Receive the data specified by the parameter length + length = (int)cdb[4]; + return length; +} + + +//--------------------------------------------------------------------------- +// +// MODE SELECT(10) check +// * Not affected by disk.code +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::SelectCheck10(const DWORD *cdb) +{ + DWORD length; + + ASSERT(this); + ASSERT(cdb); + + // Error if save parameters are set instead of SCSIHD + if (disk.id != MAKEID('S', 'C', 'H', 'D')) { + if (cdb[1] & 0x01) { + disk.code = DISK_INVALIDCDB; + return 0; + } + } + + // Receive the data specified by the parameter length + length = cdb[7]; + length <<= 8; + length |= cdb[8]; + if (length > 0x800) { + length = 0x800; + } + + return (int)length; +} + +//--------------------------------------------------------------------------- +// +// MODE SELECT +// * Not affected by disk.code +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::ModeSelect( + const DWORD* /*cdb*/, const BYTE *buf, int length) +{ + ASSERT(this); + ASSERT(buf); + ASSERT(length >= 0); + + // cannot be set + disk.code = DISK_INVALIDPRM; + + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// MODE SENSE +// *Not affected by disk.code +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::ModeSense(const DWORD *cdb, BYTE *buf) +{ + int page; + int length; + int size; + BOOL valid; + BOOL change; + int ret; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x1a); + + // Get length, clear buffer + length = (int)cdb[4]; + ASSERT((length >= 0) && (length < 0x100)); + memset(buf, 0, length); + + // Get changeable flag + if ((cdb[2] & 0xc0) == 0x40) { + change = TRUE; + } else { + change = FALSE; + } + + // Get page code (0x00 is valid from the beginning) + page = cdb[2] & 0x3f; + if (page == 0x00) { + valid = TRUE; + } else { + valid = FALSE; + } + + // Basic information + size = 4; + + // MEDIUM TYPE + if (disk.id == MAKEID('S', 'C', 'M', 'O')) { + buf[1] = 0x03; // optical reversible or erasable + } + + // DEVICE SPECIFIC PARAMETER + if (disk.writep) { + buf[2] = 0x80; + } + + // add block descriptor if DBD is 0 + if ((cdb[1] & 0x08) == 0) { + // Mode parameter header + buf[3] = 0x08; + + // Only if ready + if (disk.ready) { + // Block descriptor (number of blocks) + buf[5] = (BYTE)(disk.blocks >> 16); + buf[6] = (BYTE)(disk.blocks >> 8); + buf[7] = (BYTE)disk.blocks; + + // Block descriptor (block length) + size = 1 << disk.size; + buf[9] = (BYTE)(size >> 16); + buf[10] = (BYTE)(size >> 8); + buf[11] = (BYTE)size; + } + + // size + size = 12; + } + + // Page code 1(read-write error recovery) + if ((page == 0x01) || (page == 0x3f)) { + size += AddError(change, &buf[size]); + valid = TRUE; + } + + // Page code 3(format device) + if ((page == 0x03) || (page == 0x3f)) { + size += AddFormat(change, &buf[size]); + valid = TRUE; + } + + // Page code 4(drive parameter) + if ((page == 0x04) || (page == 0x3f)) { + size += AddDrive(change, &buf[size]); + valid = TRUE; + } + + // Page code 6(optical) + if (disk.id == MAKEID('S', 'C', 'M', 'O')) { + if ((page == 0x06) || (page == 0x3f)) { + size += AddOpt(change, &buf[size]); + valid = TRUE; + } + } + + // Page code 8(caching) + if ((page == 0x08) || (page == 0x3f)) { + size += AddCache(change, &buf[size]); + valid = TRUE; + } + + // Page code 13(CD-ROM) + if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if ((page == 0x0d) || (page == 0x3f)) { + size += AddCDROM(change, &buf[size]); + valid = TRUE; + } + } + + // Page code 14(CD-DA) + if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if ((page == 0x0e) || (page == 0x3f)) { + size += AddCDDA(change, &buf[size]); + valid = TRUE; + } + } + + // Page (vendor special) + ret = AddVendor(page, change, &buf[size]); + if (ret > 0) { + size += ret; + valid = TRUE; + } + + // final setting of mode data length + buf[0] = (BYTE)(size - 1); + + // Unsupported page + if (!valid) { + disk.code = DISK_INVALIDCDB; + return 0; + } + + // MODE SENSE success + disk.code = DISK_NOERROR; + return length; +} + +//--------------------------------------------------------------------------- +// +// MODE SENSE(10) +// *Not affected by disk.code +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::ModeSense10(const DWORD *cdb, BYTE *buf) +{ + int page; + int length; + int size; + BOOL valid; + BOOL change; + int ret; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x5a); + + // Get length, clear buffer + length = cdb[7]; + length <<= 8; + length |= cdb[8]; + if (length > 0x800) { + length = 0x800; + } + ASSERT((length >= 0) && (length < 0x800)); + memset(buf, 0, length); + + // Get changeable flag + if ((cdb[2] & 0xc0) == 0x40) { + change = TRUE; + } else { + change = FALSE; + } + + // Get page code (0x00 is valid from the beginning) + page = cdb[2] & 0x3f; + if (page == 0x00) { + valid = TRUE; + } else { + valid = FALSE; + } + + // Basic Information + size = 4; + if (disk.writep) { + buf[2] = 0x80; + } + + // add block descriptor if DBD is 0 + if ((cdb[1] & 0x08) == 0) { + // Mode parameter header + buf[3] = 0x08; + + // Only if ready + if (disk.ready) { + // Block descriptor (number of blocks) + buf[5] = (BYTE)(disk.blocks >> 16); + buf[6] = (BYTE)(disk.blocks >> 8); + buf[7] = (BYTE)disk.blocks; + + // Block descriptor (block length) + size = 1 << disk.size; + buf[9] = (BYTE)(size >> 16); + buf[10] = (BYTE)(size >> 8); + buf[11] = (BYTE)size; + } + + // Size + size = 12; + } + + // Page code 1(read-write error recovery) + if ((page == 0x01) || (page == 0x3f)) { + size += AddError(change, &buf[size]); + valid = TRUE; + } + + // Page code 3(format device) + if ((page == 0x03) || (page == 0x3f)) { + size += AddFormat(change, &buf[size]); + valid = TRUE; + } + + // Page code 4(drive parameter) + if ((page == 0x04) || (page == 0x3f)) { + size += AddDrive(change, &buf[size]); + valid = TRUE; + } + + // ペPage code 6(optical) + if (disk.id == MAKEID('S', 'C', 'M', 'O')) { + if ((page == 0x06) || (page == 0x3f)) { + size += AddOpt(change, &buf[size]); + valid = TRUE; + } + } + + // Page code 8(caching) + if ((page == 0x08) || (page == 0x3f)) { + size += AddCache(change, &buf[size]); + valid = TRUE; + } + + // Page code 13(CD-ROM) + if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if ((page == 0x0d) || (page == 0x3f)) { + size += AddCDROM(change, &buf[size]); + valid = TRUE; + } + } + + // Page code 14(CD-DA) + if (disk.id == MAKEID('S', 'C', 'C', 'D')) { + if ((page == 0x0e) || (page == 0x3f)) { + size += AddCDDA(change, &buf[size]); + valid = TRUE; + } + } + + // Page (vendor special) + ret = AddVendor(page, change, &buf[size]); + if (ret > 0) { + size += ret; + valid = TRUE; + } + + // final setting of mode data length + buf[0] = (BYTE)(size - 1); + + // Unsupported page + if (!valid) { + disk.code = DISK_INVALIDCDB; + return 0; + } + + // MODE SENSE success + disk.code = DISK_NOERROR; + return length; +} + +//--------------------------------------------------------------------------- +// +// Add error page +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddError(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x01; + buf[1] = 0x0a; + + // No changeable area + if (change) { + return 12; + } + + // Retry count is 0, limit time uses internal default value + return 12; +} + +//--------------------------------------------------------------------------- +// +// Add format page +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddFormat(BOOL change, BYTE *buf) +{ + int size; + + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x80 | 0x03; + buf[1] = 0x16; + + // Show the number of bytes in the physical sector as changeable + // (though it cannot be changed in practice) + if (change) { + buf[0xc] = 0xff; + buf[0xd] = 0xff; + return 24; + } + + if (disk.ready) { + // Set the number of tracks in one zone to 8 (TODO) + buf[0x3] = 0x08; + + // Set sector/track to 25 (TODO) + buf[0xa] = 0x00; + buf[0xb] = 0x19; + + // Set the number of bytes in the physical sector + size = 1 << disk.size; + buf[0xc] = (BYTE)(size >> 8); + buf[0xd] = (BYTE)size; + } + + // Set removable attribute + if (disk.removable) { + buf[20] = 0x20; + } + + return 24; +} + +//--------------------------------------------------------------------------- +// +// Add drive page +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddDrive(BOOL change, BYTE *buf) +{ + DWORD cylinder; + + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x04; + buf[1] = 0x16; + + // No changeable area + if (change) { + return 24; + } + + if (disk.ready) { + // Set the number of cylinders (total number of blocks + // divided by 25 sectors/track and 8 heads) + cylinder = disk.blocks; + cylinder >>= 3; + cylinder /= 25; + buf[0x2] = (BYTE)(cylinder >> 16); + buf[0x3] = (BYTE)(cylinder >> 8); + buf[0x4] = (BYTE)cylinder; + + // Fix the head at 8 + buf[0x5] = 0x8; + } + + return 24; +} + +//--------------------------------------------------------------------------- +// +// Add option +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddOpt(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x06; + buf[1] = 0x02; + + // No changeable area + if (change) { + return 4; + } + + // Do not report update blocks + return 4; +} + +//--------------------------------------------------------------------------- +// +// Add Cache Page +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddCache(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x08; + buf[1] = 0x0a; + + // No changeable area + if (change) { + return 12; + } + + // Only read cache is valid, no prefetch + return 12; +} + +//--------------------------------------------------------------------------- +// +// Add CDROM Page +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddCDROM(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x0d; + buf[1] = 0x06; + + // No changeable area + if (change) { + return 8; + } + + // 2 seconds for inactive timer + buf[3] = 0x05; + + // MSF multiples are 60 and 75 respectively + buf[5] = 60; + buf[7] = 75; + + return 8; +} + +//--------------------------------------------------------------------------- +// +// CD-DAページ追加 +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddCDDA(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x0e; + buf[1] = 0x0e; + + // No changeable area + if (change) { + return 16; + } + + // Audio waits for operation completion and allows + // PLAY across multiple tracks + return 16; +} + +//--------------------------------------------------------------------------- +// +// Add special vendor page +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::AddVendor(int /*page*/, BOOL /*change*/, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + return 0; +} + +//--------------------------------------------------------------------------- +// +// READ DEFECT DATA(10) +// *Not affected by disk.code +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) +{ + DWORD length; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x37); + + // Get length, clear buffer + length = cdb[7]; + length <<= 8; + length |= cdb[8]; + if (length > 0x800) { + length = 0x800; + } + ASSERT((length >= 0) && (length < 0x800)); + memset(buf, 0, length); + + // P/G/FORMAT + buf[1] = (cdb[1] & 0x18) | 5; + buf[3] = 8; + + buf[4] = 0xff; + buf[5] = 0xff; + buf[6] = 0xff; + buf[7] = 0xff; + + buf[8] = 0xff; + buf[9] = 0xff; + buf[10] = 0xff; + buf[11] = 0xff; + + // no list + disk.code = DISK_NODEFECT; + return 4; +} + +//--------------------------------------------------------------------------- +// +// Command +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// TEST UNIT READY +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::TestUnitReady(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // TEST UNIT READY Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// REZERO UNIT +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Rezero(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // REZERO Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// FORMAT UNIT +// *Opcode $06 for SASI, Opcode $04 for SCSI +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Format(const DWORD *cdb) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // FMTDATA=1 is not supported (but OK if there is no DEFECT LIST) + if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) { + disk.code = DISK_INVALIDCDB; + return FALSE; + } + + // FORMAT Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// REASSIGN BLOCKS +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Reassign(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // REASSIGN BLOCKS Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// READ +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::Read(BYTE *buf, DWORD block) +{ + ASSERT(this); + ASSERT(buf); + + // Status check + if (!CheckReady()) { + return 0; + } + + // Error if the total number of blocks is exceeded + if (block >= disk.blocks) { + disk.code = DISK_INVALIDLBA; + return 0; + } + + // leave it to the cache + if (!disk.dcache->Read(buf, block)) { + disk.code = DISK_READFAULT; + return 0; + } + + // Success + return (1 << disk.size); +} + +//--------------------------------------------------------------------------- +// +// WRITE check +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::WriteCheck(DWORD block) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return 0; + } + + // Error if the total number of blocks is exceeded + if (block >= disk.blocks) { + return 0; + } + + // Error if write protected + if (disk.writep) { + disk.code = DISK_WRITEPROTECT; + return 0; + } + + // Success + return (1 << disk.size); +} + +//--------------------------------------------------------------------------- +// +// WRITE +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Write(const BYTE *buf, DWORD block) +{ + ASSERT(this); + ASSERT(buf); + + // Error if not ready + if (!disk.ready) { + disk.code = DISK_NOTREADY; + return FALSE; + } + + // Error if the total number of blocks is exceeded + if (block >= disk.blocks) { + disk.code = DISK_INVALIDLBA; + return FALSE; + } + + // Error if write protected + if (disk.writep) { + disk.code = DISK_WRITEPROTECT; + return FALSE; + } + + // Leave it to the cache + if (!disk.dcache->Write(buf, block)) { + disk.code = DISK_WRITEFAULT; + return FALSE; + } + + // Success + disk.code = DISK_NOERROR; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// SEEK +// *Does not check LBA (SASI IOCS) +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Seek(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // SEEK Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// ASSIGN +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Assign(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// SPECIFY +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Specify(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// START STOP UNIT +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::StartStop(const DWORD *cdb) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x1b); + + // Look at the eject bit and eject if necessary + if (cdb[4] & 0x02) { + if (disk.lock) { + // Cannot be ejected because it is locked + disk.code = DISK_PREVENT; + return FALSE; + } + + // Eject + Eject(FALSE); + } + + // OK + disk.code = DISK_NOERROR; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// SEND DIAGNOSTIC +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::SendDiag(const DWORD *cdb) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x1d); + + // Do not support PF bit + if (cdb[1] & 0x10) { + disk.code = DISK_INVALIDCDB; + return FALSE; + } + + // Do not support parameter list + if ((cdb[3] != 0) || (cdb[4] != 0)) { + disk.code = DISK_INVALIDCDB; + return FALSE; + } + + // Always successful + disk.code = DISK_NOERROR; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// PREVENT/ALLOW MEDIUM REMOVAL +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Removal(const DWORD *cdb) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x1e); + + // Status check + if (!CheckReady()) { + return FALSE; + } + + // Set Lock flag + if (cdb[4] & 0x01) { + disk.lock = TRUE; + } else { + disk.lock = FALSE; + } + + // REMOVAL Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// READ CAPACITY +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf) +{ + DWORD blocks; + DWORD length; + + ASSERT(this); + ASSERT(buf); + + // Buffer clear + memset(buf, 0, 8); + + // Status check + if (!CheckReady()) { + return 0; + } + + // Create end of logical block address (disk.blocks-1) + ASSERT(disk.blocks > 0); + blocks = disk.blocks - 1; + buf[0] = (BYTE)(blocks >> 24); + buf[1] = (BYTE)(blocks >> 16); + buf[2] = (BYTE)(blocks >> 8); + buf[3] = (BYTE)blocks; + + // Create block length (1 << disk.size) + length = 1 << disk.size; + buf[4] = (BYTE)(length >> 24); + buf[5] = (BYTE)(length >> 16); + buf[6] = (BYTE)(length >> 8); + buf[7] = (BYTE)length; + + // return the size + return 8; +} + +//--------------------------------------------------------------------------- +// +// VERIFY +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::Verify(const DWORD *cdb) +{ + DWORD record; + DWORD blocks; + + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x2f); + + // Get parameters + record = cdb[2]; + record <<= 8; + record |= cdb[3]; + record <<= 8; + record |= cdb[4]; + record <<= 8; + record |= cdb[5]; + blocks = cdb[7]; + blocks <<= 8; + blocks |= cdb[8]; + + // Status check + if (!CheckReady()) { + return 0; + } + + // Parameter check + if (disk.blocks < (record + blocks)) { + disk.code = DISK_INVALIDLBA; + return FALSE; + } + + // Success + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// READ TOC +// +//--------------------------------------------------------------------------- +int FASTCALL Disk::ReadToc(const DWORD *cdb, BYTE *buf) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x43); + ASSERT(buf); + + // This command is not supported + disk.code = DISK_INVALIDCMD; + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::PlayAudio(const DWORD *cdb) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x45); + + // This command is not supported + disk.code = DISK_INVALIDCMD; + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO MSF +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::PlayAudioMSF(const DWORD *cdb) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x47); + + // This command is not supported + disk.code = DISK_INVALIDCMD; + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO TRACK +// +//--------------------------------------------------------------------------- +BOOL FASTCALL Disk::PlayAudioTrack(const DWORD *cdb) +{ + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x48); + + // This command is not supported + disk.code = DISK_INVALIDCMD; + return FALSE; +} + diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h new file mode 100644 index 00000000..725880fb --- /dev/null +++ b/src/raspberrypi/devices/disk.h @@ -0,0 +1,368 @@ +//--------------------------------------------------------------------------- +// +// X68000 EMULATOR "XM6" +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// +// XM6i +// Copyright (C) 2010-2015 isaki@NetBSD.org +// +// Imported sava's Anex86/T98Next image and MO format support patch. +// Comments translated to english by akuker. +// +// [ Disk ] +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "xm6.h" +#include "log.h" +#include "scsi.h" +#include "filepath.h" + +//--------------------------------------------------------------------------- +// +// Error definition (sense code returned by REQUEST SENSE) +// +// MSB Reserved (0x00) +// Sense Key +// Additional Sense Code (ASC) +// LSB Additional Sense Code Qualifier(ASCQ) +// +//--------------------------------------------------------------------------- +#define DISK_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO. +#define DISK_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED +#define DISK_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT +#define DISK_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED +#define DISK_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED +#define DISK_READFAULT 0x00031100 // UNRECOVERED READ ERROR +#define DISK_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT +#define DISK_WRITEPROTECT 0x00042700 // WRITE PROTECTED +#define DISK_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY +#define DISK_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE +#define DISK_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE +#define DISK_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB +#define DISK_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED +#define DISK_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST +#define DISK_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR +#define DISK_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR +#define DISK_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED +#define DISK_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID +#define DISK_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED +#define DISK_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND + +#if 0 +#define DISK_AUDIOPROGRESS 0x00??0011 // AUDIO PLAY IN PROGRESS +#define DISK_AUDIOPAUSED 0x00??0012 // AUDIO PLAY PAUSED +#define DISK_AUDIOSTOPPED 0x00??0014 // AUDIO PLAY STOPPED DUE TO ERROR +#define DISK_AUDIOCOMPLETE 0x00??0013 // AUDIO PLAY SUCCESSFULLY COMPLETED +#endif + + +#ifdef RASCSI +#define BENDER_SIGNATURE "RaSCSI" +// The following line was to mimic Apple's CDROM ID +// #define BENDER_SIGNATURE "SONY " +#else +#define BENDER_SIGNATURE "XM6" +#endif + +//=========================================================================== +// +// Disk Track +// +//=========================================================================== +class DiskTrack +{ +public: + // Internal data definition + typedef struct { + int track; // Track Number + int size; // Sector Size(8 or 9) + int sectors; // Number of sectors(<=0x100) + DWORD length; // Data buffer length + BYTE *buffer; // Data buffer + BOOL init; // Is it initilized? + BOOL changed; // Changed flag + DWORD maplen; // Changed map length + BOOL *changemap; // Changed map + BOOL raw; // RAW mode flag + off64_t imgoffset; // Offset to actual data + } disktrk_t; + +public: + // Basic Functions + DiskTrack(); + // Constructor + virtual ~DiskTrack(); + // Destructor + void FASTCALL Init(int track, int size, int sectors, BOOL raw = FALSE, + off64_t imgoff = 0); + // Initialization + BOOL FASTCALL Load(const Filepath& path); + // Load + BOOL FASTCALL Save(const Filepath& path); + // Save + + // Read / Write + BOOL FASTCALL Read(BYTE *buf, int sec) const; + // Sector Read + BOOL FASTCALL Write(const BYTE *buf, int sec); + // Sector Write + + // Other + int FASTCALL GetTrack() const { return dt.track; } + // Get track + BOOL FASTCALL IsChanged() const { return dt.changed; } + // Changed flag check + +private: + // Internal data + disktrk_t dt; + // Internal data +}; + +//=========================================================================== +// +// Disk Cache +// +//=========================================================================== +class DiskCache +{ +public: + // Internal data definition + typedef struct { + DiskTrack *disktrk; // Disk Track + DWORD serial; // Serial + } cache_t; + + // Number of caches + enum { + CacheMax = 16 // Number of tracks to cache + }; + +public: + // Basic Functions + DiskCache(const Filepath& path, int size, int blocks, + off64_t imgoff = 0); + // Constructor + virtual ~DiskCache(); + // Destructor + void FASTCALL SetRawMode(BOOL raw); + // CD-ROM raw mode setting + + // Access + BOOL FASTCALL Save(); + // Save and release all + BOOL FASTCALL Read(BYTE *buf, int block); + // Sector Read + BOOL FASTCALL Write(const BYTE *buf, int block); + // Sector Write + BOOL FASTCALL GetCache(int index, int& track, DWORD& serial) const; + // Get cache information + +private: + // Internal Management + void FASTCALL Clear(); + // Clear all tracks + DiskTrack* FASTCALL Assign(int track); + // Load track + BOOL FASTCALL Load(int index, int track, DiskTrack *disktrk = NULL); + // Load track + void FASTCALL Update(); + // Update serial number + + // Internal data + cache_t cache[CacheMax]; + // Cache management + DWORD serial; + // Last serial number + Filepath sec_path; + // Path + int sec_size; + // Sector size (8 or 9 or 11) + int sec_blocks; + // Blocks per sector + BOOL cd_raw; + // CD-ROM RAW mode + off64_t imgoffset; + // Offset to actual data +}; + +//=========================================================================== +// +// Disk +// +//=========================================================================== +class Disk +{ +public: + // Internal data structure + typedef struct { + DWORD id; // Media ID + BOOL ready; // Valid Disk + BOOL writep; // Write protected + BOOL readonly; // Read only + BOOL removable; // Removable + BOOL lock; // Locked + BOOL attn; // Attention + BOOL reset; // Reset + int size; // Sector Size + DWORD blocks; // Total number of sectors + DWORD lun; // LUN + DWORD code; // Status code + DiskCache *dcache; // Disk cache + off64_t imgoffset; // Offset to actual data + } disk_t; + +public: + // Basic Functions + Disk(); + // Constructor + virtual ~Disk(); + // Destructor + virtual void FASTCALL Reset(); + // Device Reset +#ifndef RASCSI + virtual BOOL FASTCALL Save(Fileio *fio, int ver); + // Save + virtual BOOL FASTCALL Load(Fileio *fio, int ver); + // Load +#endif // RASCSI + + // ID + DWORD FASTCALL GetID() const { return disk.id; } + // Get media ID + BOOL FASTCALL IsNULL() const; + // NULL check + BOOL FASTCALL IsSASI() const; + // SASI Check + BOOL FASTCALL IsSCSI() const; + // SASI Check + + // Media Operations + virtual BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); + // Open + void FASTCALL GetPath(Filepath& path) const; + // Get the path + void FASTCALL Eject(BOOL force); + // Eject + BOOL FASTCALL IsReady() const { return disk.ready; } + // Ready check + void FASTCALL WriteP(BOOL flag); + // Set Write Protect flag + BOOL FASTCALL IsWriteP() const { return disk.writep; } + // Get write protect flag + BOOL FASTCALL IsReadOnly() const { return disk.readonly; } + // Get read only flag + BOOL FASTCALL IsRemovable() const { return disk.removable; } + // Get is removable flag + BOOL FASTCALL IsLocked() const { return disk.lock; } + // Get locked status + BOOL FASTCALL IsAttn() const { return disk.attn; } + // Get attention flag + BOOL FASTCALL Flush(); + // Flush the cache + void FASTCALL GetDisk(disk_t *buffer) const; + // Get the internal data struct + + // Properties + void FASTCALL SetLUN(DWORD lun) { disk.lun = lun; } + // LUN set + DWORD FASTCALL GetLUN() { return disk.lun; } + // LUN get + // commands + virtual int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); + // INQUIRY command + virtual int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf); + // REQUEST SENSE command + int FASTCALL SelectCheck(const DWORD *cdb); + // SELECT check + int FASTCALL SelectCheck10(const DWORD *cdb); + // SELECT(10) check + virtual BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); + // MODE SELECT command + virtual int FASTCALL ModeSense(const DWORD *cdb, BYTE *buf); + // MODE SENSE command + virtual int FASTCALL ModeSense10(const DWORD *cdb, BYTE *buf); + // MODE SENSE(10) command + int FASTCALL ReadDefectData10(const DWORD *cdb, BYTE *buf); + // READ DEFECT DATA(10) command + virtual BOOL FASTCALL TestUnitReady(const DWORD *cdb); + // TEST UNIT READY command + BOOL FASTCALL Rezero(const DWORD *cdb); + // REZERO command + BOOL FASTCALL Format(const DWORD *cdb); + // FORMAT UNIT command + BOOL FASTCALL Reassign(const DWORD *cdb); + // REASSIGN UNIT command + virtual int FASTCALL Read(BYTE *buf, DWORD block); + // READ command + int FASTCALL WriteCheck(DWORD block); + // WRITE check + BOOL FASTCALL Write(const BYTE *buf, DWORD block); + // WRITE command + BOOL FASTCALL Seek(const DWORD *cdb); + // SEEK command + BOOL FASTCALL Assign(const DWORD *cdb); + // ASSIGN command + BOOL FASTCALL Specify(const DWORD *cdb); + // SPECIFY command + BOOL FASTCALL StartStop(const DWORD *cdb); + // START STOP UNIT command + BOOL FASTCALL SendDiag(const DWORD *cdb); + // SEND DIAGNOSTIC command + BOOL FASTCALL Removal(const DWORD *cdb); + // PREVENT/ALLOW MEDIUM REMOVAL command + int FASTCALL ReadCapacity(const DWORD *cdb, BYTE *buf); + // READ CAPACITY command + BOOL FASTCALL Verify(const DWORD *cdb); + // VERIFY command + virtual int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf); + // READ TOC command + virtual BOOL FASTCALL PlayAudio(const DWORD *cdb); + // PLAY AUDIO command + virtual BOOL FASTCALL PlayAudioMSF(const DWORD *cdb); + // PLAY AUDIO MSF command + virtual BOOL FASTCALL PlayAudioTrack(const DWORD *cdb); + // PLAY AUDIO TRACK command + void FASTCALL InvalidCmd() { disk.code = DISK_INVALIDCMD; } + // Unsupported command + + // Other + BOOL IsCacheWB() { return cache_wb; } + // Get cache writeback mode + void SetCacheWB(BOOL enable) { cache_wb = enable; } + // Set cache writeback mode + +protected: + // Internal processing + virtual int FASTCALL AddError(BOOL change, BYTE *buf); + // Add error + virtual int FASTCALL AddFormat(BOOL change, BYTE *buf); + // Add format + virtual int FASTCALL AddDrive(BOOL change, BYTE *buf); + // Add drive + int FASTCALL AddOpt(BOOL change, BYTE *buf); + // Add optical + int FASTCALL AddCache(BOOL change, BYTE *buf); + // Add cache + int FASTCALL AddCDROM(BOOL change, BYTE *buf); + // Add CD-ROM + int FASTCALL AddCDDA(BOOL change, BYTE *buf); + // Add CD_DA + virtual int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); + // Add vendor special info + BOOL FASTCALL CheckReady(); + // Check if ready + + // Internal data + disk_t disk; + // Internal disk data + Filepath diskpath; + // File path (for GetPath) + BOOL cache_wb; + // Cache mode +}; diff --git a/src/raspberrypi/devices/sasihd.cpp b/src/raspberrypi/devices/sasihd.cpp new file mode 100644 index 00000000..ba0a8cb4 --- /dev/null +++ b/src/raspberrypi/devices/sasihd.cpp @@ -0,0 +1,164 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SASI hard disk ] +// +//--------------------------------------------------------------------------- +#include "sasihd.h" +#include "xm6.h" +#include "fileio.h" + + +//=========================================================================== +// +// SASI Hard Disk +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +SASIHD::SASIHD() : Disk() +{ + // SASI ハードディスク + disk.id = MAKEID('S', 'A', 'H', 'D'); +} + +//--------------------------------------------------------------------------- +// +// リセット +// +//--------------------------------------------------------------------------- +void FASTCALL SASIHD::Reset() +{ + // ロック状態解除、アテンション解除 + disk.lock = FALSE; + disk.attn = FALSE; + + // Resetなし、コードをクリア + disk.reset = FALSE; + disk.code = 0x00; +} + +//--------------------------------------------------------------------------- +// +// オープン +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SASIHD::Open(const Filepath& path, BOOL /*attn*/) +{ + Fileio fio; + off64_t size; + + ASSERT(this); + ASSERT(!disk.ready); + + // Open as read-only + if (!fio.Open(path, Fileio::ReadOnly)) { + return FALSE; + } + + // Get file size + size = fio.GetFileSize(); + fio.Close(); + +#if defined(USE_MZ1F23_1024_SUPPORT) + // MZ-2500/MZ-2800用 MZ-1F23(SASI 20M/セクタサイズ1024)専用 + // 20M(22437888 BS=1024 C=21912) + if (size == 0x1566000) { + // セクタサイズとブロック数 + disk.size = 10; + disk.blocks = (DWORD)(size >> 10); + + // Call the base class + return Disk::Open(path); + } +#endif // USE_MZ1F23_1024_SUPPORT + +#if defined(REMOVE_FIXED_SASIHD_SIZE) + // 256バイト単位であること + if (size & 0xff) { + return FALSE; + } + + // 10MB以上 + if (size < 0x9f5400) { + return FALSE; + } + + // 512MB程度に制限しておく + if (size > 512 * 1024 * 1024) { + return FALSE; + } +#else + // 10MB, 20MB, 40MBのみ + switch (size) { + // 10MB(10441728 BS=256 C=40788) + case 0x9f5400: + break; + + // 20MB(20748288 BS=256 C=81048) + case 0x13c9800: + break; + + // 40MB(41496576 BS=256 C=162096) + case 0x2793000: + break; + + // Other(サポートしない) + default: + return FALSE; + } +#endif // REMOVE_FIXED_SASIHD_SIZE + + // セクタサイズとブロック数 + disk.size = 8; + disk.blocks = (DWORD)(size >> 8); + + // Call the base class + return Disk::Open(path); +} + +//--------------------------------------------------------------------------- +// +// REQUEST SENSE +// +//--------------------------------------------------------------------------- +int FASTCALL SASIHD::RequestSense(const DWORD *cdb, BYTE *buf) +{ + int size; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + + // サイズ決定 + size = (int)cdb[4]; + ASSERT((size >= 0) && (size < 0x100)); + + // サイズ0のときに4バイト転送する(Shugart Associates System Interface仕様) + if (size == 0) { + size = 4; + } + + // SASIは非拡張フォーマットに固定 + memset(buf, 0, size); + buf[0] = (BYTE)(disk.code >> 16); + buf[1] = (BYTE)(disk.lun << 5); + + // コードをクリア + disk.code = 0x00; + + return size; +} diff --git a/src/raspberrypi/devices/sasihd.h b/src/raspberrypi/devices/sasihd.h new file mode 100644 index 00000000..eb305d28 --- /dev/null +++ b/src/raspberrypi/devices/sasihd.h @@ -0,0 +1,40 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SASI hard disk ] +// +//--------------------------------------------------------------------------- +#pragma once + +#include "os.h" +#include "disk.h" +#include "filepath.h" + +//=========================================================================== +// +// SASI Hard Disk +// +//=========================================================================== +class SASIHD : public Disk +{ +public: + // Basic Functions + SASIHD(); + // Constructor + void FASTCALL Reset(); + // Reset + BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); + // Open + // commands + int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf); + // REQUEST SENSE command +}; \ No newline at end of file diff --git a/src/raspberrypi/devices/scsi_host_bridge.cpp b/src/raspberrypi/devices/scsi_host_bridge.cpp new file mode 100644 index 00000000..520c5c46 --- /dev/null +++ b/src/raspberrypi/devices/scsi_host_bridge.cpp @@ -0,0 +1,1502 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI Host Bridge for the Sharp X68000 ] +// +// Note: This requires a special driver on the host system and will only +// work with the Sharp X68000 operating system. +//--------------------------------------------------------------------------- + +#include "scsi_host_bridge.h" +#include "xm6.h" +#include "ctapdriver.h" +#include "cfilesystem.h" + +//=========================================================================== +// +// SCSI Host Bridge +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +SCSIBR::SCSIBR() : Disk() +{ + // Host Bridge + disk.id = MAKEID('S', 'C', 'B', 'R'); + +#if defined(RASCSI) && defined(__linux__) && !defined(BAREMETAL) + // TAP Driver Generation + tap = new CTapDriver(); + m_bTapEnable = tap->Init(); + + // Generate MAC Address + memset(mac_addr, 0x00, 6); + if (m_bTapEnable) { + tap->GetMacAddr(mac_addr); + mac_addr[5]++; + } + + // Packet reception flag OFF + packet_enable = FALSE; +#endif // RASCSI && !BAREMETAL + + // Create host file system + fs = new CFileSys(); + fs->Reset(); +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +SCSIBR::~SCSIBR() +{ +#if defined(RASCSI) && !defined(BAREMETAL) + // TAP driver release + if (tap) { + tap->Cleanup(); + delete tap; + } +#endif // RASCSI && !BAREMETAL + + // Release host file system + if (fs) { + fs->Reset(); + delete fs; + } +} + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIBR::Inquiry( + const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +{ + char rev[32]; + int size; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x12); + + // EVPD check + if (cdb[1] & 0x01) { + disk.code = DISK_INVALIDCDB; + return FALSE; + } + + // Basic data + // buf[0] ... Communication Device + // buf[2] ... SCSI-2 compliant command system + // buf[3] ... SCSI-2 compliant Inquiry response + // buf[4] ... Inquiry additional data + memset(buf, 0, 8); + buf[0] = 0x09; + + // SCSI-2 p.104 4.4.3 Incorrect logical unit handling + if (((cdb[1] >> 5) & 0x07) != disk.lun) { + buf[0] = 0x7f; + } + + buf[2] = 0x02; + buf[3] = 0x02; + buf[4] = 36 - 5 + 8; // required + 8 byte extension + + // Fill with blanks + memset(&buf[8], 0x20, buf[4] - 3); + + // Vendor name + memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); + + // Product name + memcpy(&buf[16], "RASCSI BRIDGE", 13); + + // Revision (XM6 version number) + sprintf(rev, "0%01d%01d%01d", + (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); + memcpy(&buf[32], rev, 4); + + // Optional function valid flag + buf[36] = '0'; + +#if defined(RASCSI) && !defined(BAREMETAL) + // TAP Enable + if (m_bTapEnable) { + buf[37] = '1'; + } +#endif // RASCSI && !BAREMETAL + + // CFileSys Enable + buf[38] = '1'; + + // Size of data that can be returned + size = (buf[4] + 5); + + // Limit if the other buffer is small + if (size > (int)cdb[4]) { + size = (int)cdb[4]; + } + + // Success + disk.code = DISK_NOERROR; + return size; +} + +//--------------------------------------------------------------------------- +// +// TEST UNIT READY +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIBR::TestUnitReady(const DWORD* /*cdb*/) +{ + ASSERT(this); + + // TEST UNIT READY Success + disk.code = DISK_NOERROR; + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// GET MESSAGE(10) +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) +{ + int type; + int phase; +#if defined(RASCSI) && !defined(BAREMETAL) + int func; + int total_len; + int i; +#endif // RASCSI && !BAREMETAL + + ASSERT(this); + + // Type + type = cdb[2]; + +#if defined(RASCSI) && !defined(BAREMETAL) + // Function number + func = cdb[3]; +#endif // RASCSI && !BAREMETAL + + // Phase + phase = cdb[9]; + + switch (type) { +#if defined(RASCSI) && !defined(BAREMETAL) + case 1: // Ethernet + // Do not process if TAP is invalid + if (!m_bTapEnable) { + return 0; + } + + switch (func) { + case 0: // Get MAC address + return GetMacAddr(buf); + + case 1: // Received packet acquisition (size/buffer) + if (phase == 0) { + // Get packet size + ReceivePacket(); + buf[0] = (BYTE)(packet_len >> 8); + buf[1] = (BYTE)packet_len; + return 2; + } else { + // Get package data + GetPacketBuf(buf); + return packet_len; + } + + case 2: // Received packet acquisition (size + buffer simultaneously) + ReceivePacket(); + buf[0] = (BYTE)(packet_len >> 8); + buf[1] = (BYTE)packet_len; + GetPacketBuf(&buf[2]); + return packet_len + 2; + + case 3: // Simultaneous acquisition of multiple packets (size + buffer simultaneously) + // Currently the maximum number of packets is 10 + // Isn't it too fast if I increase more? + total_len = 0; + for (i = 0; i < 10; i++) { + ReceivePacket(); + *buf++ = (BYTE)(packet_len >> 8); + *buf++ = (BYTE)packet_len; + total_len += 2; + if (packet_len == 0) + break; + GetPacketBuf(buf); + buf += packet_len; + total_len += packet_len; + } + return total_len; + } + break; +#endif // RASCSI && !BAREMETAL + + case 2: // Host Drive + switch (phase) { + case 0: // Get result code + return ReadFsResult(buf); + + case 1: // Return data acquisition + return ReadFsOut(buf); + + case 2: // Return additional data acquisition + return ReadFsOpt(buf); + } + break; + } + + // Error + ASSERT(FALSE); + return 0; +} + +//--------------------------------------------------------------------------- +// +// SEND MESSAGE(10) +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf) +{ + int type; + int func; + int phase; + int len; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + + // Type + type = cdb[2]; + + // Function number + func = cdb[3]; + + // Phase + phase = cdb[9]; + + // Get the number of lights + len = cdb[6]; + len <<= 8; + len |= cdb[7]; + len <<= 8; + len |= cdb[8]; + + switch (type) { +#if defined(RASCSI) && !defined(BAREMETAL) + case 1: // Ethernet + // Do not process if TAP is invalid + if (!m_bTapEnable) { + return FALSE; + } + + switch (func) { + case 0: // MAC address setting + SetMacAddr(buf); + return TRUE; + + case 1: // Send packet + SendPacket(buf, len); + return TRUE; + } + break; +#endif // RASCSI && !BAREMETAL + + case 2: // Host drive + switch (phase) { + case 0: // issue command + WriteFs(func, buf); + return TRUE; + + case 1: // additional data writing + WriteFsOpt(buf, len); + return TRUE; + } + break; + } + + // Error + ASSERT(FALSE); + return FALSE; +} + +#if defined(RASCSI) && !defined(BAREMETAL) +//--------------------------------------------------------------------------- +// +// Get MAC Address +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIBR::GetMacAddr(BYTE *mac) +{ + ASSERT(this); + ASSERT(mac); + + memcpy(mac, mac_addr, 6); + return 6; +} + +//--------------------------------------------------------------------------- +// +// Set MAC Address +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::SetMacAddr(BYTE *mac) +{ + ASSERT(this); + ASSERT(mac); + + memcpy(mac_addr, mac, 6); +} + +//--------------------------------------------------------------------------- +// +// Receive Packet +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::ReceivePacket() +{ + static const BYTE bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ASSERT(this); + ASSERT(tap); + + // previous packet has not been received + if (packet_enable) { + return; + } + + // Receive packet + packet_len = tap->Rx(packet_buf); + + // Check if received packet + if (memcmp(packet_buf, mac_addr, 6) != 0) { + if (memcmp(packet_buf, bcast_addr, 6) != 0) { + packet_len = 0; + return; + } + } + + // Discard if it exceeds the buffer size + if (packet_len > 2048) { + packet_len = 0; + return; + } + + // Store in receive buffer + if (packet_len > 0) { + packet_enable = TRUE; + } +} + +//--------------------------------------------------------------------------- +// +// Get Packet +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::GetPacketBuf(BYTE *buf) +{ + int len; + + ASSERT(this); + ASSERT(tap); + ASSERT(buf); + + // Size limit + len = packet_len; + if (len > 2048) { + len = 2048; + } + + // Copy + memcpy(buf, packet_buf, len); + + // Received + packet_enable = FALSE; +} + +//--------------------------------------------------------------------------- +// +// Send Packet +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::SendPacket(BYTE *buf, int len) +{ + ASSERT(this); + ASSERT(tap); + ASSERT(buf); + + tap->Tx(buf, len); +} +#endif // RASCSI && !BAREMETAL + +//--------------------------------------------------------------------------- +// +// $40 - Device Boot +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_InitDevice(BYTE *buf) +{ + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + fs->Reset(); + fsresult = fs->InitDevice((Human68k::argument_t*)buf); +} + +//--------------------------------------------------------------------------- +// +// $41 - Directory Check +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_CheckDir(BYTE *buf) +{ + DWORD nUnit; + Human68k::namests_t *pNamests; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + fsresult = fs->CheckDir(nUnit, pNamests); +} + +//--------------------------------------------------------------------------- +// +// $42 - Create Directory +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_MakeDir(BYTE *buf) +{ + DWORD nUnit; + Human68k::namests_t *pNamests; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + fsresult = fs->MakeDir(nUnit, pNamests); +} + +//--------------------------------------------------------------------------- +// +// $43 - Remove Directory +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_RemoveDir(BYTE *buf) +{ + DWORD nUnit; + Human68k::namests_t *pNamests; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + fsresult = fs->RemoveDir(nUnit, pNamests); +} + +//--------------------------------------------------------------------------- +// +// $44 - Rename +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Rename(BYTE *buf) +{ + DWORD nUnit; + Human68k::namests_t *pNamests; + Human68k::namests_t* pNamestsNew; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + pNamestsNew = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + fsresult = fs->Rename(nUnit, pNamests, pNamestsNew); +} + +//--------------------------------------------------------------------------- +// +// $45 - Delete File +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Delete(BYTE *buf) +{ + DWORD nUnit; + Human68k::namests_t *pNamests; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + fsresult = fs->Delete(nUnit, pNamests); +} + +//--------------------------------------------------------------------------- +// +// $46 - Get / Set file attributes +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Attribute(BYTE *buf) +{ + DWORD nUnit; + Human68k::namests_t *pNamests; + DWORD nHumanAttribute; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + dp = (DWORD*)&buf[i]; + nHumanAttribute = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->Attribute(nUnit, pNamests, nHumanAttribute); +} + +//--------------------------------------------------------------------------- +// +// $47 - File Search +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Files(BYTE *buf) +{ + DWORD nUnit; + DWORD nKey; + Human68k::namests_t *pNamests; + Human68k::files_t *files; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + files = (Human68k::files_t*)&buf[i]; + i += sizeof(Human68k::files_t); + + files->sector = ntohl(files->sector); + files->offset = ntohs(files->offset); + files->time = ntohs(files->time); + files->date = ntohs(files->date); + files->size = ntohl(files->size); + + fsresult = fs->Files(nUnit, nKey, pNamests, files); + + files->sector = htonl(files->sector); + files->offset = htons(files->offset); + files->time = htons(files->time); + files->date = htons(files->date); + files->size = htonl(files->size); + + i = 0; + memcpy(&fsout[i], files, sizeof(Human68k::files_t)); + i += sizeof(Human68k::files_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $48 - File next search +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_NFiles(BYTE *buf) +{ + DWORD nUnit; + DWORD nKey; + Human68k::files_t *files; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + files = (Human68k::files_t*)&buf[i]; + i += sizeof(Human68k::files_t); + + files->sector = ntohl(files->sector); + files->offset = ntohs(files->offset); + files->time = ntohs(files->time); + files->date = ntohs(files->date); + files->size = ntohl(files->size); + + fsresult = fs->NFiles(nUnit, nKey, files); + + files->sector = htonl(files->sector); + files->offset = htons(files->offset); + files->time = htons(files->time); + files->date = htons(files->date); + files->size = htonl(files->size); + + i = 0; + memcpy(&fsout[i], files, sizeof(Human68k::files_t)); + i += sizeof(Human68k::files_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $49 - File Creation +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Create(BYTE *buf) +{ + DWORD nUnit; + DWORD nKey; + Human68k::namests_t *pNamests; + Human68k::fcb_t *pFcb; + DWORD nAttribute; + BOOL bForce; + DWORD *dp; + BOOL *bp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + dp = (DWORD*)&buf[i]; + nAttribute = ntohl(*dp); + i += sizeof(DWORD); + + bp = (BOOL*)&buf[i]; + bForce = ntohl(*bp); + i += sizeof(BOOL); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->Create(nUnit, nKey, pNamests, pFcb, nAttribute, bForce); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $4A - Open File +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Open(BYTE *buf) +{ + DWORD nUnit; + DWORD nKey; + Human68k::namests_t *pNamests; + Human68k::fcb_t *pFcb; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pNamests = (Human68k::namests_t*)&buf[i]; + i += sizeof(Human68k::namests_t); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->Open(nUnit, nKey, pNamests, pFcb); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $4B - Close File +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Close(BYTE *buf) +{ + DWORD nUnit; + DWORD nKey; + Human68k::fcb_t *pFcb; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->Close(nUnit, nKey, pFcb); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $4C - Read File +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Read(BYTE *buf) +{ + DWORD nKey; + Human68k::fcb_t *pFcb; + DWORD nSize; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + dp = (DWORD*)&buf[i]; + nSize = ntohl(*dp); + i += sizeof(DWORD); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->Read(nKey, pFcb, fsopt, nSize); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; + + fsoptlen = fsresult; +} + +//--------------------------------------------------------------------------- +// +// $4D - Write file +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Write(BYTE *buf) +{ + DWORD nKey; + Human68k::fcb_t *pFcb; + DWORD nSize; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + dp = (DWORD*)&buf[i]; + nSize = ntohl(*dp); + i += sizeof(DWORD); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->Write(nKey, pFcb, fsopt, nSize); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $4E - Seek file +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Seek(BYTE *buf) +{ + DWORD nKey; + Human68k::fcb_t *pFcb; + DWORD nMode; + int nOffset; + DWORD *dp; + int *ip; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + dp = (DWORD*)&buf[i]; + nMode = ntohl(*dp); + i += sizeof(DWORD); + + ip = (int*)&buf[i]; + nOffset = ntohl(*ip); + i += sizeof(int); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->Seek(nKey, pFcb, nMode, nOffset); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $4F - File Timestamp Get / Set +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_TimeStamp(BYTE *buf) +{ + DWORD nUnit; + DWORD nKey; + Human68k::fcb_t *pFcb; + DWORD nHumanTime; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nKey = ntohl(*dp); + i += sizeof(DWORD); + + pFcb = (Human68k::fcb_t*)&buf[i]; + i += sizeof(Human68k::fcb_t); + + dp = (DWORD*)&buf[i]; + nHumanTime = ntohl(*dp); + i += sizeof(DWORD); + + pFcb->fileptr = ntohl(pFcb->fileptr); + pFcb->mode = ntohs(pFcb->mode); + pFcb->time = ntohs(pFcb->time); + pFcb->date = ntohs(pFcb->date); + pFcb->size = ntohl(pFcb->size); + + fsresult = fs->TimeStamp(nUnit, nKey, pFcb, nHumanTime); + + pFcb->fileptr = htonl(pFcb->fileptr); + pFcb->mode = htons(pFcb->mode); + pFcb->time = htons(pFcb->time); + pFcb->date = htons(pFcb->date); + pFcb->size = htonl(pFcb->size); + + i = 0; + memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); + i += sizeof(Human68k::fcb_t); + + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $50 - Get Capacity +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_GetCapacity(BYTE *buf) +{ + DWORD nUnit; + Human68k::capacity_t cap; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->GetCapacity(nUnit, &cap); + + cap.freearea = htons(cap.freearea); + cap.clusters = htons(cap.clusters); + cap.sectors = htons(cap.sectors); + cap.bytes = htons(cap.bytes); + + memcpy(fsout, &cap, sizeof(Human68k::capacity_t)); + fsoutlen = sizeof(Human68k::capacity_t); +} + +//--------------------------------------------------------------------------- +// +// $51 - Drive status inspection/control +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_CtrlDrive(BYTE *buf) +{ + DWORD nUnit; + Human68k::ctrldrive_t *pCtrlDrive; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + pCtrlDrive = (Human68k::ctrldrive_t*)&buf[i]; + i += sizeof(Human68k::ctrldrive_t); + + fsresult = fs->CtrlDrive(nUnit, pCtrlDrive); + + memcpy(fsout, pCtrlDrive, sizeof(Human68k::ctrldrive_t)); + fsoutlen = sizeof(Human68k::ctrldrive_t); +} + +//--------------------------------------------------------------------------- +// +// $52 - Get DPB +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_GetDPB(BYTE *buf) +{ + DWORD nUnit; + Human68k::dpb_t dpb; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->GetDPB(nUnit, &dpb); + + dpb.sector_size = htons(dpb.sector_size); + dpb.fat_sector = htons(dpb.fat_sector); + dpb.file_max = htons(dpb.file_max); + dpb.data_sector = htons(dpb.data_sector); + dpb.cluster_max = htons(dpb.cluster_max); + dpb.root_sector = htons(dpb.root_sector); + + memcpy(fsout, &dpb, sizeof(Human68k::dpb_t)); + fsoutlen = sizeof(Human68k::dpb_t); +} + +//--------------------------------------------------------------------------- +// +// $53 - Read Sector +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_DiskRead(BYTE *buf) +{ + DWORD nUnit; + DWORD nSector; + DWORD nSize; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nSector = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nSize = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->DiskRead(nUnit, fsout, nSector, nSize); + fsoutlen = 0x200; +} + +//--------------------------------------------------------------------------- +// +// $54 - Write Sector +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_DiskWrite(BYTE *buf) +{ + DWORD nUnit; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->DiskWrite(nUnit); +} + +//--------------------------------------------------------------------------- +// +// $55 - IOCTRL +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Ioctrl(BYTE *buf) +{ + DWORD nUnit; + DWORD nFunction; + Human68k::ioctrl_t *pIoctrl; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + dp = (DWORD*)&buf[i]; + nFunction = ntohl(*dp); + i += sizeof(DWORD); + + pIoctrl = (Human68k::ioctrl_t*)&buf[i]; + i += sizeof(Human68k::ioctrl_t); + + switch (nFunction) { + case 2: + case (DWORD)-2: + pIoctrl->param = htonl(pIoctrl->param); + break; + } + + fsresult = fs->Ioctrl(nUnit, nFunction, pIoctrl); + + switch (nFunction) { + case 0: + pIoctrl->media = htons(pIoctrl->media); + break; + case 1: + case (DWORD)-3: + pIoctrl->param = htonl(pIoctrl->param); + break; + } + + i = 0; + memcpy(&fsout[i], pIoctrl, sizeof(Human68k::ioctrl_t)); + i += sizeof(Human68k::ioctrl_t); + fsoutlen = i; +} + +//--------------------------------------------------------------------------- +// +// $56 - Flush +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Flush(BYTE *buf) +{ + DWORD nUnit; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->Flush(nUnit); +} + +//--------------------------------------------------------------------------- +// +// $57 - Check Media +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_CheckMedia(BYTE *buf) +{ + DWORD nUnit; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->CheckMedia(nUnit); +} + +//--------------------------------------------------------------------------- +// +// $58 - Lock +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::FS_Lock(BYTE *buf) +{ + DWORD nUnit; + DWORD *dp; + int i; + + ASSERT(this); + ASSERT(fs); + ASSERT(buf); + + i = 0; + dp = (DWORD*)buf; + nUnit = ntohl(*dp); + i += sizeof(DWORD); + + fsresult = fs->Lock(nUnit); +} + +//--------------------------------------------------------------------------- +// +// Read Filesystem (result code) +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIBR::ReadFsResult(BYTE *buf) +{ + DWORD *dp; + + ASSERT(this); + ASSERT(buf); + + dp = (DWORD*)buf; + *dp = htonl(fsresult); + return sizeof(DWORD); +} + +//--------------------------------------------------------------------------- +// +// Read Filesystem (return data) +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIBR::ReadFsOut(BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + memcpy(buf, fsout, fsoutlen); + return fsoutlen; +} + +//--------------------------------------------------------------------------- +// +// Read file system (return option data) +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIBR::ReadFsOpt(BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + memcpy(buf, fsopt, fsoptlen); + return fsoptlen; +} + +//--------------------------------------------------------------------------- +// +// Write Filesystem +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::WriteFs(int func, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + fsresult = FS_FATAL_INVALIDCOMMAND; + fsoutlen = 0; + fsoptlen = 0; + + // コマンド分岐 + func &= 0x1f; + switch (func) { + case 0x00: return FS_InitDevice(buf); // $40 - start device + case 0x01: return FS_CheckDir(buf); // $41 - directory check + case 0x02: return FS_MakeDir(buf); // $42 - create directory + case 0x03: return FS_RemoveDir(buf); // $43 - remove directory + case 0x04: return FS_Rename(buf); // $44 - change file name + case 0x05: return FS_Delete(buf); // $45 - delete file + case 0x06: return FS_Attribute(buf); // $46 - Get/set file attribute + case 0x07: return FS_Files(buf); // $47 - file search + case 0x08: return FS_NFiles(buf); // $48 - next file search + case 0x09: return FS_Create(buf); // $49 - create file + case 0x0A: return FS_Open(buf); // $4A - File open + case 0x0B: return FS_Close(buf); // $4B - File close + case 0x0C: return FS_Read(buf); // $4C - read file + case 0x0D: return FS_Write(buf); // $4D - write file + case 0x0E: return FS_Seek(buf); // $4E - File seek + case 0x0F: return FS_TimeStamp(buf); // $4F - Get/set file modification time + case 0x10: return FS_GetCapacity(buf); // $50 - get capacity + case 0x11: return FS_CtrlDrive(buf); // $51 - Drive control/state check + case 0x12: return FS_GetDPB(buf); // $52 - Get DPB + case 0x13: return FS_DiskRead(buf); // $53 - read sector + case 0x14: return FS_DiskWrite(buf); // $54 - write sector + case 0x15: return FS_Ioctrl(buf); // $55 - IOCTRL + case 0x16: return FS_Flush(buf); // $56 - flush + case 0x17: return FS_CheckMedia(buf); // $57 - check media exchange + case 0x18: return FS_Lock(buf); // $58 - exclusive control + } +} + +//--------------------------------------------------------------------------- +// +// File system write (input option data) +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIBR::WriteFsOpt(BYTE *buf, int num) +{ + ASSERT(this); + + memcpy(fsopt, buf, num); +} diff --git a/src/raspberrypi/devices/scsi_host_bridge.h b/src/raspberrypi/devices/scsi_host_bridge.h new file mode 100644 index 00000000..e79e933f --- /dev/null +++ b/src/raspberrypi/devices/scsi_host_bridge.h @@ -0,0 +1,153 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI Host Bridge for the Sharp X68000 ] +// +// Note: This requires a special driver on the host system and will only +// work with the Sharp X68000 operating system. +//--------------------------------------------------------------------------- +#pragma once + +#include "xm6.h" +#include "os.h" +#include "disk.h" + +//=========================================================================== +// +// SCSI Host Bridge +// +//=========================================================================== +#if defined(RASCSI) && !defined(BAREMETAL) +class CTapDriver; +#endif // RASCSI && !BAREMETAL +class CFileSys; +class SCSIBR : public Disk +{ +public: + // Basic Functions + SCSIBR(); + // Constructor + virtual ~SCSIBR(); + // Destructor + + // commands + int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); + // INQUIRY command + BOOL FASTCALL TestUnitReady(const DWORD *cdb); + // TEST UNIT READY command + int FASTCALL GetMessage10(const DWORD *cdb, BYTE *buf); + // GET MESSAGE10 command + BOOL FASTCALL SendMessage10(const DWORD *cdb, BYTE *buf); + // SEND MESSAGE10 command + +private: +#if defined(RASCSI) && !defined(BAREMETAL) + int FASTCALL GetMacAddr(BYTE *buf); + // Get MAC address + void FASTCALL SetMacAddr(BYTE *buf); + // Set MAC address + void FASTCALL ReceivePacket(); + // Receive a packet + void FASTCALL GetPacketBuf(BYTE *buf); + // Get a packet + void FASTCALL SendPacket(BYTE *buf, int len); + // Send a packet + + CTapDriver *tap; + // TAP driver + BOOL m_bTapEnable; + // TAP valid flag + BYTE mac_addr[6]; + // MAC Addres + int packet_len; + // Receive packet size + BYTE packet_buf[0x1000]; + // Receive packet buffer + BOOL packet_enable; + // Received packet valid +#endif // RASCSI && !BAREMETAL + + int FASTCALL ReadFsResult(BYTE *buf); + // Read filesystem (result code) + int FASTCALL ReadFsOut(BYTE *buf); + // Read filesystem (return data) + int FASTCALL ReadFsOpt(BYTE *buf); + // Read file system (optional data) + void FASTCALL WriteFs(int func, BYTE *buf); + // File system write (execute) + void FASTCALL WriteFsOpt(BYTE *buf, int len); + // File system write (optional data) + // Command handlers + void FASTCALL FS_InitDevice(BYTE *buf); + // $40 - boot + void FASTCALL FS_CheckDir(BYTE *buf); + // $41 - directory check + void FASTCALL FS_MakeDir(BYTE *buf); + // $42 - create directory + void FASTCALL FS_RemoveDir(BYTE *buf); + // $43 - delete directory + void FASTCALL FS_Rename(BYTE *buf); + // $44 - change filename + void FASTCALL FS_Delete(BYTE *buf); + // $45 - delete file + void FASTCALL FS_Attribute(BYTE *buf); + // $46 - get/set file attributes + void FASTCALL FS_Files(BYTE *buf); + // $47 - file search + void FASTCALL FS_NFiles(BYTE *buf); + // $48 - find next file + void FASTCALL FS_Create(BYTE *buf); + // $49 - create file + void FASTCALL FS_Open(BYTE *buf); + // $4A - open file + void FASTCALL FS_Close(BYTE *buf); + // $4B - close file + void FASTCALL FS_Read(BYTE *buf); + // $4C - read file + void FASTCALL FS_Write(BYTE *buf); + // $4D - write file + void FASTCALL FS_Seek(BYTE *buf); + // $4E - seek file + void FASTCALL FS_TimeStamp(BYTE *buf); + // $4F - get/set file time + void FASTCALL FS_GetCapacity(BYTE *buf); + // $50 - get capacity + void FASTCALL FS_CtrlDrive(BYTE *buf); + // $51 - drive status check/control + void FASTCALL FS_GetDPB(BYTE *buf); + // $52 - get DPB + void FASTCALL FS_DiskRead(BYTE *buf); + // $53 - read sector + void FASTCALL FS_DiskWrite(BYTE *buf); + // $54 - write sector + void FASTCALL FS_Ioctrl(BYTE *buf); + // $55 - IOCTRL + void FASTCALL FS_Flush(BYTE *buf); + // $56 - flush cache + void FASTCALL FS_CheckMedia(BYTE *buf); + // $57 - check media + void FASTCALL FS_Lock(BYTE *buf); + // $58 - get exclusive control + + CFileSys *fs; + // File system accessor + DWORD fsresult; + // File system access result code + BYTE fsout[0x800]; + // File system access result buffer + DWORD fsoutlen; + // File system access result buffer size + BYTE fsopt[0x1000000]; + // File system access buffer + DWORD fsoptlen; + // File system access buffer size +}; diff --git a/src/raspberrypi/devices/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp new file mode 100644 index 00000000..d898f04c --- /dev/null +++ b/src/raspberrypi/devices/scsicd.cpp @@ -0,0 +1,1089 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI Hard Disk for Apple Macintosh ] +// +//--------------------------------------------------------------------------- + +#include "xm6.h" +#include "scsicd.h" +#include "fileio.h" + +//=========================================================================== +// +// CD Track +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +CDTrack::CDTrack(SCSICD *scsicd) +{ + ASSERT(scsicd); + + // Set parent CD-ROM device + cdrom = scsicd; + + // Track defaults to disabled + valid = FALSE; + + // Initialize other data + track_no = -1; + first_lba = 0; + last_lba = 0; + audio = FALSE; + raw = FALSE; +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +CDTrack::~CDTrack() +{ +} + +//--------------------------------------------------------------------------- +// +// Init +// +//--------------------------------------------------------------------------- +BOOL FASTCALL CDTrack::Init(int track, DWORD first, DWORD last) +{ + ASSERT(this); + ASSERT(!valid); + ASSERT(track >= 1); + ASSERT(first < last); + + // Set and enable track number + track_no = track; + valid = TRUE; + + // Remember LBA + first_lba = first; + last_lba = last; + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Set Path +// +//--------------------------------------------------------------------------- +void FASTCALL CDTrack::SetPath(BOOL cdda, const Filepath& path) +{ + ASSERT(this); + ASSERT(valid); + + // CD-DA or data + audio = cdda; + + // Remember the path + imgpath = path; +} + +//--------------------------------------------------------------------------- +// +// Get Path +// +//--------------------------------------------------------------------------- +void FASTCALL CDTrack::GetPath(Filepath& path) const +{ + ASSERT(this); + ASSERT(valid); + + // Return the path (by reference) + path = imgpath; +} + +//--------------------------------------------------------------------------- +// +// Add Index +// +//--------------------------------------------------------------------------- +void FASTCALL CDTrack::AddIndex(int index, DWORD lba) +{ + ASSERT(this); + ASSERT(valid); + ASSERT(index > 0); + ASSERT(first_lba <= lba); + ASSERT(lba <= last_lba); + + // Currently does not support indexes + ASSERT(FALSE); +} + +//--------------------------------------------------------------------------- +// +// Gets the start of LBA +// +//--------------------------------------------------------------------------- +DWORD FASTCALL CDTrack::GetFirst() const +{ + ASSERT(this); + ASSERT(valid); + ASSERT(first_lba < last_lba); + + return first_lba; +} + +//--------------------------------------------------------------------------- +// +// Get the end of LBA +// +//--------------------------------------------------------------------------- +DWORD FASTCALL CDTrack::GetLast() const +{ + ASSERT(this); + ASSERT(valid); + ASSERT(first_lba < last_lba); + + return last_lba; +} + +//--------------------------------------------------------------------------- +// +// Get the number of blocks +// +//--------------------------------------------------------------------------- +DWORD FASTCALL CDTrack::GetBlocks() const +{ + ASSERT(this); + ASSERT(valid); + ASSERT(first_lba < last_lba); + + // Calculate from start LBA and end LBA + return (DWORD)(last_lba - first_lba + 1); +} + +//--------------------------------------------------------------------------- +// +// Get track number +// +//--------------------------------------------------------------------------- +int FASTCALL CDTrack::GetTrackNo() const +{ + ASSERT(this); + ASSERT(valid); + ASSERT(track_no >= 1); + + return track_no; +} + +//--------------------------------------------------------------------------- +// +// Is valid block +// +//--------------------------------------------------------------------------- +BOOL FASTCALL CDTrack::IsValid(DWORD lba) const +{ + ASSERT(this); + + // FALSE if the track itself is invalid + if (!valid) { + return FALSE; + } + + // If the block is BEFORE the first block + if (lba < first_lba) { + return FALSE; + } + + // If the block is AFTER the last block + if (last_lba < lba) { + return FALSE; + } + + // This track is valid + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Is audio track +// +//--------------------------------------------------------------------------- +BOOL FASTCALL CDTrack::IsAudio() const +{ + ASSERT(this); + ASSERT(valid); + + return audio; +} + +//=========================================================================== +// +// CD-DA Buffer +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +CDDABuf::CDDABuf() +{ +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +CDDABuf::~CDDABuf() +{ +} + +//=========================================================================== +// +// SCSI CD-ROM +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +SCSICD::SCSICD() : Disk() +{ + int i; + + // SCSI CD-ROM + disk.id = MAKEID('S', 'C', 'C', 'D'); + + // removable, write protected + disk.removable = TRUE; + disk.writep = TRUE; + + // NOT in raw format + rawfile = FALSE; + + // Frame initialization + frame = 0; + + // Track initialization + for (i = 0; i < TrackMax; i++) { + track[i] = NULL; + } + tracks = 0; + dataindex = -1; + audioindex = -1; +} + +//--------------------------------------------------------------------------- +// +// Destructor +// +//--------------------------------------------------------------------------- +SCSICD::~SCSICD() +{ + // Clear track + ClearTrack(); +} + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Load +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::Load(Fileio *fio, int ver) +{ + DWORD sz; + disk_t buf; + DWORD padding; + Filepath path; + + ASSERT(this); + ASSERT(fio); + ASSERT(ver >= 0x0200); + + // Prior to version 2.03, the disk was not saved + if (ver <= 0x0202) { + return TRUE; + } + + // load size, match + if (!fio->Read(&sz, sizeof(sz))) { + return FALSE; + } + if (sz != 52) { + return FALSE; + } + + // load into buffer + PROP_IMPORT(fio, buf.id); + PROP_IMPORT(fio, buf.ready); + PROP_IMPORT(fio, buf.writep); + PROP_IMPORT(fio, buf.readonly); + PROP_IMPORT(fio, buf.removable); + PROP_IMPORT(fio, buf.lock); + PROP_IMPORT(fio, buf.attn); + PROP_IMPORT(fio, buf.reset); + PROP_IMPORT(fio, buf.size); + PROP_IMPORT(fio, buf.blocks); + PROP_IMPORT(fio, buf.lun); + PROP_IMPORT(fio, buf.code); + PROP_IMPORT(fio, padding); + + // Load path + if (!path.Load(fio, ver)) { + return FALSE; + } + + // Always eject + Eject(TRUE); + + // move only if IDs match + if (disk.id != buf.id) { + // It was not a CD-ROM when saving. Maintain eject status + return TRUE; + } + + // Try to reopen + if (!Open(path, FALSE)) { + // Cannot reopen. Maintain eject status + return TRUE; + } + + // Disk cache is created in Open. Move property only + if (!disk.readonly) { + disk.writep = buf.writep; + } + disk.lock = buf.lock; + disk.attn = buf.attn; + disk.reset = buf.reset; + disk.lun = buf.lun; + disk.code = buf.code; + + // Discard the disk cache again + if (disk.dcache) { + delete disk.dcache; + disk.dcache = NULL; + } + disk.dcache = NULL; + + // Tentative + disk.blocks = track[0]->GetBlocks(); + if (disk.blocks > 0) { + // Recreate the disk cache + track[0]->GetPath(path); + disk.dcache = new DiskCache(path, disk.size, disk.blocks); + disk.dcache->SetRawMode(rawfile); + + // Reset data index + dataindex = 0; + } + + return TRUE; +} +#endif // RASCSI + +//--------------------------------------------------------------------------- +// +// Open +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::Open(const Filepath& path, BOOL attn) +{ + Fileio fio; + off64_t size; + TCHAR file[5]; + + ASSERT(this); + ASSERT(!disk.ready); + + // Initialization, track clear + disk.blocks = 0; + rawfile = FALSE; + ClearTrack(); + + // Open as read-only + if (!fio.Open(path, Fileio::ReadOnly)) { + return FALSE; + } + + // Close and transfer for physical CD access + if (path.GetPath()[0] == _T('\\')) { + // Close + fio.Close(); + + // Open physical CD + if (!OpenPhysical(path)) { + return FALSE; + } + } else { + // Get file size + size = fio.GetFileSize(); + if (size <= 4) { + fio.Close(); + return FALSE; + } + + // Judge whether it is a CUE sheet or an ISO file + fio.Read(file, 4); + file[4] = '\0'; + fio.Close(); + + // If it starts with FILE, consider it as a CUE sheet + if (xstrncasecmp(file, _T("FILE"), 4) == 0) { + // Open as CUE + if (!OpenCue(path)) { + return FALSE; + } + } else { + // Open as ISO + if (!OpenIso(path)) { + return FALSE; + } + } + } + + // Successful opening + ASSERT(disk.blocks > 0); + disk.size = 11; + + // Call the base class + Disk::Open(path); + + // Set RAW flag + ASSERT(disk.dcache); + disk.dcache->SetRawMode(rawfile); + + // Since it is a ROM media, writing is not possible + disk.writep = TRUE; + + // Attention if ready + if (disk.ready && attn) { + disk.attn = TRUE; + } + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Open (CUE) +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::OpenCue(const Filepath& /*path*/) +{ + ASSERT(this); + + // Always fail + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// オープン(ISO) +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::OpenIso(const Filepath& path) +{ + Fileio fio; + off64_t size; + BYTE header[12]; + BYTE sync[12]; + + ASSERT(this); + + // Open as read-only + if (!fio.Open(path, Fileio::ReadOnly)) { + return FALSE; + } + + // Get file size + size = fio.GetFileSize(); + if (size < 0x800) { + fio.Close(); + return FALSE; + } + + // Read the first 12 bytes and close + if (!fio.Read(header, sizeof(header))) { + fio.Close(); + return FALSE; + } + + // Check if it is RAW format + memset(sync, 0xff, sizeof(sync)); + sync[0] = 0x00; + sync[11] = 0x00; + rawfile = FALSE; + if (memcmp(header, sync, sizeof(sync)) == 0) { + // 00,FFx10,00, so it is presumed to be RAW format + if (!fio.Read(header, 4)) { + fio.Close(); + return FALSE; + } + + // Supports MODE1/2048 or MODE1/2352 only + if (header[3] != 0x01) { + // Different mode + fio.Close(); + return FALSE; + } + + // Set to RAW file + rawfile = TRUE; + } + fio.Close(); + + if (rawfile) { + // Size must be a multiple of 2536 and less than 700MB + if (size % 0x930) { + return FALSE; + } + if (size > 912579600) { + return FALSE; + } + + // Set the number of blocks + disk.blocks = (DWORD)(size / 0x930); + } else { + // Size must be a multiple of 2048 and less than 700MB + if (size & 0x7ff) { + return FALSE; + } + if (size > 0x2bed5000) { + return FALSE; + } + + // Set the number of blocks + disk.blocks = (DWORD)(size >> 11); + } + + // Create only one data track + ASSERT(!track[0]); + track[0] = new CDTrack(this); + track[0]->Init(1, 0, disk.blocks - 1); + track[0]->SetPath(FALSE, path); + tracks = 1; + dataindex = 0; + + // Successful opening + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Open (Physical) +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::OpenPhysical(const Filepath& path) +{ + Fileio fio; + off64_t size; + + ASSERT(this); + + // Open as read-only + if (!fio.Open(path, Fileio::ReadOnly)) { + return FALSE; + } + + // Get size + size = fio.GetFileSize(); + if (size < 0x800) { + fio.Close(); + return FALSE; + } + + // Close + fio.Close(); + + // Size must be a multiple of 2048 and less than 700MB + if (size & 0x7ff) { + return FALSE; + } + if (size > 0x2bed5000) { + return FALSE; + } + + // Set the number of blocks + disk.blocks = (DWORD)(size >> 11); + + // Create only one data track + ASSERT(!track[0]); + track[0] = new CDTrack(this); + track[0]->Init(1, 0, disk.blocks - 1); + track[0]->SetPath(FALSE, path); + tracks = 1; + dataindex = 0; + + // Successful opening + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int FASTCALL SCSICD::Inquiry( + const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +{ + char rev[32]; + int size; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x12); + + // EVPD check + if (cdb[1] & 0x01) { + disk.code = DISK_INVALIDCDB; + return FALSE; + } + + // Basic data + // buf[0] ... CD-ROM Device + // buf[1] ... Removable + // buf[2] ... SCSI-2 compliant command system + // buf[3] ... SCSI-2 compliant Inquiry response + // buf[4] ... Inquiry additional data + memset(buf, 0, 8); + buf[0] = 0x05; + + // SCSI-2 p.104 4.4.3 Incorrect logical unit handling + if (((cdb[1] >> 5) & 0x07) != disk.lun) { + buf[0] = 0x7f; + } + + buf[1] = 0x80; + buf[2] = 0x02; + buf[3] = 0x02; + buf[4] = 36 - 5; // Required + + // Fill with blanks + memset(&buf[8], 0x20, buf[4] - 3); + + // Vendor name + memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); + + // Product name + memcpy(&buf[16], "CD-ROM CDU-55S", 14); + + // Revision (XM6 version number) + sprintf(rev, "0%01d%01d%01d", + (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); + memcpy(&buf[32], rev, 4); +// +// The following code worked with the modified Apple CD-ROM drivers. Need to +// test with the original code to see if it works as well.... +// buf[4] = 42; // Required +// +// // Fill with blanks +// memset(&buf[8], 0x20, buf[4] - 3); +// +// // Vendor name +// memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); +// +// // Product name +// memcpy(&buf[16], "CD-ROM CDU-8003A", 16); +// +// // Revision (XM6 version number) +//// sprintf(rev, "1.9a", +// //// (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); +// memcpy(&buf[32], "1.9a", 4); +// +// //strcpy(&buf[35],"A1.9a"); +// buf[36]=0x20; +// memcpy(&buf[37],"1999/01/01",10); + + // Size of data that can be returned + size = (buf[4] + 5); + + // Limit if the other buffer is small + if (size > (int)cdb[4]) { + size = (int)cdb[4]; + } + + // Success + disk.code = DISK_NOERROR; + return size; +} + +//--------------------------------------------------------------------------- +// +// READ +// +//--------------------------------------------------------------------------- +int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) +{ + int index; + Filepath path; + + ASSERT(this); + ASSERT(buf); + + // Status check + if (!CheckReady()) { + return 0; + } + + // Search for the track + index = SearchTrack(block); + + // if invalid, out of range + if (index < 0) { + disk.code = DISK_INVALIDLBA; + return 0; + } + ASSERT(track[index]); + + // If different from the current data track + if (dataindex != index) { + // Delete current disk cache (no need to save) + delete disk.dcache; + disk.dcache = NULL; + + // Reset the number of blocks + disk.blocks = track[index]->GetBlocks(); + ASSERT(disk.blocks > 0); + + // Recreate the disk cache + track[index]->GetPath(path); + disk.dcache = new DiskCache(path, disk.size, disk.blocks); + disk.dcache->SetRawMode(rawfile); + + // Reset data index + dataindex = index; + } + + // Base class + ASSERT(dataindex >= 0); + return Disk::Read(buf, block); +} + +//--------------------------------------------------------------------------- +// +// READ TOC +// +//--------------------------------------------------------------------------- +int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) +{ + int last; + int index; + int length; + int loop; + int i; + BOOL msf; + DWORD lba; + + ASSERT(this); + ASSERT(cdb); + ASSERT(cdb[0] == 0x43); + ASSERT(buf); + + // Check if ready + if (!CheckReady()) { + return 0; + } + + // If ready, there is at least one track + ASSERT(tracks > 0); + ASSERT(track[0]); + + // Get allocation length, clear buffer + length = cdb[7] << 8; + length |= cdb[8]; + memset(buf, 0, length); + + // Get MSF Flag + if (cdb[1] & 0x02) { + msf = TRUE; + } else { + msf = FALSE; + } + + // Get and check the last track number + last = track[tracks - 1]->GetTrackNo(); + if ((int)cdb[6] > last) { + // Except for AA + if (cdb[6] != 0xaa) { + disk.code = DISK_INVALIDCDB; + return 0; + } + } + + // Check start index + index = 0; + if (cdb[6] != 0x00) { + // Advance the track until the track numbers match + while (track[index]) { + if ((int)cdb[6] == track[index]->GetTrackNo()) { + break; + } + index++; + } + + // AA if not found or internal error + if (!track[index]) { + if (cdb[6] == 0xaa) { + // Returns the final LBA+1 because it is AA + buf[0] = 0x00; + buf[1] = 0x0a; + buf[2] = (BYTE)track[0]->GetTrackNo(); + buf[3] = (BYTE)last; + buf[6] = 0xaa; + lba = track[tracks - 1]->GetLast() + 1; + if (msf) { + LBAtoMSF(lba, &buf[8]); + } else { + buf[10] = (BYTE)(lba >> 8); + buf[11] = (BYTE)lba; + } + return length; + } + + // Otherwise, error + disk.code = DISK_INVALIDCDB; + return 0; + } + } + + // Number of track descriptors returned this time (number of loops) + loop = last - track[index]->GetTrackNo() + 1; + ASSERT(loop >= 1); + + // Create header + buf[0] = (BYTE)(((loop << 3) + 2) >> 8); + buf[1] = (BYTE)((loop << 3) + 2); + buf[2] = (BYTE)track[0]->GetTrackNo(); + buf[3] = (BYTE)last; + buf += 4; + + // Loop.... + for (i = 0; i < loop; i++) { + // ADR and Control + if (track[index]->IsAudio()) { + // audio track + buf[1] = 0x10; + } else { + // data track + buf[1] = 0x14; + } + + // track number + buf[2] = (BYTE)track[index]->GetTrackNo(); + + // track address + if (msf) { + LBAtoMSF(track[index]->GetFirst(), &buf[4]); + } else { + buf[6] = (BYTE)(track[index]->GetFirst() >> 8); + buf[7] = (BYTE)(track[index]->GetFirst()); + } + + // Advance buffer pointer and index + buf += 8; + index++; + } + + // Always return only the allocation length + return length; +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::PlayAudio(const DWORD* /*cdb*/) +{ + ASSERT(this); + + disk.code = DISK_INVALIDCDB; + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO MSF +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::PlayAudioMSF(const DWORD* /*cdb*/) +{ + ASSERT(this); + + disk.code = DISK_INVALIDCDB; + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// PLAY AUDIO TRACK +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::PlayAudioTrack(const DWORD* /*cdb*/) +{ + ASSERT(this); + + disk.code = DISK_INVALIDCDB; + return FALSE; +} + +//--------------------------------------------------------------------------- +// +// LBA→MSF Conversion +// +//--------------------------------------------------------------------------- +void FASTCALL SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const +{ + DWORD m; + DWORD s; + DWORD f; + + ASSERT(this); + + // 75 and 75*60 get the remainder + m = lba / (75 * 60); + s = lba % (75 * 60); + f = s % 75; + s /= 75; + + // The base point is M=0, S=2, F=0 + s += 2; + if (s >= 60) { + s -= 60; + m++; + } + + // Store + ASSERT(m < 0x100); + ASSERT(s < 60); + ASSERT(f < 75); + msf[0] = 0x00; + msf[1] = (BYTE)m; + msf[2] = (BYTE)s; + msf[3] = (BYTE)f; +} + +//--------------------------------------------------------------------------- +// +// MSF→LBA Conversion +// +//--------------------------------------------------------------------------- +DWORD FASTCALL SCSICD::MSFtoLBA(const BYTE *msf) const +{ + DWORD lba; + + ASSERT(this); + ASSERT(msf[2] < 60); + ASSERT(msf[3] < 75); + + // 1, 75, add up in multiples of 75*60 + lba = msf[1]; + lba *= 60; + lba += msf[2]; + lba *= 75; + lba += msf[3]; + + // Since the base point is M=0, S=2, F=0, subtract 150 + lba -= 150; + + return lba; +} + +//--------------------------------------------------------------------------- +// +// Clear Track +// +//--------------------------------------------------------------------------- +void FASTCALL SCSICD::ClearTrack() +{ + int i; + + ASSERT(this); + + // delete the track object + for (i = 0; i < TrackMax; i++) { + if (track[i]) { + delete track[i]; + track[i] = NULL; + } + } + + // Number of tracks is 0 + tracks = 0; + + // No settings for data and audio + dataindex = -1; + audioindex = -1; +} + +//--------------------------------------------------------------------------- +// +// Track Search +// * Returns -1 if not found +// +//--------------------------------------------------------------------------- +int FASTCALL SCSICD::SearchTrack(DWORD lba) const +{ + int i; + + ASSERT(this); + + // Track loop + for (i = 0; i < tracks; i++) { + // Listen to the track + ASSERT(track[i]); + if (track[i]->IsValid(lba)) { + return i; + } + } + + // Track wasn't found + return -1; +} + +//--------------------------------------------------------------------------- +// +// Next Frame +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSICD::NextFrame() +{ + ASSERT(this); + ASSERT((frame >= 0) && (frame < 75)); + + // set the frame in the range 0-74 + frame = (frame + 1) % 75; + + // FALSE after one lap + if (frame != 0) { + return TRUE; + } else { + return FALSE; + } +} + +//--------------------------------------------------------------------------- +// +// Get CD-DA buffer +// +//--------------------------------------------------------------------------- +void FASTCALL SCSICD::GetBuf( + DWORD* /*buffer*/, int /*samples*/, DWORD /*rate*/) +{ + ASSERT(this); +} diff --git a/src/raspberrypi/devices/scsicd.h b/src/raspberrypi/devices/scsicd.h new file mode 100644 index 00000000..ca4b9070 --- /dev/null +++ b/src/raspberrypi/devices/scsicd.h @@ -0,0 +1,230 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI Hard Disk for Apple Macintosh ] +// +//--------------------------------------------------------------------------- +#pragma once + +#include "os.h" +#include "disk.h" +#include "filepath.h" + + +//--------------------------------------------------------------------------- +// +// Class precedence definition +// +//--------------------------------------------------------------------------- +class SCSICD; + +//=========================================================================== +// +// CD-ROM Track +// +//=========================================================================== +class CDTrack +{ +public: + // Basic Functions + CDTrack(SCSICD *scsicd); + // Constructor + virtual ~CDTrack(); + // Destructor + BOOL FASTCALL Init(int track, DWORD first, DWORD last); + // Initialization + + // Properties + void FASTCALL SetPath(BOOL cdda, const Filepath& path); + // Set the path + void FASTCALL GetPath(Filepath& path) const; + // Get the path + void FASTCALL AddIndex(int index, DWORD lba); + // Add index + DWORD FASTCALL GetFirst() const; + // Get the start LBA + DWORD FASTCALL GetLast() const; + // Get the last LBA + DWORD FASTCALL GetBlocks() const; + // Get the number of blocks + int FASTCALL GetTrackNo() const; + // Get the track number + BOOL FASTCALL IsValid(DWORD lba) const; + // Is this a valid LBA? + BOOL FASTCALL IsAudio() const; + // Is this an audio track? + +private: + SCSICD *cdrom; + // Parent device + BOOL valid; + // Valid track + int track_no; + // Track number + DWORD first_lba; + // First LBA + DWORD last_lba; + // Last LBA + BOOL audio; + // Audio track flag + BOOL raw; + // RAW data flag + Filepath imgpath; + // Image file path +}; + +//=========================================================================== +// +// CD-DA Buffer +// +//=========================================================================== +class CDDABuf +{ +public: + // Basic Functions + CDDABuf(); + // Constructor + virtual ~CDDABuf(); + // Destructor +#if 0 + BOOL Init(); + // Initialization + BOOL FASTCALL Load(const Filepath& path); + // Load + BOOL FASTCALL Save(const Filepath& path); + // Save + + // API + void FASTCALL Clear(); + // Clear the buffer + BOOL FASTCALL Open(Filepath& path); + // File specification + BOOL FASTCALL GetBuf(DWORD *buffer, int frames); + // Get the buffer + BOOL FASTCALL IsValid(); + // Check if Valid + BOOL FASTCALL ReadReq(); + // Read Request + BOOL FASTCALL IsEnd() const; + // Finish check + +private: + Filepath wavepath; + // Wave path + BOOL valid; + // Open result (is it valid?) + DWORD *buf; + // Data buffer + DWORD read; + // Read pointer + DWORD write; + // Write pointer + DWORD num; + // Valid number of data + DWORD rest; + // Remaining file size +#endif +}; + +//=========================================================================== +// +// SCSI CD-ROM +// +//=========================================================================== +class SCSICD : public Disk +{ +public: + // Number of tracks + enum { + TrackMax = 96 // Maximum number of tracks + }; + +public: + // Basic Functions + SCSICD(); + // Constructor + virtual ~SCSICD(); + // Destructor + BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); + // Open +#ifndef RASCSI + BOOL FASTCALL Load(Fileio *fio, int ver); + // Load +#endif // RASCSI + + // commands + int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); + // INQUIRY command + int FASTCALL Read(BYTE *buf, DWORD block); + // READ command + int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf); + // READ TOC command + BOOL FASTCALL PlayAudio(const DWORD *cdb); + // PLAY AUDIO command + BOOL FASTCALL PlayAudioMSF(const DWORD *cdb); + // PLAY AUDIO MSF command + BOOL FASTCALL PlayAudioTrack(const DWORD *cdb); + // PLAY AUDIO TRACK command + + // CD-DA + BOOL FASTCALL NextFrame(); + // Frame notification + void FASTCALL GetBuf(DWORD *buffer, int samples, DWORD rate); + // Get CD-DA buffer + + // LBA-MSF変換 + void FASTCALL LBAtoMSF(DWORD lba, BYTE *msf) const; + // LBA→MSF conversion + DWORD FASTCALL MSFtoLBA(const BYTE *msf) const; + // MSF→LBA conversion + +private: + // Open + BOOL FASTCALL OpenCue(const Filepath& path); + // Open(CUE) + BOOL FASTCALL OpenIso(const Filepath& path); + // Open(ISO) + BOOL FASTCALL OpenPhysical(const Filepath& path); + // Open(Physical) + BOOL rawfile; + // RAW flag + + // Track management + void FASTCALL ClearTrack(); + // Clear the track + int FASTCALL SearchTrack(DWORD lba) const; + // Track search + CDTrack* track[TrackMax]; + // Track opbject references + int tracks; + // Effective number of track objects + int dataindex; + // Current data track + int audioindex; + // Current audio track + + int frame; + // Frame number + +#if 0 + CDDABuf da_buf; + // CD-DA buffer + int da_num; + // Number of CD-DA tracks + int da_cur; + // CD-DA current track + int da_next; + // CD-DA next track + BOOL da_req; + // CD-DA data request +#endif +}; diff --git a/src/raspberrypi/devices/scsihd.cpp b/src/raspberrypi/devices/scsihd.cpp new file mode 100644 index 00000000..ed4c32b1 --- /dev/null +++ b/src/raspberrypi/devices/scsihd.cpp @@ -0,0 +1,268 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI hard disk ] +// +//--------------------------------------------------------------------------- +#include "scsihd.h" +#include "xm6.h" +#include "fileio.h" + +//=========================================================================== +// +// SCSI Hard Disk +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +SCSIHD::SCSIHD() : Disk() +{ + // SCSI Hard Disk + disk.id = MAKEID('S', 'C', 'H', 'D'); +} + +//--------------------------------------------------------------------------- +// +// Reset +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIHD::Reset() +{ + // Unlock and release attention + disk.lock = FALSE; + disk.attn = FALSE; + + // No reset, clear code + disk.reset = FALSE; + disk.code = 0x00; +} + +//--------------------------------------------------------------------------- +// +// Open +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIHD::Open(const Filepath& path, BOOL /*attn*/) +{ + Fileio fio; + off64_t size; + + ASSERT(this); + ASSERT(!disk.ready); + + // read open required + if (!fio.Open(path, Fileio::ReadOnly)) { + return FALSE; + } + + // Get file size + size = fio.GetFileSize(); + fio.Close(); + + // Must be 512 bytes + if (size & 0x1ff) { + return FALSE; + } + + // 10MB or more + if (size < 0x9f5400) { + return FALSE; + } + // 2TB according to xm6i + // There is a similar one in wxw/wxw_cfg.cpp + if (size > 2LL * 1024 * 1024 * 1024 * 1024) { + return FALSE; + } + + // sector size and number of blocks + disk.size = 9; + disk.blocks = (DWORD)(size >> 9); + + // Call base class + return Disk::Open(path); +} + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIHD::Inquiry( + const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +{ + char vendor[32]; + char product[32]; + char rev[32]; + int size; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x12); + + // EVPD check + if (cdb[1] & 0x01) { + disk.code = DISK_INVALIDCDB; + return 0; + } + + // Ready check (Error if no image file) + if (!disk.ready) { + disk.code = DISK_NOTREADY; + return 0; + } + + // Basic data + // buf[0] ... Direct Access Device + // buf[2] ... SCSI-2 compliant command system + // buf[3] ... SCSI-2 compliant Inquiry response + // buf[4] ... Inquiry additional data + memset(buf, 0, 8); + + // SCSI-2 p.104 4.4.3 Incorrect logical unit handling + if (((cdb[1] >> 5) & 0x07) != disk.lun) { + buf[0] = 0x7f; + } + + buf[2] = 0x02; + buf[3] = 0x02; + buf[4] = 122 + 3; // Value close to real HDD + + // Fill with blanks + memset(&buf[8], 0x20, buf[4] - 3); + + // Determine vendor name/product name + sprintf(vendor, BENDER_SIGNATURE); + size = disk.blocks >> 11; + if (size < 300) + sprintf(product, "PRODRIVE LPS%dS", size); + else if (size < 600) + sprintf(product, "MAVERICK%dS", size); + else if (size < 800) + sprintf(product, "LIGHTNING%dS", size); + else if (size < 1000) + sprintf(product, "TRAILBRAZER%dS", size); + else if (size < 2000) + sprintf(product, "FIREBALL%dS", size); + else + sprintf(product, "FBSE%d.%dS", size / 1000, (size % 1000) / 100); + + // Vendor name + memcpy(&buf[8], vendor, strlen(vendor)); + + // Product name + memcpy(&buf[16], product, strlen(product)); + + // Revision + sprintf(rev, "0%01d%01d%01d", + (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); + memcpy(&buf[32], rev, 4); + + // Size of data that can be returned + size = (buf[4] + 5); + + // Limit if the other buffer is small + if (size > (int)cdb[4]) { + size = (int)cdb[4]; + } + + // Success + disk.code = DISK_NOERROR; + return size; +} + +//--------------------------------------------------------------------------- +// +// MODE SELECT +// *Not affected by disk.code +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) +{ + BYTE page; + int size; + + ASSERT(this); + ASSERT(buf); + ASSERT(length >= 0); + + // PF + if (cdb[1] & 0x10) { + // Mode Parameter header + if (length >= 12) { + // Check the block length bytes + size = 1 << disk.size; + if (buf[9] != (BYTE)(size >> 16) || + buf[10] != (BYTE)(size >> 8) || + buf[11] != (BYTE)size) { + // currently does not allow changing sector length + disk.code = DISK_INVALIDPRM; + return FALSE; + } + buf += 12; + length -= 12; + } + + // Parsing the page + while (length > 0) { + // Get page + page = buf[0]; + + switch (page) { + // format device + case 0x03: + // check the number of bytes in the physical sector + size = 1 << disk.size; + if (buf[0xc] != (BYTE)(size >> 8) || + buf[0xd] != (BYTE)size) { + // currently does not allow changing sector length + disk.code = DISK_INVALIDPRM; + return FALSE; + } + break; + + // CD-ROM Parameters + // According to the SONY CDU-541 manual, Page code 8 is supposed + // to set the Logical Block Adress Format, as well as the + // inactivity timer multiplier + case 0x08: + // Debug code for Issue #2: + // https://github.com/akuker/RASCSI/issues/2 + printf("[Unhandled page code] Received mode page code 8 with total length %d\n ", length); + for (int i = 0; i= (off64_t)sizeof(hdr)) { + if (!fio.Read(hdr, sizeof(hdr))) { + fio.Close(); + return FALSE; + } + } + fio.Close(); + + // 512バイト単位であること + if (size & 0x1ff) { + return FALSE; + } + + // 10MB以上 + if (size < 0x9f5400) { + return FALSE; + } + // xm6iに準じて2TB + // よく似たものが wxw/wxw_cfg.cpp にもある + if (size > 2LL * 1024 * 1024 * 1024 * 1024) { + return FALSE; + } + + // 拡張子別にパラメータを決定 + ext = path.GetFileExt(); + if (xstrcasecmp(ext, _T(".HDN")) == 0) { + // デフォルト設定としてセクタサイズ512,セクタ数25,ヘッド数8を想定 + imgoffset = 0; + imgsize = size; + sectorsize = 512; + sectors = 25; + heads = 8; + cylinders = (int)(size >> 9); + cylinders >>= 3; + cylinders /= 25; + } else if (xstrcasecmp(ext, _T(".HDI")) == 0) { // Anex86 HD image? + imgoffset = getDwordLE(&hdr[4 + 4]); + imgsize = getDwordLE(&hdr[4 + 4 + 4]); + sectorsize = getDwordLE(&hdr[4 + 4 + 4 + 4]); + sectors = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4]); + heads = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4]); + cylinders = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4 + 4]); + } else if (xstrcasecmp(ext, _T(".NHD")) == 0 && + memcmp(hdr, "T98HDDIMAGE.R0\0", 15) == 0) { // T98Next HD image? + imgoffset = getDwordLE(&hdr[0x10 + 0x100]); + cylinders = getDwordLE(&hdr[0x10 + 0x100 + 4]); + heads = getWordLE(&hdr[0x10 + 0x100 + 4 + 4]); + sectors = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2]); + sectorsize = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2 + 2]); + imgsize = (off64_t)cylinders * heads * sectors * sectorsize; + } + + // セクタサイズは256または512をサポート + if (sectorsize != 256 && sectorsize != 512) { + return FALSE; + } + + // イメージサイズの整合性チェック + if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) { + return FALSE; + } + + // セクタサイズ + for(disk.size = 16; disk.size > 0; --(disk.size)) { + if ((1 << disk.size) == sectorsize) + break; + } + if (disk.size <= 0 || disk.size > 16) { + return FALSE; + } + + // ブロック数 + disk.blocks = (DWORD)(imgsize >> disk.size); + disk.imgoffset = imgoffset; + + // Call the base class + return Disk::Open(path); +} + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIHD_NEC::Inquiry( + const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +{ + int size; + + // 基底クラス + size = SCSIHD::Inquiry(cdb, buf, major, minor); + + // 基底クラスでエラーなら終了 + if (size == 0) { + return 0; + } + + // SCSI1相当に変更 + buf[2] = 0x01; + buf[3] = 0x01; + + // Replace Vendor name + buf[8] = 'N'; + buf[9] = 'E'; + buf[10] = 'C'; + + return size; +} + +//--------------------------------------------------------------------------- +// +// エラーページ追加 +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIHD_NEC::AddError(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x01; + buf[1] = 0x06; + + // No changeable area + if (change) { + return 8; + } + + // リトライカウントは0、リミットタイムは装置内部のデフォルト値を使用 + return 8; +} + +//--------------------------------------------------------------------------- +// +// フォーマットページ追加 +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf) +{ + int size; + + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x80 | 0x03; + buf[1] = 0x16; + + // 物理セクタのバイト数は変更可能に見せる(実際には変更できないが) + if (change) { + buf[0xc] = 0xff; + buf[0xd] = 0xff; + return 24; + } + + if (disk.ready) { + // 1ゾーンのトラック数を設定(PC-9801-55はこの値を見ているようだ) + buf[0x2] = (BYTE)(heads >> 8); + buf[0x3] = (BYTE)heads; + + // 1トラックのセクタ数を設定 + buf[0xa] = (BYTE)(sectors >> 8); + buf[0xb] = (BYTE)sectors; + + // 物理セクタのバイト数を設定 + size = 1 << disk.size; + buf[0xc] = (BYTE)(size >> 8); + buf[0xd] = (BYTE)size; + } + + // リムーバブル属性を設定(昔の名残) + if (disk.removable) { + buf[20] = 0x20; + } + + return 24; +} + +//--------------------------------------------------------------------------- +// +// ドライブページ追加 +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIHD_NEC::AddDrive(BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Set the message length + buf[0] = 0x04; + buf[1] = 0x12; + + // No changeable area + if (change) { + return 20; + } + + if (disk.ready) { + // シリンダ数を設定 + buf[0x2] = (BYTE)(cylinders >> 16); + buf[0x3] = (BYTE)(cylinders >> 8); + buf[0x4] = (BYTE)cylinders; + + // ヘッド数を設定 + buf[0x5] = (BYTE)heads; + } + + return 20; +} diff --git a/src/raspberrypi/devices/scsihd_nec.h b/src/raspberrypi/devices/scsihd_nec.h new file mode 100644 index 00000000..270bf6bc --- /dev/null +++ b/src/raspberrypi/devices/scsihd_nec.h @@ -0,0 +1,61 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI NEC "Genuine" Hard Disk] +// +//--------------------------------------------------------------------------- +#pragma once + +#include "scsihd.h" + +//=========================================================================== +// +// SCSI hard disk (PC-9801-55 NEC genuine /Anex86/T98Next) +// +//=========================================================================== +class SCSIHD_NEC : public SCSIHD +{ +public: + // Basic Functions + SCSIHD_NEC(); + // Constructor + + BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); + // Open + + // commands + int FASTCALL Inquiry( + const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); + // INQUIRY command + + // Internal processing + int FASTCALL AddError(BOOL change, BYTE *buf); + // Add error + int FASTCALL AddFormat(BOOL change, BYTE *buf); + // Add format + int FASTCALL AddDrive(BOOL change, BYTE *buf); + // Add drive + +private: + int cylinders; + // Number of cylinders + int heads; + // Number of heads + int sectors; + // Number of sectors + int sectorsize; + // Sector size + off64_t imgoffset; + // Image offset + off64_t imgsize; + // Image size +}; \ No newline at end of file diff --git a/src/raspberrypi/devices/scsimo.cpp b/src/raspberrypi/devices/scsimo.cpp new file mode 100644 index 00000000..64d363cc --- /dev/null +++ b/src/raspberrypi/devices/scsimo.cpp @@ -0,0 +1,421 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI Magneto-Optical Disk] +// +//--------------------------------------------------------------------------- + +#include "scsimo.h" +#include "xm6.h" +#include "fileio.h" + +//=========================================================================== +// +// SCSI magneto-optical disk +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +SCSIMO::SCSIMO() : Disk() +{ + // SCSI magneto-optical disk + disk.id = MAKEID('S', 'C', 'M', 'O'); + + // Set as removable + disk.removable = TRUE; +} + +//--------------------------------------------------------------------------- +// +// Open +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIMO::Open(const Filepath& path, BOOL attn) +{ + Fileio fio; + off64_t size; + + ASSERT(this); + ASSERT(!disk.ready); + + // Open as read-only + if (!fio.Open(path, Fileio::ReadOnly)) { + return FALSE; + } + + // Get file size + size = fio.GetFileSize(); + fio.Close(); + + switch (size) { + // 128MB + case 0x797f400: + disk.size = 9; + disk.blocks = 248826; + break; + + // 230MB + case 0xd9eea00: + disk.size = 9; + disk.blocks = 446325; + break; + + // 540MB + case 0x1fc8b800: + disk.size = 9; + disk.blocks = 1041500; + break; + + // 640MB + case 0x25e28000: + disk.size = 11; + disk.blocks = 310352; + break; + + // Other (this is an error) + default: + return FALSE; + } + + // Call the base class + Disk::Open(path); + + // Attention if ready + if (disk.ready && attn) { + disk.attn = TRUE; + } + + return TRUE; +} + +#ifndef RASCSI +//--------------------------------------------------------------------------- +// +// Load +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIMO::Load(Fileio *fio, int ver) +{ + DWORD sz; + disk_t buf; + DWORD padding; + Filepath path; + + ASSERT(this); + ASSERT(fio); + ASSERT(ver >= 0x0200); + + // Prior to version 2.03, the disk was not saved + if (ver <= 0x0202) { + return TRUE; + } + + // load size, match + if (!fio->Read(&sz, sizeof(sz))) { + return FALSE; + } + if (sz != 52) { + return FALSE; + } + + // load into buffer + PROP_IMPORT(fio, buf.id); + PROP_IMPORT(fio, buf.ready); + PROP_IMPORT(fio, buf.writep); + PROP_IMPORT(fio, buf.readonly); + PROP_IMPORT(fio, buf.removable); + PROP_IMPORT(fio, buf.lock); + PROP_IMPORT(fio, buf.attn); + PROP_IMPORT(fio, buf.reset); + PROP_IMPORT(fio, buf.size); + PROP_IMPORT(fio, buf.blocks); + PROP_IMPORT(fio, buf.lun); + PROP_IMPORT(fio, buf.code); + PROP_IMPORT(fio, padding); + + // Load path + if (!path.Load(fio, ver)) { + return FALSE; + } + + // Always eject + Eject(TRUE); + + // Move only if IDs match + if (disk.id != buf.id) { + // Not MO at the time of save. Maintain eject status + return TRUE; + } + + // Re-try opening + if (!Open(path, FALSE)) { + // Cannot reopen. Maintain eject status + return TRUE; + } + + // Disk cache is created in Open. Move property only + if (!disk.readonly) { + disk.writep = buf.writep; + } + disk.lock = buf.lock; + disk.attn = buf.attn; + disk.reset = buf.reset; + disk.lun = buf.lun; + disk.code = buf.code; + + // loaded successfully + return TRUE; +} +#endif // RASCSI + +//--------------------------------------------------------------------------- +// +// INQUIRY +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIMO::Inquiry( + const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) +{ + int size; + char rev[32]; + + ASSERT(this); + ASSERT(cdb); + ASSERT(buf); + ASSERT(cdb[0] == 0x12); + + // EVPD check + if (cdb[1] & 0x01) { + disk.code = DISK_INVALIDCDB; + return FALSE; + } + + // 基本データ + // buf[0] ... Optical Memory Device + // buf[1] ... Removable + // buf[2] ... SCSI-2 compliant command system + // buf[3] ... SCSI-2 compliant Inquiry response + // buf[4] ... Inquiry additional data + memset(buf, 0, 8); + buf[0] = 0x07; + + // SCSI-2 p.104 4.4.3 Incorrect logical unit handling + if (((cdb[1] >> 5) & 0x07) != disk.lun) { + buf[0] = 0x7f; + } + + buf[1] = 0x80; + buf[2] = 0x02; + buf[3] = 0x02; + buf[4] = 36 - 5; // required + + // Fill with blanks + memset(&buf[8], 0x20, buf[4] - 3); + + // Vendor name + memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); + + // Product name + memcpy(&buf[16], "M2513A", 6); + + // Revision (XM6 version number) + sprintf(rev, "0%01d%01d%01d", + (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); + memcpy(&buf[32], rev, 4); + + // Size return data + size = (buf[4] + 5); + + // Limit the size if the buffer is too small + if (size > (int)cdb[4]) { + size = (int)cdb[4]; + } + + // Success + disk.code = DISK_NOERROR; + return size; +} + +//--------------------------------------------------------------------------- +// +// MODE SELECT +// *Not affected by disk.code +// +//--------------------------------------------------------------------------- +BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) +{ + int page; + int size; + + ASSERT(this); + ASSERT(buf); + ASSERT(length >= 0); + + // PF + if (cdb[1] & 0x10) { + // Mode Parameter header + if (length >= 12) { + // Check the block length (in bytes) + size = 1 << disk.size; + if (buf[9] != (BYTE)(size >> 16) || + buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) { + // Currently does not allow changing sector length + disk.code = DISK_INVALIDPRM; + return FALSE; + } + buf += 12; + length -= 12; + } + + // Parsing the page + while (length > 0) { + // Get the page + page = buf[0]; + + switch (page) { + // format device + case 0x03: + // Check the number of bytes in the physical sector + size = 1 << disk.size; + if (buf[0xc] != (BYTE)(size >> 8) || + buf[0xd] != (BYTE)size) { + // Currently does not allow changing sector length + disk.code = DISK_INVALIDPRM; + return FALSE; + } + break; + // vendor unique format + case 0x20: + // just ignore, for now + break; + + // Other page + default: + break; + } + + // Advance to the next page + size = buf[1] + 2; + length -= size; + buf += size; + } + } + + // Do not generate an error for the time being (MINIX) + disk.code = DISK_NOERROR; + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Vendor Unique Format Page 20h (MO) +// +//--------------------------------------------------------------------------- +int FASTCALL SCSIMO::AddVendor(int page, BOOL change, BYTE *buf) +{ + ASSERT(this); + ASSERT(buf); + + // Page code 20h + if ((page != 0x20) && (page != 0x3f)) { + return 0; + } + + // Set the message length + buf[0] = 0x20; + buf[1] = 0x0a; + + // No changeable area + if (change) { + return 12; + } + + /* + mode page code 20h - Vendor Unique Format Page + format mode XXh type 0 + information: http://h20628.www2.hp.com/km-ext/kmcsdirect/emr_na-lpg28560-1.pdf + + offset description + 02h format mode + 03h type of format (0) + 04~07h size of user band (total sectors?) + 08~09h size of spare band (spare sectors?) + 0A~0Bh number of bands + + actual value of each 3.5inches optical medium (grabbed by Fujitsu M2513EL) + + 128M 230M 540M 640M + --------------------------------------------------- + size of user band 3CBFAh 6CF75h FE45Ch 4BC50h + size of spare band 0400h 0401h 08CAh 08C4h + number of bands 0001h 000Ah 0012h 000Bh + + further information: http://r2089.blog36.fc2.com/blog-entry-177.html + */ + + if (disk.ready) { + unsigned spare = 0; + unsigned bands = 0; + + if (disk.size == 9) switch (disk.blocks) { + // 128MB + case 248826: + spare = 1024; + bands = 1; + break; + + // 230MB + case 446325: + spare = 1025; + bands = 10; + break; + + // 540MB + case 1041500: + spare = 2250; + bands = 18; + break; + } + + if (disk.size == 11) switch (disk.blocks) { + // 640MB + case 310352: + spare = 2244; + bands = 11; + break; + + // 1.3GB (lpproj: not tested with real device) + case 605846: + spare = 4437; + bands = 18; + break; + } + + buf[2] = 0; // format mode + buf[3] = 0; // type of format + buf[4] = (BYTE)(disk.blocks >> 24); + buf[5] = (BYTE)(disk.blocks >> 16); + buf[6] = (BYTE)(disk.blocks >> 8); + buf[7] = (BYTE)disk.blocks; + buf[8] = (BYTE)(spare >> 8); + buf[9] = (BYTE)spare; + buf[10] = (BYTE)(bands >> 8); + buf[11] = (BYTE)bands; + } + + return 12; +} diff --git a/src/raspberrypi/devices/scsimo.h b/src/raspberrypi/devices/scsimo.h new file mode 100644 index 00000000..f8aa2990 --- /dev/null +++ b/src/raspberrypi/devices/scsimo.h @@ -0,0 +1,49 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) +// Copyright (C) 2014-2020 GIMONS +// Copyright (C) akuker +// +// Licensed under the BSD 3-Clause License. +// See LICENSE file in the project root folder. +// +// [ SCSI Magneto-Optical Disk] +// +//--------------------------------------------------------------------------- +#pragma once + +#include "os.h" +#include "disk.h" +#include "filepath.h" + +//=========================================================================== +// +// SCSI magneto-optical disk +// +//=========================================================================== +class SCSIMO : public Disk +{ +public: + // Basic Functions + SCSIMO(); + // Constructor + BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); + // Open +#ifndef RASCSI + BOOL FASTCALL Load(Fileio *fio, int ver); + // Load +#endif // RASCSI + + // commands + int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); + // INQUIRY command + BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); + // MODE SELECT(6) command + + // Internal processing + int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); + // Add vendor special page +}; \ No newline at end of file diff --git a/src/raspberrypi/disk.cpp b/src/raspberrypi/disk.cpp deleted file mode 100644 index 20e453b7..00000000 --- a/src/raspberrypi/disk.cpp +++ /dev/null @@ -1,9872 +0,0 @@ -//--------------------------------------------------------------------------- -// -// X68000 EMULATOR "XM6" -// -// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) -// Copyright (C) 2014-2020 GIMONS -// -// XM6i -// Copyright (C) 2010-2015 isaki@NetBSD.org -// Copyright (C) 2010 Y.Sugahara -// -// Imported sava's Anex86/T98Next image and MO format support patch. -// Imported NetBSD support and some optimisation patch by Rin Okuyama. -// Comments translated to english by akuker. -// -// [ Disk ] -// -//--------------------------------------------------------------------------- - -#include "os.h" -#include "xm6.h" -#include "filepath.h" -#include "fileio.h" -#ifdef RASCSI -#include "gpiobus.h" -#ifndef BAREMETAL -#include "ctapdriver.h" -#endif // BAREMETAL -#include "cfilesystem.h" -#include "disk.h" -#else -#include "vm.h" -#include "disk.h" -#include "windrv.h" -#include "ctapdriver.h" -#include "mfc_com.h" -#include "mfc_host.h" -#endif // RASCSI - -//=========================================================================== -// -// Disk -// -//=========================================================================== -//#define DISK_LOG - -#ifdef RASCSI -#define BENDER_SIGNATURE "RaSCSI" -// The following line was to mimic Apple's CDROM ID -// #define BENDER_SIGNATURE "SONY " -#else -#define BENDER_SIGNATURE "XM6" -#endif - -//=========================================================================== -// -// Disk Track -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -DiskTrack::DiskTrack() -{ - // Initialization of internal information - dt.track = 0; - dt.size = 0; - dt.sectors = 0; - dt.raw = FALSE; - dt.init = FALSE; - dt.changed = FALSE; - dt.length = 0; - dt.buffer = NULL; - dt.maplen = 0; - dt.changemap = NULL; - dt.imgoffset = 0; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -DiskTrack::~DiskTrack() -{ - // Release memory, but do not save automatically - if (dt.buffer) { - free(dt.buffer); - dt.buffer = NULL; - } - if (dt.changemap) { - free(dt.changemap); - dt.changemap = NULL; - } -} - -//--------------------------------------------------------------------------- -// -// Initialization -// -//--------------------------------------------------------------------------- -void FASTCALL DiskTrack::Init( - int track, int size, int sectors, BOOL raw, off64_t imgoff) -{ - ASSERT(track >= 0); - ASSERT((size >= 8) && (size <= 11)); - ASSERT((sectors > 0) && (sectors <= 0x100)); - ASSERT(imgoff >= 0); - - // Set Parameters - dt.track = track; - dt.size = size; - dt.sectors = sectors; - dt.raw = raw; - - // Not initialized (needs to be loaded) - dt.init = FALSE; - - // Not Changed - dt.changed = FALSE; - - // Offset to actual data - dt.imgoffset = imgoff; -} - -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Load(const Filepath& path) -{ - Fileio fio; - off64_t offset; - int i; - int length; - - ASSERT(this); - - // Not needed if already loaded - if (dt.init) { - ASSERT(dt.buffer); - ASSERT(dt.changemap); - return TRUE; - } - - // Calculate offset (previous tracks are considered to - // hold 256 sectors) - offset = ((off64_t)dt.track << 8); - if (dt.raw) { - ASSERT(dt.size == 11); - offset *= 0x930; - offset += 0x10; - } else { - offset <<= dt.size; - } - - // Add offset to real image - offset += dt.imgoffset; - - // Calculate length (data size of this track) - length = dt.sectors << dt.size; - - // Allocate buffer memory - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - - if (dt.buffer == NULL) { -#if defined(RASCSI) && !defined(BAREMETAL) - posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512); -#else - dt.buffer = (BYTE *)malloc(length * sizeof(BYTE)); -#endif // RASCSI && !BAREMETAL - dt.length = length; - } - - if (!dt.buffer) { - return FALSE; - } - - // Reallocate if the buffer length is different - if (dt.length != (DWORD)length) { - free(dt.buffer); -#if defined(RASCSI) && !defined(BAREMETAL) - posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512); -#else - dt.buffer = (BYTE *)malloc(length * sizeof(BYTE)); -#endif // RASCSI && !BAREMETAL - dt.length = length; - } - - // Reserve change map memory - if (dt.changemap == NULL) { - dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); - dt.maplen = dt.sectors; - } - - if (!dt.changemap) { - return FALSE; - } - - // Reallocate if the buffer length is different - if (dt.maplen != (DWORD)dt.sectors) { - free(dt.changemap); - dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL)); - dt.maplen = dt.sectors; - } - - // Clear changemap - memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); - - // Read from File -#if defined(RASCSI) && !defined(BAREMETAL) - if (!fio.OpenDIO(path, Fileio::ReadOnly)) { -#else - if (!fio.Open(path, Fileio::ReadOnly)) { -#endif // RASCSI && !BAREMETAL - return FALSE; - } - if (dt.raw) { - // Split Reading - for (i = 0; i < dt.sectors; i++) { - // Seek - if (!fio.Seek(offset)) { - fio.Close(); - return FALSE; - } - - // Read - if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) { - fio.Close(); - return FALSE; - } - - // Next offset - offset += 0x930; - } - } else { - // Continuous reading - if (!fio.Seek(offset)) { - fio.Close(); - return FALSE; - } - if (!fio.Read(dt.buffer, length)) { - fio.Close(); - return FALSE; - } - } - fio.Close(); - - // Set a flag and end normally - dt.init = TRUE; - dt.changed = FALSE; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Save(const Filepath& path) -{ - off64_t offset; - int i; - int j; - Fileio fio; - int length; - int total; - - ASSERT(this); - - // Not needed if not initialized - if (!dt.init) { - return TRUE; - } - - // Not needed unless changed - if (!dt.changed) { - return TRUE; - } - - // Need to write - ASSERT(dt.buffer); - ASSERT(dt.changemap); - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - - // Writing in RAW mode is not allowed - ASSERT(!dt.raw); - - // Calculate offset (previous tracks are considered to hold 256 - offset = ((off64_t)dt.track << 8); - offset <<= dt.size; - - // Add offset to real image - offset += dt.imgoffset; - - // Calculate length per sector - length = 1 << dt.size; - - // Open file - if (!fio.Open(path, Fileio::ReadWrite)) { - return FALSE; - } - - // Partial write loop - for (i = 0; i < dt.sectors;) { - // If changed - if (dt.changemap[i]) { - // Initialize write size - total = 0; - - // Seek - if (!fio.Seek(offset + ((off64_t)i << dt.size))) { - fio.Close(); - return FALSE; - } - - // Consectutive sector length - for (j = i; j < dt.sectors; j++) { - // end when interrupted - if (!dt.changemap[j]) { - break; - } - - // Add one sector - total += length; - } - - // Write - if (!fio.Write(&dt.buffer[i << dt.size], total)) { - fio.Close(); - return FALSE; - } - - // To unmodified sector - i = j; - } else { - // Next Sector - i++; - } - } - - // Close - fio.Close(); - - // Drop the change flag and exit - memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL)); - dt.changed = FALSE; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Read Sector -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Read(BYTE *buf, int sec) const -{ - ASSERT(this); - ASSERT(buf); - ASSERT((sec >= 0) & (sec < 0x100)); - - // Error if not initialized - if (!dt.init) { - return FALSE; - } - - // // Error if the number of sectors exceeds the valid number - if (sec >= dt.sectors) { - return FALSE; - } - - // Copy - ASSERT(dt.buffer); - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - memcpy(buf, &dt.buffer[(off64_t)sec << dt.size], (off64_t)1 << dt.size); - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Write Sector -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskTrack::Write(const BYTE *buf, int sec) -{ - int offset; - int length; - - ASSERT(this); - ASSERT(buf); - ASSERT((sec >= 0) & (sec < 0x100)); - ASSERT(!dt.raw); - - // Error if not initialized - if (!dt.init) { - return FALSE; - } - - // // Error if the number of sectors exceeds the valid number - if (sec >= dt.sectors) { - return FALSE; - } - - // Calculate offset and length - offset = sec << dt.size; - length = 1 << dt.size; - - // Compare - ASSERT(dt.buffer); - ASSERT((dt.size >= 8) && (dt.size <= 11)); - ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100)); - if (memcmp(buf, &dt.buffer[offset], length) == 0) { - // 同じものを書き込もうとしているので、正常終了 - return TRUE; - } - - // Copy, change - memcpy(&dt.buffer[offset], buf, length); - dt.changemap[sec] = TRUE; - dt.changed = TRUE; - - // Success - return TRUE; -} - -//=========================================================================== -// -// Disk Cache -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -DiskCache::DiskCache( - const Filepath& path, int size, int blocks, off64_t imgoff) -{ - int i; - - ASSERT((size >= 8) && (size <= 11)); - ASSERT(blocks > 0); - ASSERT(imgoff >= 0); - - // Cache work - for (i = 0; i < CacheMax; i++) { - cache[i].disktrk = NULL; - cache[i].serial = 0; - } - - // Other - serial = 0; - sec_path = path; - sec_size = size; - sec_blocks = blocks; - cd_raw = FALSE; - imgoffset = imgoff; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -DiskCache::~DiskCache() -{ - // Clear the track - Clear(); -} - -//--------------------------------------------------------------------------- -// -// RAW Mode Setting -// -//--------------------------------------------------------------------------- -void FASTCALL DiskCache::SetRawMode(BOOL raw) -{ - ASSERT(this); - ASSERT(sec_size == 11); - - // Configuration - cd_raw = raw; -} - -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Save() -{ - int i; - - ASSERT(this); - - // Save track - for (i = 0; i < CacheMax; i++) { - // Is it a valid track? - if (cache[i].disktrk) { - // Save - if (!cache[i].disktrk->Save(sec_path)) { - return FALSE; - } - } - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Get disk cache information -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::GetCache(int index, int& track, DWORD& aserial) const -{ - ASSERT(this); - ASSERT((index >= 0) && (index < CacheMax)); - - // FALSE if unused - if (!cache[index].disktrk) { - return FALSE; - } - - // Set track and serial - track = cache[index].disktrk->GetTrack(); - aserial = cache[index].serial; - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Clear -// -//--------------------------------------------------------------------------- -void FASTCALL DiskCache::Clear() -{ - int i; - - ASSERT(this); - - // Free the cache - for (i = 0; i < CacheMax; i++) { - if (cache[i].disktrk) { - delete cache[i].disktrk; - cache[i].disktrk = NULL; - } - } -} - -//--------------------------------------------------------------------------- -// -// Sector Read -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Read(BYTE *buf, int block) -{ - int track; - DiskTrack *disktrk; - - ASSERT(this); - ASSERT(sec_size != 0); - - // Update first - Update(); - - // Calculate track (fixed to 256 sectors/track) - track = block >> 8; - - // Get the track data - disktrk = Assign(track); - if (!disktrk) { - return FALSE; - } - - // Read the track data to the cache - return disktrk->Read(buf, (BYTE)block); -} - -//--------------------------------------------------------------------------- -// -// Sector write -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Write(const BYTE *buf, int block) -{ - int track; - DiskTrack *disktrk; - - ASSERT(this); - ASSERT(sec_size != 0); - - // Update first - Update(); - - // Calculate track (fixed to 256 sectors/track) - track = block >> 8; - - // Get that track data - disktrk = Assign(track); - if (!disktrk) { - return FALSE; - } - - // Write the data to the cache - return disktrk->Write(buf, (BYTE)block); -} - -//--------------------------------------------------------------------------- -// -// Track Assignment -// -//--------------------------------------------------------------------------- -DiskTrack* FASTCALL DiskCache::Assign(int track) -{ - int i; - int c; - DWORD s; - DiskTrack *disktrk; - - ASSERT(this); - ASSERT(sec_size != 0); - ASSERT(track >= 0); - - // First, check if it is already assigned - for (i = 0; i < CacheMax; i++) { - if (cache[i].disktrk) { - if (cache[i].disktrk->GetTrack() == track) { - // Track match - cache[i].serial = serial; - return cache[i].disktrk; - } - } - } - - // Next, check for empty - for (i = 0; i < CacheMax; i++) { - if (!cache[i].disktrk) { - // Try loading - if (Load(i, track)) { - // Success loading - cache[i].serial = serial; - return cache[i].disktrk; - } - - // Load failed - return NULL; - } - } - - // Finally, find the youngest serial number and delete it - - // Set index 0 as candidate c - s = cache[0].serial; - c = 0; - - // Compare candidate with serial and update to smaller one - for (i = 0; i < CacheMax; i++) { - ASSERT(cache[i].disktrk); - - // Compare and update the existing serial - if (cache[i].serial < s) { - s = cache[i].serial; - c = i; - } - } - - // Save this track - if (!cache[c].disktrk->Save(sec_path)) { - return NULL; - } - - // Delete this track - disktrk = cache[c].disktrk; - cache[c].disktrk = NULL; - - // Load - if (Load(c, track, disktrk)) { - // Successful loading - cache[c].serial = serial; - return cache[c].disktrk; - } - - // Load failed - return NULL; -} - -//--------------------------------------------------------------------------- -// -// Load cache -// -//--------------------------------------------------------------------------- -BOOL FASTCALL DiskCache::Load(int index, int track, DiskTrack *disktrk) -{ - int sectors; - - ASSERT(this); - ASSERT((index >= 0) && (index < CacheMax)); - ASSERT(track >= 0); - ASSERT(!cache[index].disktrk); - - // Get the number of sectors on this track - sectors = sec_blocks - (track << 8); - ASSERT(sectors > 0); - if (sectors > 0x100) { - sectors = 0x100; - } - - // Create a disk track - if (disktrk == NULL) { - disktrk = new DiskTrack(); - } - - // Initialize disk track - disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset); - - // Try loading - if (!disktrk->Load(sec_path)) { - // 失敗 - delete disktrk; - return FALSE; - } - - // Allocation successful, work set - cache[index].disktrk = disktrk; - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Update serial number -// -//--------------------------------------------------------------------------- -void FASTCALL DiskCache::Update() -{ - int i; - - ASSERT(this); - - // Update and do nothing except 0 - serial++; - if (serial != 0) { - return; - } - - // Clear serial of all caches (loop in 32bit) - for (i = 0; i < CacheMax; i++) { - cache[i].serial = 0; - } -} - - -//=========================================================================== -// -// Disk -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -Disk::Disk() -{ - // Work initialization - disk.id = MAKEID('N', 'U', 'L', 'L'); - disk.ready = FALSE; - disk.writep = FALSE; - disk.readonly = FALSE; - disk.removable = FALSE; - disk.lock = FALSE; - disk.attn = FALSE; - disk.reset = FALSE; - disk.size = 0; - disk.blocks = 0; - disk.lun = 0; - disk.code = 0; - disk.dcache = NULL; - disk.imgoffset = 0; - - // Other - cache_wb = TRUE; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -Disk::~Disk() -{ - // Save disk cache - if (disk.ready) { - // Only if ready... - if (disk.dcache) { - disk.dcache->Save(); - } - } - - // Clear disk cache - if (disk.dcache) { - delete disk.dcache; - disk.dcache = NULL; - } -} - -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::Reset() -{ - ASSERT(this); - - // no lock, no attention, reset - disk.lock = FALSE; - disk.attn = FALSE; - disk.reset = TRUE; -} - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Save(Fileio *fio, int ver) -{ - DWORD sz; - DWORD padding; - - ASSERT(this); - ASSERT(fio); - - // Save size - sz = 52; - if (!fio->Write(&sz, sizeof(sz))) { - return FALSE; - } - - // Save entity - PROP_EXPORT(fio, disk.id); - PROP_EXPORT(fio, disk.ready); - PROP_EXPORT(fio, disk.writep); - PROP_EXPORT(fio, disk.readonly); - PROP_EXPORT(fio, disk.removable); - PROP_EXPORT(fio, disk.lock); - PROP_EXPORT(fio, disk.attn); - PROP_EXPORT(fio, disk.reset); - PROP_EXPORT(fio, disk.size); - PROP_EXPORT(fio, disk.blocks); - PROP_EXPORT(fio, disk.lun); - PROP_EXPORT(fio, disk.code); - PROP_EXPORT(fio, padding); - - // Save the path - if (!diskpath.Save(fio, ver)) { - return FALSE; - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Load(Fileio *fio, int ver) -{ - DWORD sz; - disk_t buf; - DWORD padding; - Filepath path; - - ASSERT(this); - ASSERT(fio); - - // Prior to version 2.03, the disk was not saved - if (ver <= 0x0202) { - return TRUE; - } - - // Delete the current disk cache - if (disk.dcache) { - disk.dcache->Save(); - delete disk.dcache; - disk.dcache = NULL; - } - - // Load size - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 52) { - return FALSE; - } - - // Load into buffer - PROP_IMPORT(fio, buf.id); - PROP_IMPORT(fio, buf.ready); - PROP_IMPORT(fio, buf.writep); - PROP_IMPORT(fio, buf.readonly); - PROP_IMPORT(fio, buf.removable); - PROP_IMPORT(fio, buf.lock); - PROP_IMPORT(fio, buf.attn); - PROP_IMPORT(fio, buf.reset); - PROP_IMPORT(fio, buf.size); - PROP_IMPORT(fio, buf.blocks); - PROP_IMPORT(fio, buf.lun); - PROP_IMPORT(fio, buf.code); - PROP_IMPORT(fio, padding); - - // Load path - if (!path.Load(fio, ver)) { - return FALSE; - } - - // Move only if IDs match - if (disk.id == buf.id) { - // Do nothing if null - if (IsNULL()) { - return TRUE; - } - - // Same type of device as when saving - disk.ready = FALSE; - if (Open(path)) { - // Disk cache is created in Open - // move only properties - if (!disk.readonly) { - disk.writep = buf.writep; - } - disk.lock = buf.lock; - disk.attn = buf.attn; - disk.reset = buf.reset; - disk.lun = buf.lun; - disk.code = buf.code; - - // Loaded successfully - return TRUE; - } - } - - // Disk cache rebuild - if (!IsReady()) { - disk.dcache = NULL; - } else { - disk.dcache = new DiskCache(diskpath, disk.size, disk.blocks); - } - - return TRUE; -} -#endif // RASCSI - -//--------------------------------------------------------------------------- -// -// NULL Check -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::IsNULL() const -{ - ASSERT(this); - - if (disk.id == MAKEID('N', 'U', 'L', 'L')) { - return TRUE; - } - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// SASI Check -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::IsSASI() const -{ - ASSERT(this); - - if (disk.id == MAKEID('S', 'A', 'H', 'D')) { - return TRUE; - } - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// Open -// * Call as a post-process after successful opening in a derived class -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Open(const Filepath& path, BOOL /*attn*/) -{ - Fileio fio; - - ASSERT(this); - ASSERT((disk.size >= 8) && (disk.size <= 11)); - ASSERT(disk.blocks > 0); - - // Ready - disk.ready = TRUE; - - // Cache initialization - ASSERT(!disk.dcache); - disk.dcache = - new DiskCache(path, disk.size, disk.blocks, disk.imgoffset); - - // Can read/write open - if (fio.Open(path, Fileio::ReadWrite)) { - // Write permission, not read only - disk.writep = FALSE; - disk.readonly = FALSE; - fio.Close(); - } else { - // Write protected, read only - disk.writep = TRUE; - disk.readonly = TRUE; - } - - // Not locked - disk.lock = FALSE; - - // Save path - diskpath = path; - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Eject -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::Eject(BOOL force) -{ - ASSERT(this); - - // Can only be ejected if it is removable - if (!disk.removable) { - return; - } - - // If you're not ready, you don't need to eject - if (!disk.ready) { - return; - } - - // Must be unlocked if there is no force flag - if (!force) { - if (disk.lock) { - return; - } - } - - // Remove disk cache - disk.dcache->Save(); - delete disk.dcache; - disk.dcache = NULL; - - // Not ready, no attention - disk.ready = FALSE; - disk.writep = FALSE; - disk.readonly = FALSE; - disk.attn = FALSE; -} - -//--------------------------------------------------------------------------- -// -// Write Protected -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::WriteP(BOOL writep) -{ - ASSERT(this); - - // be ready - if (!disk.ready) { - return; - } - - // Read Only, protect only - if (disk.readonly) { - ASSERT(disk.writep); - return; - } - - // Write protect flag setting - disk.writep = writep; -} - -//--------------------------------------------------------------------------- -// -// Get Disk -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::GetDisk(disk_t *buffer) const -{ - ASSERT(this); - ASSERT(buffer); - - // Assign internal buffer - *buffer = disk; -} - -//--------------------------------------------------------------------------- -// -// Get Path -// -//--------------------------------------------------------------------------- -void FASTCALL Disk::GetPath(Filepath& path) const -{ - path = diskpath; -} - -//--------------------------------------------------------------------------- -// -// Flush -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Flush() -{ - ASSERT(this); - - // Do nothing if there's nothing cached - if (!disk.dcache) { - return TRUE; - } - - // Save cache - return disk.dcache->Save(); -} - -//--------------------------------------------------------------------------- -// -// Check Ready -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::CheckReady() -{ - ASSERT(this); - - // Not ready if reset - if (disk.reset) { - disk.code = DISK_DEVRESET; - disk.reset = FALSE; - return FALSE; - } - - // Not ready if it needs attention - if (disk.attn) { - disk.code = DISK_ATTENTION; - disk.attn = FALSE; - return FALSE; - } - - // Return status if not ready - if (!disk.ready) { - disk.code = DISK_NOTREADY; - return FALSE; - } - - // Initialization with no error - disk.code = DISK_NOERROR; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// INQUIRY -// *You need to be successful at all times -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::Inquiry( - const DWORD* /*cdb*/, BYTE* /*buf*/, DWORD /*major*/, DWORD /*minor*/) -{ - ASSERT(this); - - // default is INQUIRY failure - disk.code = DISK_INVALIDCMD; - return 0; -} - -//--------------------------------------------------------------------------- -// -// REQUEST SENSE -// *SASI is a separate process -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::RequestSense(const DWORD *cdb, BYTE *buf) -{ - int size; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - - // Return not ready only if there are no errors - if (disk.code == DISK_NOERROR) { - if (!disk.ready) { - disk.code = DISK_NOTREADY; - } - } - - // Size determination (according to allocation length) - size = (int)cdb[4]; - ASSERT((size >= 0) && (size < 0x100)); - - // For SCSI-1, transfer 4 bytes when the size is 0 - // (Deleted this specification for SCSI-2) - if (size == 0) { - size = 4; - } - - // Clear the buffer - memset(buf, 0, size); - - // Set 18 bytes including extended sense data - buf[0] = 0x70; - buf[2] = (BYTE)(disk.code >> 16); - buf[7] = 10; - buf[12] = (BYTE)(disk.code >> 8); - buf[13] = (BYTE)disk.code; - - // Clear the code - disk.code = 0x00; - - return size; -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT check -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::SelectCheck(const DWORD *cdb) -{ - int length; - - ASSERT(this); - ASSERT(cdb); - - // Error if save parameters are set instead of SCSIHD - if (disk.id != MAKEID('S', 'C', 'H', 'D')) { - // Error if save parameters are set - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return 0; - } - } - - // Receive the data specified by the parameter length - length = (int)cdb[4]; - return length; -} - - -//--------------------------------------------------------------------------- -// -// MODE SELECT(10) check -// * Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::SelectCheck10(const DWORD *cdb) -{ - DWORD length; - - ASSERT(this); - ASSERT(cdb); - - // Error if save parameters are set instead of SCSIHD - if (disk.id != MAKEID('S', 'C', 'H', 'D')) { - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return 0; - } - } - - // Receive the data specified by the parameter length - length = cdb[7]; - length <<= 8; - length |= cdb[8]; - if (length > 0x800) { - length = 0x800; - } - - return (int)length; -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT -// * Not affected by disk.code -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::ModeSelect( - const DWORD* /*cdb*/, const BYTE *buf, int length) -{ - ASSERT(this); - ASSERT(buf); - ASSERT(length >= 0); - - // cannot be set - disk.code = DISK_INVALIDPRM; - - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// MODE SENSE -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ModeSense(const DWORD *cdb, BYTE *buf) -{ - int page; - int length; - int size; - BOOL valid; - BOOL change; - int ret; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x1a); - - // Get length, clear buffer - length = (int)cdb[4]; - ASSERT((length >= 0) && (length < 0x100)); - memset(buf, 0, length); - - // Get changeable flag - if ((cdb[2] & 0xc0) == 0x40) { - change = TRUE; - } else { - change = FALSE; - } - - // Get page code (0x00 is valid from the beginning) - page = cdb[2] & 0x3f; - if (page == 0x00) { - valid = TRUE; - } else { - valid = FALSE; - } - - // Basic information - size = 4; - - // MEDIUM TYPE - if (disk.id == MAKEID('S', 'C', 'M', 'O')) { - buf[1] = 0x03; // optical reversible or erasable - } - - // DEVICE SPECIFIC PARAMETER - if (disk.writep) { - buf[2] = 0x80; - } - - // add block descriptor if DBD is 0 - if ((cdb[1] & 0x08) == 0) { - // Mode parameter header - buf[3] = 0x08; - - // Only if ready - if (disk.ready) { - // Block descriptor (number of blocks) - buf[5] = (BYTE)(disk.blocks >> 16); - buf[6] = (BYTE)(disk.blocks >> 8); - buf[7] = (BYTE)disk.blocks; - - // Block descriptor (block length) - size = 1 << disk.size; - buf[9] = (BYTE)(size >> 16); - buf[10] = (BYTE)(size >> 8); - buf[11] = (BYTE)size; - } - - // size - size = 12; - } - - // Page code 1(read-write error recovery) - if ((page == 0x01) || (page == 0x3f)) { - size += AddError(change, &buf[size]); - valid = TRUE; - } - - // Page code 3(format device) - if ((page == 0x03) || (page == 0x3f)) { - size += AddFormat(change, &buf[size]); - valid = TRUE; - } - - // Page code 4(drive parameter) - if ((page == 0x04) || (page == 0x3f)) { - size += AddDrive(change, &buf[size]); - valid = TRUE; - } - - // Page code 6(optical) - if (disk.id == MAKEID('S', 'C', 'M', 'O')) { - if ((page == 0x06) || (page == 0x3f)) { - size += AddOpt(change, &buf[size]); - valid = TRUE; - } - } - - // Page code 8(caching) - if ((page == 0x08) || (page == 0x3f)) { - size += AddCache(change, &buf[size]); - valid = TRUE; - } - - // Page code 13(CD-ROM) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { - if ((page == 0x0d) || (page == 0x3f)) { - size += AddCDROM(change, &buf[size]); - valid = TRUE; - } - } - - // Page code 14(CD-DA) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { - if ((page == 0x0e) || (page == 0x3f)) { - size += AddCDDA(change, &buf[size]); - valid = TRUE; - } - } - - // Page (vendor special) - ret = AddVendor(page, change, &buf[size]); - if (ret > 0) { - size += ret; - valid = TRUE; - } - - // final setting of mode data length - buf[0] = (BYTE)(size - 1); - - // Unsupported page - if (!valid) { - disk.code = DISK_INVALIDCDB; - return 0; - } - - // MODE SENSE success - disk.code = DISK_NOERROR; - return length; -} - -//--------------------------------------------------------------------------- -// -// MODE SENSE(10) -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ModeSense10(const DWORD *cdb, BYTE *buf) -{ - int page; - int length; - int size; - BOOL valid; - BOOL change; - int ret; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x5a); - - // Get length, clear buffer - length = cdb[7]; - length <<= 8; - length |= cdb[8]; - if (length > 0x800) { - length = 0x800; - } - ASSERT((length >= 0) && (length < 0x800)); - memset(buf, 0, length); - - // Get changeable flag - if ((cdb[2] & 0xc0) == 0x40) { - change = TRUE; - } else { - change = FALSE; - } - - // Get page code (0x00 is valid from the beginning) - page = cdb[2] & 0x3f; - if (page == 0x00) { - valid = TRUE; - } else { - valid = FALSE; - } - - // Basic Information - size = 4; - if (disk.writep) { - buf[2] = 0x80; - } - - // add block descriptor if DBD is 0 - if ((cdb[1] & 0x08) == 0) { - // Mode parameter header - buf[3] = 0x08; - - // Only if ready - if (disk.ready) { - // Block descriptor (number of blocks) - buf[5] = (BYTE)(disk.blocks >> 16); - buf[6] = (BYTE)(disk.blocks >> 8); - buf[7] = (BYTE)disk.blocks; - - // Block descriptor (block length) - size = 1 << disk.size; - buf[9] = (BYTE)(size >> 16); - buf[10] = (BYTE)(size >> 8); - buf[11] = (BYTE)size; - } - - // Size - size = 12; - } - - // Page code 1(read-write error recovery) - if ((page == 0x01) || (page == 0x3f)) { - size += AddError(change, &buf[size]); - valid = TRUE; - } - - // Page code 3(format device) - if ((page == 0x03) || (page == 0x3f)) { - size += AddFormat(change, &buf[size]); - valid = TRUE; - } - - // Page code 4(drive parameter) - if ((page == 0x04) || (page == 0x3f)) { - size += AddDrive(change, &buf[size]); - valid = TRUE; - } - - // ペPage code 6(optical) - if (disk.id == MAKEID('S', 'C', 'M', 'O')) { - if ((page == 0x06) || (page == 0x3f)) { - size += AddOpt(change, &buf[size]); - valid = TRUE; - } - } - - // Page code 8(caching) - if ((page == 0x08) || (page == 0x3f)) { - size += AddCache(change, &buf[size]); - valid = TRUE; - } - - // Page code 13(CD-ROM) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { - if ((page == 0x0d) || (page == 0x3f)) { - size += AddCDROM(change, &buf[size]); - valid = TRUE; - } - } - - // Page code 14(CD-DA) - if (disk.id == MAKEID('S', 'C', 'C', 'D')) { - if ((page == 0x0e) || (page == 0x3f)) { - size += AddCDDA(change, &buf[size]); - valid = TRUE; - } - } - - // Page (vendor special) - ret = AddVendor(page, change, &buf[size]); - if (ret > 0) { - size += ret; - valid = TRUE; - } - - // final setting of mode data length - buf[0] = (BYTE)(size - 1); - - // Unsupported page - if (!valid) { - disk.code = DISK_INVALIDCDB; - return 0; - } - - // MODE SENSE success - disk.code = DISK_NOERROR; - return length; -} - -//--------------------------------------------------------------------------- -// -// Add error page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddError(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x01; - buf[1] = 0x0a; - - // No changeable area - if (change) { - return 12; - } - - // Retry count is 0, limit time uses internal default value - return 12; -} - -//--------------------------------------------------------------------------- -// -// Add format page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddFormat(BOOL change, BYTE *buf) -{ - int size; - - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x80 | 0x03; - buf[1] = 0x16; - - // Show the number of bytes in the physical sector as changeable - // (though it cannot be changed in practice) - if (change) { - buf[0xc] = 0xff; - buf[0xd] = 0xff; - return 24; - } - - if (disk.ready) { - // Set the number of tracks in one zone to 8 (TODO) - buf[0x3] = 0x08; - - // Set sector/track to 25 (TODO) - buf[0xa] = 0x00; - buf[0xb] = 0x19; - - // Set the number of bytes in the physical sector - size = 1 << disk.size; - buf[0xc] = (BYTE)(size >> 8); - buf[0xd] = (BYTE)size; - } - - // Set removable attribute - if (disk.removable) { - buf[20] = 0x20; - } - - return 24; -} - -//--------------------------------------------------------------------------- -// -// Add drive page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddDrive(BOOL change, BYTE *buf) -{ - DWORD cylinder; - - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x04; - buf[1] = 0x16; - - // No changeable area - if (change) { - return 24; - } - - if (disk.ready) { - // Set the number of cylinders (total number of blocks - // divided by 25 sectors/track and 8 heads) - cylinder = disk.blocks; - cylinder >>= 3; - cylinder /= 25; - buf[0x2] = (BYTE)(cylinder >> 16); - buf[0x3] = (BYTE)(cylinder >> 8); - buf[0x4] = (BYTE)cylinder; - - // Fix the head at 8 - buf[0x5] = 0x8; - } - - return 24; -} - -//--------------------------------------------------------------------------- -// -// Add option -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddOpt(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x06; - buf[1] = 0x02; - - // No changeable area - if (change) { - return 4; - } - - // Do not report update blocks - return 4; -} - -//--------------------------------------------------------------------------- -// -// Add Cache Page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddCache(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x08; - buf[1] = 0x0a; - - // No changeable area - if (change) { - return 12; - } - - // Only read cache is valid, no prefetch - return 12; -} - -//--------------------------------------------------------------------------- -// -// Add CDROM Page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddCDROM(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x0d; - buf[1] = 0x06; - - // No changeable area - if (change) { - return 8; - } - - // 2 seconds for inactive timer - buf[3] = 0x05; - - // MSF multiples are 60 and 75 respectively - buf[5] = 60; - buf[7] = 75; - - return 8; -} - -//--------------------------------------------------------------------------- -// -// CD-DAページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddCDDA(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x0e; - buf[1] = 0x0e; - - // No changeable area - if (change) { - return 16; - } - - // Audio waits for operation completion and allows - // PLAY across multiple tracks - return 16; -} - -//--------------------------------------------------------------------------- -// -// Add special vendor page -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::AddVendor(int /*page*/, BOOL /*change*/, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - return 0; -} - -//--------------------------------------------------------------------------- -// -// READ DEFECT DATA(10) -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf) -{ - DWORD length; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x37); - - // Get length, clear buffer - length = cdb[7]; - length <<= 8; - length |= cdb[8]; - if (length > 0x800) { - length = 0x800; - } - ASSERT((length >= 0) && (length < 0x800)); - memset(buf, 0, length); - - // P/G/FORMAT - buf[1] = (cdb[1] & 0x18) | 5; - buf[3] = 8; - - buf[4] = 0xff; - buf[5] = 0xff; - buf[6] = 0xff; - buf[7] = 0xff; - - buf[8] = 0xff; - buf[9] = 0xff; - buf[10] = 0xff; - buf[11] = 0xff; - - // no list - disk.code = DISK_NODEFECT; - return 4; -} - -//--------------------------------------------------------------------------- -// -// Command -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// TEST UNIT READY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::TestUnitReady(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // TEST UNIT READY Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// REZERO UNIT -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Rezero(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // REZERO Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// FORMAT UNIT -// *Opcode $06 for SASI, Opcode $04 for SCSI -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Format(const DWORD *cdb) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // FMTDATA=1 is not supported (but OK if there is no DEFECT LIST) - if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) { - disk.code = DISK_INVALIDCDB; - return FALSE; - } - - // FORMAT Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// REASSIGN BLOCKS -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Reassign(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // REASSIGN BLOCKS Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// READ -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::Read(BYTE *buf, DWORD block) -{ - ASSERT(this); - ASSERT(buf); - - // Status check - if (!CheckReady()) { - return 0; - } - - // Error if the total number of blocks is exceeded - if (block >= disk.blocks) { - disk.code = DISK_INVALIDLBA; - return 0; - } - - // leave it to the cache - if (!disk.dcache->Read(buf, block)) { - disk.code = DISK_READFAULT; - return 0; - } - - // Success - return (1 << disk.size); -} - -//--------------------------------------------------------------------------- -// -// WRITE check -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::WriteCheck(DWORD block) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return 0; - } - - // Error if the total number of blocks is exceeded - if (block >= disk.blocks) { - return 0; - } - - // Error if write protected - if (disk.writep) { - disk.code = DISK_WRITEPROTECT; - return 0; - } - - // Success - return (1 << disk.size); -} - -//--------------------------------------------------------------------------- -// -// WRITE -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Write(const BYTE *buf, DWORD block) -{ - ASSERT(this); - ASSERT(buf); - - // Error if not ready - if (!disk.ready) { - disk.code = DISK_NOTREADY; - return FALSE; - } - - // Error if the total number of blocks is exceeded - if (block >= disk.blocks) { - disk.code = DISK_INVALIDLBA; - return FALSE; - } - - // Error if write protected - if (disk.writep) { - disk.code = DISK_WRITEPROTECT; - return FALSE; - } - - // Leave it to the cache - if (!disk.dcache->Write(buf, block)) { - disk.code = DISK_WRITEFAULT; - return FALSE; - } - - // Success - disk.code = DISK_NOERROR; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// SEEK -// *Does not check LBA (SASI IOCS) -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Seek(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // SEEK Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// ASSIGN -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Assign(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// SPECIFY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Specify(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// START STOP UNIT -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::StartStop(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x1b); - - // Look at the eject bit and eject if necessary - if (cdb[4] & 0x02) { - if (disk.lock) { - // Cannot be ejected because it is locked - disk.code = DISK_PREVENT; - return FALSE; - } - - // Eject - Eject(FALSE); - } - - // OK - disk.code = DISK_NOERROR; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// SEND DIAGNOSTIC -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::SendDiag(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x1d); - - // Do not support PF bit - if (cdb[1] & 0x10) { - disk.code = DISK_INVALIDCDB; - return FALSE; - } - - // Do not support parameter list - if ((cdb[3] != 0) || (cdb[4] != 0)) { - disk.code = DISK_INVALIDCDB; - return FALSE; - } - - // Always successful - disk.code = DISK_NOERROR; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// PREVENT/ALLOW MEDIUM REMOVAL -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Removal(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x1e); - - // Status check - if (!CheckReady()) { - return FALSE; - } - - // Set Lock flag - if (cdb[4] & 0x01) { - disk.lock = TRUE; - } else { - disk.lock = FALSE; - } - - // REMOVAL Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// READ CAPACITY -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf) -{ - DWORD blocks; - DWORD length; - - ASSERT(this); - ASSERT(buf); - - // Buffer clear - memset(buf, 0, 8); - - // Status check - if (!CheckReady()) { - return 0; - } - - // Create end of logical block address (disk.blocks-1) - ASSERT(disk.blocks > 0); - blocks = disk.blocks - 1; - buf[0] = (BYTE)(blocks >> 24); - buf[1] = (BYTE)(blocks >> 16); - buf[2] = (BYTE)(blocks >> 8); - buf[3] = (BYTE)blocks; - - // Create block length (1 << disk.size) - length = 1 << disk.size; - buf[4] = (BYTE)(length >> 24); - buf[5] = (BYTE)(length >> 16); - buf[6] = (BYTE)(length >> 8); - buf[7] = (BYTE)length; - - // return the size - return 8; -} - -//--------------------------------------------------------------------------- -// -// VERIFY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::Verify(const DWORD *cdb) -{ - DWORD record; - DWORD blocks; - - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x2f); - - // Get parameters - record = cdb[2]; - record <<= 8; - record |= cdb[3]; - record <<= 8; - record |= cdb[4]; - record <<= 8; - record |= cdb[5]; - blocks = cdb[7]; - blocks <<= 8; - blocks |= cdb[8]; - - // Status check - if (!CheckReady()) { - return 0; - } - - // Parameter check - if (disk.blocks < (record + blocks)) { - disk.code = DISK_INVALIDLBA; - return FALSE; - } - - // Success - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// READ TOC -// -//--------------------------------------------------------------------------- -int FASTCALL Disk::ReadToc(const DWORD *cdb, BYTE *buf) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x43); - ASSERT(buf); - - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::PlayAudio(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x45); - - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO MSF -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::PlayAudioMSF(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x47); - - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO TRACK -// -//--------------------------------------------------------------------------- -BOOL FASTCALL Disk::PlayAudioTrack(const DWORD *cdb) -{ - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x48); - - // This command is not supported - disk.code = DISK_INVALIDCMD; - return FALSE; -} - -//=========================================================================== -// -// SASI Hard Disk -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SASIHD::SASIHD() : Disk() -{ - // SASI ハードディスク - disk.id = MAKEID('S', 'A', 'H', 'D'); -} - -//--------------------------------------------------------------------------- -// -// リセット -// -//--------------------------------------------------------------------------- -void FASTCALL SASIHD::Reset() -{ - // ロック状態解除、アテンション解除 - disk.lock = FALSE; - disk.attn = FALSE; - - // Resetなし、コードをクリア - disk.reset = FALSE; - disk.code = 0x00; -} - -//--------------------------------------------------------------------------- -// -// オープン -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIHD::Open(const Filepath& path, BOOL /*attn*/) -{ - Fileio fio; - off64_t size; - - ASSERT(this); - ASSERT(!disk.ready); - - // Open as read-only - if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; - } - - // Get file size - size = fio.GetFileSize(); - fio.Close(); - -#if defined(USE_MZ1F23_1024_SUPPORT) - // MZ-2500/MZ-2800用 MZ-1F23(SASI 20M/セクタサイズ1024)専用 - // 20M(22437888 BS=1024 C=21912) - if (size == 0x1566000) { - // セクタサイズとブロック数 - disk.size = 10; - disk.blocks = (DWORD)(size >> 10); - - // Call the base class - return Disk::Open(path); - } -#endif // USE_MZ1F23_1024_SUPPORT - -#if defined(REMOVE_FIXED_SASIHD_SIZE) - // 256バイト単位であること - if (size & 0xff) { - return FALSE; - } - - // 10MB以上 - if (size < 0x9f5400) { - return FALSE; - } - - // 512MB程度に制限しておく - if (size > 512 * 1024 * 1024) { - return FALSE; - } -#else - // 10MB, 20MB, 40MBのみ - switch (size) { - // 10MB(10441728 BS=256 C=40788) - case 0x9f5400: - break; - - // 20MB(20748288 BS=256 C=81048) - case 0x13c9800: - break; - - // 40MB(41496576 BS=256 C=162096) - case 0x2793000: - break; - - // Other(サポートしない) - default: - return FALSE; - } -#endif // REMOVE_FIXED_SASIHD_SIZE - - // セクタサイズとブロック数 - disk.size = 8; - disk.blocks = (DWORD)(size >> 8); - - // Call the base class - return Disk::Open(path); -} - -//--------------------------------------------------------------------------- -// -// REQUEST SENSE -// -//--------------------------------------------------------------------------- -int FASTCALL SASIHD::RequestSense(const DWORD *cdb, BYTE *buf) -{ - int size; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - - // サイズ決定 - size = (int)cdb[4]; - ASSERT((size >= 0) && (size < 0x100)); - - // サイズ0のときに4バイト転送する(Shugart Associates System Interface仕様) - if (size == 0) { - size = 4; - } - - // SASIは非拡張フォーマットに固定 - memset(buf, 0, size); - buf[0] = (BYTE)(disk.code >> 16); - buf[1] = (BYTE)(disk.lun << 5); - - // コードをクリア - disk.code = 0x00; - - return size; -} - -//=========================================================================== -// -// SCSI ハードディスク -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SCSIHD::SCSIHD() : Disk() -{ - // SCSI Hard Disk - disk.id = MAKEID('S', 'C', 'H', 'D'); -} - -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIHD::Reset() -{ - // Unlock and release attention - disk.lock = FALSE; - disk.attn = FALSE; - - // No reset, clear code - disk.reset = FALSE; - disk.code = 0x00; -} - -//--------------------------------------------------------------------------- -// -// Open -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIHD::Open(const Filepath& path, BOOL /*attn*/) -{ - Fileio fio; - off64_t size; - - ASSERT(this); - ASSERT(!disk.ready); - - // read open required - if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; - } - - // Get file size - size = fio.GetFileSize(); - fio.Close(); - - // Must be 512 bytes - if (size & 0x1ff) { - return FALSE; - } - - // 10MB or more - if (size < 0x9f5400) { - return FALSE; - } - // 2TB according to xm6i - // There is a similar one in wxw/wxw_cfg.cpp - if (size > 2LL * 1024 * 1024 * 1024 * 1024) { - return FALSE; - } - - // sector size and number of blocks - disk.size = 9; - disk.blocks = (DWORD)(size >> 9); - - // Call base class - return Disk::Open(path); -} - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) -{ - char vendor[32]; - char product[32]; - char rev[32]; - int size; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x12); - - // EVPD check - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return 0; - } - - // Ready check (Error if no image file) - if (!disk.ready) { - disk.code = DISK_NOTREADY; - return 0; - } - - // Basic data - // buf[0] ... Direct Access Device - // buf[2] ... SCSI-2 compliant command system - // buf[3] ... SCSI-2 compliant Inquiry response - // buf[4] ... Inquiry additional data - memset(buf, 0, 8); - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - - buf[2] = 0x02; - buf[3] = 0x02; - buf[4] = 122 + 3; // Value close to real HDD - - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Determine vendor name/product name - sprintf(vendor, BENDER_SIGNATURE); - size = disk.blocks >> 11; - if (size < 300) - sprintf(product, "PRODRIVE LPS%dS", size); - else if (size < 600) - sprintf(product, "MAVERICK%dS", size); - else if (size < 800) - sprintf(product, "LIGHTNING%dS", size); - else if (size < 1000) - sprintf(product, "TRAILBRAZER%dS", size); - else if (size < 2000) - sprintf(product, "FIREBALL%dS", size); - else - sprintf(product, "FBSE%d.%dS", size / 1000, (size % 1000) / 100); - - // Vendor name - memcpy(&buf[8], vendor, strlen(vendor)); - - // Product name - memcpy(&buf[16], product, strlen(product)); - - // Revision - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); - - // Size of data that can be returned - size = (buf[4] + 5); - - // Limit if the other buffer is small - if (size > (int)cdb[4]) { - size = (int)cdb[4]; - } - - // Success - disk.code = DISK_NOERROR; - return size; -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIHD::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) -{ - BYTE page; - int size; - - ASSERT(this); - ASSERT(buf); - ASSERT(length >= 0); - - // PF - if (cdb[1] & 0x10) { - // Mode Parameter header - if (length >= 12) { - // Check the block length bytes - size = 1 << disk.size; - if (buf[9] != (BYTE)(size >> 16) || - buf[10] != (BYTE)(size >> 8) || - buf[11] != (BYTE)size) { - // currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; - } - buf += 12; - length -= 12; - } - - // Parsing the page - while (length > 0) { - // Get page - page = buf[0]; - - switch (page) { - // format device - case 0x03: - // check the number of bytes in the physical sector - size = 1 << disk.size; - if (buf[0xc] != (BYTE)(size >> 8) || - buf[0xd] != (BYTE)size) { - // currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; - } - break; - - // CD-ROM Parameters - // According to the SONY CDU-541 manual, Page code 8 is supposed - // to set the Logical Block Adress Format, as well as the - // inactivity timer multiplier - case 0x08: - // Debug code for Issue #2: - // https://github.com/akuker/RASCSI/issues/2 - printf("[Unhandled page code] Received mode page code 8 with total length %d\n ", length); - for (int i = 0; i= (off64_t)sizeof(hdr)) { - if (!fio.Read(hdr, sizeof(hdr))) { - fio.Close(); - return FALSE; - } - } - fio.Close(); - - // 512バイト単位であること - if (size & 0x1ff) { - return FALSE; - } - - // 10MB以上 - if (size < 0x9f5400) { - return FALSE; - } - // xm6iに準じて2TB - // よく似たものが wxw/wxw_cfg.cpp にもある - if (size > 2LL * 1024 * 1024 * 1024 * 1024) { - return FALSE; - } - - // 拡張子別にパラメータを決定 - ext = path.GetFileExt(); - if (xstrcasecmp(ext, _T(".HDN")) == 0) { - // デフォルト設定としてセクタサイズ512,セクタ数25,ヘッド数8を想定 - imgoffset = 0; - imgsize = size; - sectorsize = 512; - sectors = 25; - heads = 8; - cylinders = (int)(size >> 9); - cylinders >>= 3; - cylinders /= 25; - } else if (xstrcasecmp(ext, _T(".HDI")) == 0) { // Anex86 HD image? - imgoffset = getDwordLE(&hdr[4 + 4]); - imgsize = getDwordLE(&hdr[4 + 4 + 4]); - sectorsize = getDwordLE(&hdr[4 + 4 + 4 + 4]); - sectors = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4]); - heads = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4]); - cylinders = getDwordLE(&hdr[4 + 4 + 4 + 4 + 4 + 4 + 4]); - } else if (xstrcasecmp(ext, _T(".NHD")) == 0 && - memcmp(hdr, "T98HDDIMAGE.R0\0", 15) == 0) { // T98Next HD image? - imgoffset = getDwordLE(&hdr[0x10 + 0x100]); - cylinders = getDwordLE(&hdr[0x10 + 0x100 + 4]); - heads = getWordLE(&hdr[0x10 + 0x100 + 4 + 4]); - sectors = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2]); - sectorsize = getWordLE(&hdr[0x10 + 0x100 + 4 + 4 + 2 + 2]); - imgsize = (off64_t)cylinders * heads * sectors * sectorsize; - } - - // セクタサイズは256または512をサポート - if (sectorsize != 256 && sectorsize != 512) { - return FALSE; - } - - // イメージサイズの整合性チェック - if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) { - return FALSE; - } - - // セクタサイズ - for(disk.size = 16; disk.size > 0; --(disk.size)) { - if ((1 << disk.size) == sectorsize) - break; - } - if (disk.size <= 0 || disk.size > 16) { - return FALSE; - } - - // ブロック数 - disk.blocks = (DWORD)(imgsize >> disk.size); - disk.imgoffset = imgoffset; - - // Call the base class - return Disk::Open(path); -} - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) -{ - int size; - - // 基底クラス - size = SCSIHD::Inquiry(cdb, buf, major, minor); - - // 基底クラスでエラーなら終了 - if (size == 0) { - return 0; - } - - // SCSI1相当に変更 - buf[2] = 0x01; - buf[3] = 0x01; - - // Replace Vendor name - buf[8] = 'N'; - buf[9] = 'E'; - buf[10] = 'C'; - - return size; -} - -//--------------------------------------------------------------------------- -// -// エラーページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::AddError(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x01; - buf[1] = 0x06; - - // No changeable area - if (change) { - return 8; - } - - // リトライカウントは0、リミットタイムは装置内部のデフォルト値を使用 - return 8; -} - -//--------------------------------------------------------------------------- -// -// フォーマットページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::AddFormat(BOOL change, BYTE *buf) -{ - int size; - - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x80 | 0x03; - buf[1] = 0x16; - - // 物理セクタのバイト数は変更可能に見せる(実際には変更できないが) - if (change) { - buf[0xc] = 0xff; - buf[0xd] = 0xff; - return 24; - } - - if (disk.ready) { - // 1ゾーンのトラック数を設定(PC-9801-55はこの値を見ているようだ) - buf[0x2] = (BYTE)(heads >> 8); - buf[0x3] = (BYTE)heads; - - // 1トラックのセクタ数を設定 - buf[0xa] = (BYTE)(sectors >> 8); - buf[0xb] = (BYTE)sectors; - - // 物理セクタのバイト数を設定 - size = 1 << disk.size; - buf[0xc] = (BYTE)(size >> 8); - buf[0xd] = (BYTE)size; - } - - // リムーバブル属性を設定(昔の名残) - if (disk.removable) { - buf[20] = 0x20; - } - - return 24; -} - -//--------------------------------------------------------------------------- -// -// ドライブページ追加 -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_NEC::AddDrive(BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Set the message length - buf[0] = 0x04; - buf[1] = 0x12; - - // No changeable area - if (change) { - return 20; - } - - if (disk.ready) { - // シリンダ数を設定 - buf[0x2] = (BYTE)(cylinders >> 16); - buf[0x3] = (BYTE)(cylinders >> 8); - buf[0x4] = (BYTE)cylinders; - - // ヘッド数を設定 - buf[0x5] = (BYTE)heads; - } - - return 20; -} - -//=========================================================================== -// -// SCSI hard disk (Macintosh Apple genuine) -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SCSIHD_APPLE::SCSIHD_APPLE() : SCSIHD() -{ -} - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_APPLE::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) -{ - int size; - char vendor[32]; - char product[32]; - - // Call the base class - size = SCSIHD::Inquiry(cdb, buf, major, minor); - - // End if there is an error in the base class - if (size == 0) { - return 0; - } - - // Vendor name - sprintf(vendor, " SEAGATE"); - memcpy(&buf[8], vendor, strlen(vendor)); - - // Product name - sprintf(product, " ST225N"); - memcpy(&buf[16], product, strlen(product)); - - return size; -} - -//--------------------------------------------------------------------------- -// -// Add Vendor special page -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIHD_APPLE::AddVendor(int page, BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Page code 48 - if ((page != 0x30) && (page != 0x3f)) { - return 0; - } - - // Set the message length - buf[0] = 0x30; - buf[1] = 0x1c; - - // No changeable area - if (change) { - return 30; - } - - // APPLE COMPUTER, INC. - memcpy(&buf[0xa], "APPLE COMPUTER, INC.", 20); - - return 30; -} - -//=========================================================================== -// -// SCSI magneto-optical disk -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SCSIMO::SCSIMO() : Disk() -{ - // SCSI magneto-optical disk - disk.id = MAKEID('S', 'C', 'M', 'O'); - - // Set as removable - disk.removable = TRUE; -} - -//--------------------------------------------------------------------------- -// -// Open -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIMO::Open(const Filepath& path, BOOL attn) -{ - Fileio fio; - off64_t size; - - ASSERT(this); - ASSERT(!disk.ready); - - // Open as read-only - if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; - } - - // Get file size - size = fio.GetFileSize(); - fio.Close(); - - switch (size) { - // 128MB - case 0x797f400: - disk.size = 9; - disk.blocks = 248826; - break; - - // 230MB - case 0xd9eea00: - disk.size = 9; - disk.blocks = 446325; - break; - - // 540MB - case 0x1fc8b800: - disk.size = 9; - disk.blocks = 1041500; - break; - - // 640MB - case 0x25e28000: - disk.size = 11; - disk.blocks = 310352; - break; - - // Other (this is an error) - default: - return FALSE; - } - - // Call the base class - Disk::Open(path); - - // Attention if ready - if (disk.ready && attn) { - disk.attn = TRUE; - } - - return TRUE; -} - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIMO::Load(Fileio *fio, int ver) -{ - DWORD sz; - disk_t buf; - DWORD padding; - Filepath path; - - ASSERT(this); - ASSERT(fio); - ASSERT(ver >= 0x0200); - - // Prior to version 2.03, the disk was not saved - if (ver <= 0x0202) { - return TRUE; - } - - // load size, match - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 52) { - return FALSE; - } - - // load into buffer - PROP_IMPORT(fio, buf.id); - PROP_IMPORT(fio, buf.ready); - PROP_IMPORT(fio, buf.writep); - PROP_IMPORT(fio, buf.readonly); - PROP_IMPORT(fio, buf.removable); - PROP_IMPORT(fio, buf.lock); - PROP_IMPORT(fio, buf.attn); - PROP_IMPORT(fio, buf.reset); - PROP_IMPORT(fio, buf.size); - PROP_IMPORT(fio, buf.blocks); - PROP_IMPORT(fio, buf.lun); - PROP_IMPORT(fio, buf.code); - PROP_IMPORT(fio, padding); - - // Load path - if (!path.Load(fio, ver)) { - return FALSE; - } - - // Always eject - Eject(TRUE); - - // Move only if IDs match - if (disk.id != buf.id) { - // Not MO at the time of save. Maintain eject status - return TRUE; - } - - // Re-try opening - if (!Open(path, FALSE)) { - // Cannot reopen. Maintain eject status - return TRUE; - } - - // Disk cache is created in Open. Move property only - if (!disk.readonly) { - disk.writep = buf.writep; - } - disk.lock = buf.lock; - disk.attn = buf.attn; - disk.reset = buf.reset; - disk.lun = buf.lun; - disk.code = buf.code; - - // loaded successfully - return TRUE; -} -#endif // RASCSI - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIMO::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) -{ - int size; - char rev[32]; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x12); - - // EVPD check - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return FALSE; - } - - // 基本データ - // buf[0] ... Optical Memory Device - // buf[1] ... Removable - // buf[2] ... SCSI-2 compliant command system - // buf[3] ... SCSI-2 compliant Inquiry response - // buf[4] ... Inquiry additional data - memset(buf, 0, 8); - buf[0] = 0x07; - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - - buf[1] = 0x80; - buf[2] = 0x02; - buf[3] = 0x02; - buf[4] = 36 - 5; // required - - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Vendor name - memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); - - // Product name - memcpy(&buf[16], "M2513A", 6); - - // Revision (XM6 version number) - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); - - // Size return data - size = (buf[4] + 5); - - // Limit the size if the buffer is too small - if (size > (int)cdb[4]) { - size = (int)cdb[4]; - } - - // Success - disk.code = DISK_NOERROR; - return size; -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT -// *Not affected by disk.code -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIMO::ModeSelect(const DWORD *cdb, const BYTE *buf, int length) -{ - int page; - int size; - - ASSERT(this); - ASSERT(buf); - ASSERT(length >= 0); - - // PF - if (cdb[1] & 0x10) { - // Mode Parameter header - if (length >= 12) { - // Check the block length (in bytes) - size = 1 << disk.size; - if (buf[9] != (BYTE)(size >> 16) || - buf[10] != (BYTE)(size >> 8) || buf[11] != (BYTE)size) { - // Currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; - } - buf += 12; - length -= 12; - } - - // Parsing the page - while (length > 0) { - // Get the page - page = buf[0]; - - switch (page) { - // format device - case 0x03: - // Check the number of bytes in the physical sector - size = 1 << disk.size; - if (buf[0xc] != (BYTE)(size >> 8) || - buf[0xd] != (BYTE)size) { - // Currently does not allow changing sector length - disk.code = DISK_INVALIDPRM; - return FALSE; - } - break; - // vendor unique format - case 0x20: - // just ignore, for now - break; - - // Other page - default: - break; - } - - // Advance to the next page - size = buf[1] + 2; - length -= size; - buf += size; - } - } - - // Do not generate an error for the time being (MINIX) - disk.code = DISK_NOERROR; - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Vendor Unique Format Page 20h (MO) -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIMO::AddVendor(int page, BOOL change, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - // Page code 20h - if ((page != 0x20) && (page != 0x3f)) { - return 0; - } - - // Set the message length - buf[0] = 0x20; - buf[1] = 0x0a; - - // No changeable area - if (change) { - return 12; - } - - /* - mode page code 20h - Vendor Unique Format Page - format mode XXh type 0 - information: http://h20628.www2.hp.com/km-ext/kmcsdirect/emr_na-lpg28560-1.pdf - - offset description - 02h format mode - 03h type of format (0) - 04~07h size of user band (total sectors?) - 08~09h size of spare band (spare sectors?) - 0A~0Bh number of bands - - actual value of each 3.5inches optical medium (grabbed by Fujitsu M2513EL) - - 128M 230M 540M 640M - --------------------------------------------------- - size of user band 3CBFAh 6CF75h FE45Ch 4BC50h - size of spare band 0400h 0401h 08CAh 08C4h - number of bands 0001h 000Ah 0012h 000Bh - - further information: http://r2089.blog36.fc2.com/blog-entry-177.html - */ - - if (disk.ready) { - unsigned spare = 0; - unsigned bands = 0; - - if (disk.size == 9) switch (disk.blocks) { - // 128MB - case 248826: - spare = 1024; - bands = 1; - break; - - // 230MB - case 446325: - spare = 1025; - bands = 10; - break; - - // 540MB - case 1041500: - spare = 2250; - bands = 18; - break; - } - - if (disk.size == 11) switch (disk.blocks) { - // 640MB - case 310352: - spare = 2244; - bands = 11; - break; - - // 1.3GB (lpproj: not tested with real device) - case 605846: - spare = 4437; - bands = 18; - break; - } - - buf[2] = 0; // format mode - buf[3] = 0; // type of format - buf[4] = (BYTE)(disk.blocks >> 24); - buf[5] = (BYTE)(disk.blocks >> 16); - buf[6] = (BYTE)(disk.blocks >> 8); - buf[7] = (BYTE)disk.blocks; - buf[8] = (BYTE)(spare >> 8); - buf[9] = (BYTE)spare; - buf[10] = (BYTE)(bands >> 8); - buf[11] = (BYTE)bands; - } - - return 12; -} - -//=========================================================================== -// -// CD Track -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -CDTrack::CDTrack(SCSICD *scsicd) -{ - ASSERT(scsicd); - - // Set parent CD-ROM device - cdrom = scsicd; - - // Track defaults to disabled - valid = FALSE; - - // Initialize other data - track_no = -1; - first_lba = 0; - last_lba = 0; - audio = FALSE; - raw = FALSE; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -CDTrack::~CDTrack() -{ -} - -//--------------------------------------------------------------------------- -// -// Init -// -//--------------------------------------------------------------------------- -BOOL FASTCALL CDTrack::Init(int track, DWORD first, DWORD last) -{ - ASSERT(this); - ASSERT(!valid); - ASSERT(track >= 1); - ASSERT(first < last); - - // Set and enable track number - track_no = track; - valid = TRUE; - - // Remember LBA - first_lba = first; - last_lba = last; - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Set Path -// -//--------------------------------------------------------------------------- -void FASTCALL CDTrack::SetPath(BOOL cdda, const Filepath& path) -{ - ASSERT(this); - ASSERT(valid); - - // CD-DA or data - audio = cdda; - - // Remember the path - imgpath = path; -} - -//--------------------------------------------------------------------------- -// -// Get Path -// -//--------------------------------------------------------------------------- -void FASTCALL CDTrack::GetPath(Filepath& path) const -{ - ASSERT(this); - ASSERT(valid); - - // Return the path (by reference) - path = imgpath; -} - -//--------------------------------------------------------------------------- -// -// Add Index -// -//--------------------------------------------------------------------------- -void FASTCALL CDTrack::AddIndex(int index, DWORD lba) -{ - ASSERT(this); - ASSERT(valid); - ASSERT(index > 0); - ASSERT(first_lba <= lba); - ASSERT(lba <= last_lba); - - // Currently does not support indexes - ASSERT(FALSE); -} - -//--------------------------------------------------------------------------- -// -// Gets the start of LBA -// -//--------------------------------------------------------------------------- -DWORD FASTCALL CDTrack::GetFirst() const -{ - ASSERT(this); - ASSERT(valid); - ASSERT(first_lba < last_lba); - - return first_lba; -} - -//--------------------------------------------------------------------------- -// -// Get the end of LBA -// -//--------------------------------------------------------------------------- -DWORD FASTCALL CDTrack::GetLast() const -{ - ASSERT(this); - ASSERT(valid); - ASSERT(first_lba < last_lba); - - return last_lba; -} - -//--------------------------------------------------------------------------- -// -// Get the number of blocks -// -//--------------------------------------------------------------------------- -DWORD FASTCALL CDTrack::GetBlocks() const -{ - ASSERT(this); - ASSERT(valid); - ASSERT(first_lba < last_lba); - - // Calculate from start LBA and end LBA - return (DWORD)(last_lba - first_lba + 1); -} - -//--------------------------------------------------------------------------- -// -// Get track number -// -//--------------------------------------------------------------------------- -int FASTCALL CDTrack::GetTrackNo() const -{ - ASSERT(this); - ASSERT(valid); - ASSERT(track_no >= 1); - - return track_no; -} - -//--------------------------------------------------------------------------- -// -// Is valid block -// -//--------------------------------------------------------------------------- -BOOL FASTCALL CDTrack::IsValid(DWORD lba) const -{ - ASSERT(this); - - // FALSE if the track itself is invalid - if (!valid) { - return FALSE; - } - - // If the block is BEFORE the first block - if (lba < first_lba) { - return FALSE; - } - - // If the block is AFTER the last block - if (last_lba < lba) { - return FALSE; - } - - // This track is valid - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Is audio track -// -//--------------------------------------------------------------------------- -BOOL FASTCALL CDTrack::IsAudio() const -{ - ASSERT(this); - ASSERT(valid); - - return audio; -} - -//=========================================================================== -// -// CD-DA Buffer -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -CDDABuf::CDDABuf() -{ -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -CDDABuf::~CDDABuf() -{ -} - -//=========================================================================== -// -// SCSI CD-ROM -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SCSICD::SCSICD() : Disk() -{ - int i; - - // SCSI CD-ROM - disk.id = MAKEID('S', 'C', 'C', 'D'); - - // removable, write protected - disk.removable = TRUE; - disk.writep = TRUE; - - // NOT in raw format - rawfile = FALSE; - - // Frame initialization - frame = 0; - - // Track initialization - for (i = 0; i < TrackMax; i++) { - track[i] = NULL; - } - tracks = 0; - dataindex = -1; - audioindex = -1; -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -SCSICD::~SCSICD() -{ - // Clear track - ClearTrack(); -} - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::Load(Fileio *fio, int ver) -{ - DWORD sz; - disk_t buf; - DWORD padding; - Filepath path; - - ASSERT(this); - ASSERT(fio); - ASSERT(ver >= 0x0200); - - // Prior to version 2.03, the disk was not saved - if (ver <= 0x0202) { - return TRUE; - } - - // load size, match - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 52) { - return FALSE; - } - - // load into buffer - PROP_IMPORT(fio, buf.id); - PROP_IMPORT(fio, buf.ready); - PROP_IMPORT(fio, buf.writep); - PROP_IMPORT(fio, buf.readonly); - PROP_IMPORT(fio, buf.removable); - PROP_IMPORT(fio, buf.lock); - PROP_IMPORT(fio, buf.attn); - PROP_IMPORT(fio, buf.reset); - PROP_IMPORT(fio, buf.size); - PROP_IMPORT(fio, buf.blocks); - PROP_IMPORT(fio, buf.lun); - PROP_IMPORT(fio, buf.code); - PROP_IMPORT(fio, padding); - - // Load path - if (!path.Load(fio, ver)) { - return FALSE; - } - - // Always eject - Eject(TRUE); - - // move only if IDs match - if (disk.id != buf.id) { - // It was not a CD-ROM when saving. Maintain eject status - return TRUE; - } - - // Try to reopen - if (!Open(path, FALSE)) { - // Cannot reopen. Maintain eject status - return TRUE; - } - - // Disk cache is created in Open. Move property only - if (!disk.readonly) { - disk.writep = buf.writep; - } - disk.lock = buf.lock; - disk.attn = buf.attn; - disk.reset = buf.reset; - disk.lun = buf.lun; - disk.code = buf.code; - - // Discard the disk cache again - if (disk.dcache) { - delete disk.dcache; - disk.dcache = NULL; - } - disk.dcache = NULL; - - // Tentative - disk.blocks = track[0]->GetBlocks(); - if (disk.blocks > 0) { - // Recreate the disk cache - track[0]->GetPath(path); - disk.dcache = new DiskCache(path, disk.size, disk.blocks); - disk.dcache->SetRawMode(rawfile); - - // Reset data index - dataindex = 0; - } - - return TRUE; -} -#endif // RASCSI - -//--------------------------------------------------------------------------- -// -// Open -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::Open(const Filepath& path, BOOL attn) -{ - Fileio fio; - off64_t size; - TCHAR file[5]; - - ASSERT(this); - ASSERT(!disk.ready); - - // Initialization, track clear - disk.blocks = 0; - rawfile = FALSE; - ClearTrack(); - - // Open as read-only - if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; - } - - // Close and transfer for physical CD access - if (path.GetPath()[0] == _T('\\')) { - // Close - fio.Close(); - - // Open physical CD - if (!OpenPhysical(path)) { - return FALSE; - } - } else { - // Get file size - size = fio.GetFileSize(); - if (size <= 4) { - fio.Close(); - return FALSE; - } - - // Judge whether it is a CUE sheet or an ISO file - fio.Read(file, 4); - file[4] = '\0'; - fio.Close(); - - // If it starts with FILE, consider it as a CUE sheet - if (xstrncasecmp(file, _T("FILE"), 4) == 0) { - // Open as CUE - if (!OpenCue(path)) { - return FALSE; - } - } else { - // Open as ISO - if (!OpenIso(path)) { - return FALSE; - } - } - } - - // Successful opening - ASSERT(disk.blocks > 0); - disk.size = 11; - - // Call the base class - Disk::Open(path); - - // Set RAW flag - ASSERT(disk.dcache); - disk.dcache->SetRawMode(rawfile); - - // Since it is a ROM media, writing is not possible - disk.writep = TRUE; - - // Attention if ready - if (disk.ready && attn) { - disk.attn = TRUE; - } - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Open (CUE) -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::OpenCue(const Filepath& /*path*/) -{ - ASSERT(this); - - // Always fail - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// オープン(ISO) -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::OpenIso(const Filepath& path) -{ - Fileio fio; - off64_t size; - BYTE header[12]; - BYTE sync[12]; - - ASSERT(this); - - // Open as read-only - if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; - } - - // Get file size - size = fio.GetFileSize(); - if (size < 0x800) { - fio.Close(); - return FALSE; - } - - // Read the first 12 bytes and close - if (!fio.Read(header, sizeof(header))) { - fio.Close(); - return FALSE; - } - - // Check if it is RAW format - memset(sync, 0xff, sizeof(sync)); - sync[0] = 0x00; - sync[11] = 0x00; - rawfile = FALSE; - if (memcmp(header, sync, sizeof(sync)) == 0) { - // 00,FFx10,00, so it is presumed to be RAW format - if (!fio.Read(header, 4)) { - fio.Close(); - return FALSE; - } - - // Supports MODE1/2048 or MODE1/2352 only - if (header[3] != 0x01) { - // Different mode - fio.Close(); - return FALSE; - } - - // Set to RAW file - rawfile = TRUE; - } - fio.Close(); - - if (rawfile) { - // Size must be a multiple of 2536 and less than 700MB - if (size % 0x930) { - return FALSE; - } - if (size > 912579600) { - return FALSE; - } - - // Set the number of blocks - disk.blocks = (DWORD)(size / 0x930); - } else { - // Size must be a multiple of 2048 and less than 700MB - if (size & 0x7ff) { - return FALSE; - } - if (size > 0x2bed5000) { - return FALSE; - } - - // Set the number of blocks - disk.blocks = (DWORD)(size >> 11); - } - - // Create only one data track - ASSERT(!track[0]); - track[0] = new CDTrack(this); - track[0]->Init(1, 0, disk.blocks - 1); - track[0]->SetPath(FALSE, path); - tracks = 1; - dataindex = 0; - - // Successful opening - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Open (Physical) -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::OpenPhysical(const Filepath& path) -{ - Fileio fio; - off64_t size; - - ASSERT(this); - - // Open as read-only - if (!fio.Open(path, Fileio::ReadOnly)) { - return FALSE; - } - - // Get size - size = fio.GetFileSize(); - if (size < 0x800) { - fio.Close(); - return FALSE; - } - - // Close - fio.Close(); - - // Size must be a multiple of 2048 and less than 700MB - if (size & 0x7ff) { - return FALSE; - } - if (size > 0x2bed5000) { - return FALSE; - } - - // Set the number of blocks - disk.blocks = (DWORD)(size >> 11); - - // Create only one data track - ASSERT(!track[0]); - track[0] = new CDTrack(this); - track[0]->Init(1, 0, disk.blocks - 1); - track[0]->SetPath(FALSE, path); - tracks = 1; - dataindex = 0; - - // Successful opening - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSICD::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) -{ - char rev[32]; - int size; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x12); - - // EVPD check - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return FALSE; - } - - // Basic data - // buf[0] ... CD-ROM Device - // buf[1] ... Removable - // buf[2] ... SCSI-2 compliant command system - // buf[3] ... SCSI-2 compliant Inquiry response - // buf[4] ... Inquiry additional data - memset(buf, 0, 8); - buf[0] = 0x05; - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - - buf[1] = 0x80; - buf[2] = 0x02; - buf[3] = 0x02; - buf[4] = 36 - 5; // Required - - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Vendor name - memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); - - // Product name - memcpy(&buf[16], "CD-ROM CDU-55S", 14); - - // Revision (XM6 version number) - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); -// -// The following code worked with the modified Apple CD-ROM drivers. Need to -// test with the original code to see if it works as well.... -// buf[4] = 42; // Required -// -// // Fill with blanks -// memset(&buf[8], 0x20, buf[4] - 3); -// -// // Vendor name -// memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); -// -// // Product name -// memcpy(&buf[16], "CD-ROM CDU-8003A", 16); -// -// // Revision (XM6 version number) -//// sprintf(rev, "1.9a", -// //// (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); -// memcpy(&buf[32], "1.9a", 4); -// -// //strcpy(&buf[35],"A1.9a"); -// buf[36]=0x20; -// memcpy(&buf[37],"1999/01/01",10); - - // Size of data that can be returned - size = (buf[4] + 5); - - // Limit if the other buffer is small - if (size > (int)cdb[4]) { - size = (int)cdb[4]; - } - - // Success - disk.code = DISK_NOERROR; - return size; -} - -//--------------------------------------------------------------------------- -// -// READ -// -//--------------------------------------------------------------------------- -int FASTCALL SCSICD::Read(BYTE *buf, DWORD block) -{ - int index; - Filepath path; - - ASSERT(this); - ASSERT(buf); - - // Status check - if (!CheckReady()) { - return 0; - } - - // Search for the track - index = SearchTrack(block); - - // if invalid, out of range - if (index < 0) { - disk.code = DISK_INVALIDLBA; - return 0; - } - ASSERT(track[index]); - - // If different from the current data track - if (dataindex != index) { - // Delete current disk cache (no need to save) - delete disk.dcache; - disk.dcache = NULL; - - // Reset the number of blocks - disk.blocks = track[index]->GetBlocks(); - ASSERT(disk.blocks > 0); - - // Recreate the disk cache - track[index]->GetPath(path); - disk.dcache = new DiskCache(path, disk.size, disk.blocks); - disk.dcache->SetRawMode(rawfile); - - // Reset data index - dataindex = index; - } - - // Base class - ASSERT(dataindex >= 0); - return Disk::Read(buf, block); -} - -//--------------------------------------------------------------------------- -// -// READ TOC -// -//--------------------------------------------------------------------------- -int FASTCALL SCSICD::ReadToc(const DWORD *cdb, BYTE *buf) -{ - int last; - int index; - int length; - int loop; - int i; - BOOL msf; - DWORD lba; - - ASSERT(this); - ASSERT(cdb); - ASSERT(cdb[0] == 0x43); - ASSERT(buf); - - // Check if ready - if (!CheckReady()) { - return 0; - } - - // If ready, there is at least one track - ASSERT(tracks > 0); - ASSERT(track[0]); - - // Get allocation length, clear buffer - length = cdb[7] << 8; - length |= cdb[8]; - memset(buf, 0, length); - - // Get MSF Flag - if (cdb[1] & 0x02) { - msf = TRUE; - } else { - msf = FALSE; - } - - // Get and check the last track number - last = track[tracks - 1]->GetTrackNo(); - if ((int)cdb[6] > last) { - // Except for AA - if (cdb[6] != 0xaa) { - disk.code = DISK_INVALIDCDB; - return 0; - } - } - - // Check start index - index = 0; - if (cdb[6] != 0x00) { - // Advance the track until the track numbers match - while (track[index]) { - if ((int)cdb[6] == track[index]->GetTrackNo()) { - break; - } - index++; - } - - // AA if not found or internal error - if (!track[index]) { - if (cdb[6] == 0xaa) { - // Returns the final LBA+1 because it is AA - buf[0] = 0x00; - buf[1] = 0x0a; - buf[2] = (BYTE)track[0]->GetTrackNo(); - buf[3] = (BYTE)last; - buf[6] = 0xaa; - lba = track[tracks - 1]->GetLast() + 1; - if (msf) { - LBAtoMSF(lba, &buf[8]); - } else { - buf[10] = (BYTE)(lba >> 8); - buf[11] = (BYTE)lba; - } - return length; - } - - // Otherwise, error - disk.code = DISK_INVALIDCDB; - return 0; - } - } - - // Number of track descriptors returned this time (number of loops) - loop = last - track[index]->GetTrackNo() + 1; - ASSERT(loop >= 1); - - // Create header - buf[0] = (BYTE)(((loop << 3) + 2) >> 8); - buf[1] = (BYTE)((loop << 3) + 2); - buf[2] = (BYTE)track[0]->GetTrackNo(); - buf[3] = (BYTE)last; - buf += 4; - - // Loop.... - for (i = 0; i < loop; i++) { - // ADR and Control - if (track[index]->IsAudio()) { - // audio track - buf[1] = 0x10; - } else { - // data track - buf[1] = 0x14; - } - - // track number - buf[2] = (BYTE)track[index]->GetTrackNo(); - - // track address - if (msf) { - LBAtoMSF(track[index]->GetFirst(), &buf[4]); - } else { - buf[6] = (BYTE)(track[index]->GetFirst() >> 8); - buf[7] = (BYTE)(track[index]->GetFirst()); - } - - // Advance buffer pointer and index - buf += 8; - index++; - } - - // Always return only the allocation length - return length; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::PlayAudio(const DWORD* /*cdb*/) -{ - ASSERT(this); - - disk.code = DISK_INVALIDCDB; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO MSF -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::PlayAudioMSF(const DWORD* /*cdb*/) -{ - ASSERT(this); - - disk.code = DISK_INVALIDCDB; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO TRACK -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::PlayAudioTrack(const DWORD* /*cdb*/) -{ - ASSERT(this); - - disk.code = DISK_INVALIDCDB; - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// LBA→MSF Conversion -// -//--------------------------------------------------------------------------- -void FASTCALL SCSICD::LBAtoMSF(DWORD lba, BYTE *msf) const -{ - DWORD m; - DWORD s; - DWORD f; - - ASSERT(this); - - // 75 and 75*60 get the remainder - m = lba / (75 * 60); - s = lba % (75 * 60); - f = s % 75; - s /= 75; - - // The base point is M=0, S=2, F=0 - s += 2; - if (s >= 60) { - s -= 60; - m++; - } - - // Store - ASSERT(m < 0x100); - ASSERT(s < 60); - ASSERT(f < 75); - msf[0] = 0x00; - msf[1] = (BYTE)m; - msf[2] = (BYTE)s; - msf[3] = (BYTE)f; -} - -//--------------------------------------------------------------------------- -// -// MSF→LBA Conversion -// -//--------------------------------------------------------------------------- -DWORD FASTCALL SCSICD::MSFtoLBA(const BYTE *msf) const -{ - DWORD lba; - - ASSERT(this); - ASSERT(msf[2] < 60); - ASSERT(msf[3] < 75); - - // 1, 75, add up in multiples of 75*60 - lba = msf[1]; - lba *= 60; - lba += msf[2]; - lba *= 75; - lba += msf[3]; - - // Since the base point is M=0, S=2, F=0, subtract 150 - lba -= 150; - - return lba; -} - -//--------------------------------------------------------------------------- -// -// Clear Track -// -//--------------------------------------------------------------------------- -void FASTCALL SCSICD::ClearTrack() -{ - int i; - - ASSERT(this); - - // delete the track object - for (i = 0; i < TrackMax; i++) { - if (track[i]) { - delete track[i]; - track[i] = NULL; - } - } - - // Number of tracks is 0 - tracks = 0; - - // No settings for data and audio - dataindex = -1; - audioindex = -1; -} - -//--------------------------------------------------------------------------- -// -// Track Search -// * Returns -1 if not found -// -//--------------------------------------------------------------------------- -int FASTCALL SCSICD::SearchTrack(DWORD lba) const -{ - int i; - - ASSERT(this); - - // Track loop - for (i = 0; i < tracks; i++) { - // Listen to the track - ASSERT(track[i]); - if (track[i]->IsValid(lba)) { - return i; - } - } - - // Track wasn't found - return -1; -} - -//--------------------------------------------------------------------------- -// -// Next Frame -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSICD::NextFrame() -{ - ASSERT(this); - ASSERT((frame >= 0) && (frame < 75)); - - // set the frame in the range 0-74 - frame = (frame + 1) % 75; - - // FALSE after one lap - if (frame != 0) { - return TRUE; - } else { - return FALSE; - } -} - -//--------------------------------------------------------------------------- -// -// Get CD-DA buffer -// -//--------------------------------------------------------------------------- -void FASTCALL SCSICD::GetBuf( - DWORD* /*buffer*/, int /*samples*/, DWORD /*rate*/) -{ - ASSERT(this); -} - -//=========================================================================== -// -// SCSI Host Bridge -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -SCSIBR::SCSIBR() : Disk() -{ - // Host Bridge - disk.id = MAKEID('S', 'C', 'B', 'R'); - -#if defined(RASCSI) && !defined(BAREMETAL) - // TAP Driver Generation - tap = new CTapDriver(); - m_bTapEnable = tap->Init(); - - // Generate MAC Address - memset(mac_addr, 0x00, 6); - if (m_bTapEnable) { - tap->GetMacAddr(mac_addr); - mac_addr[5]++; - } - - // Packet reception flag OFF - packet_enable = FALSE; -#endif // RASCSI && !BAREMETAL - - // Create host file system - fs = new CFileSys(); - fs->Reset(); -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -SCSIBR::~SCSIBR() -{ -#if defined(RASCSI) && !defined(BAREMETAL) - // TAP driver release - if (tap) { - tap->Cleanup(); - delete tap; - } -#endif // RASCSI && !BAREMETAL - - // Release host file system - if (fs) { - fs->Reset(); - delete fs; - } -} - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIBR::Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor) -{ - char rev[32]; - int size; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - ASSERT(cdb[0] == 0x12); - - // EVPD check - if (cdb[1] & 0x01) { - disk.code = DISK_INVALIDCDB; - return FALSE; - } - - // Basic data - // buf[0] ... Communication Device - // buf[2] ... SCSI-2 compliant command system - // buf[3] ... SCSI-2 compliant Inquiry response - // buf[4] ... Inquiry additional data - memset(buf, 0, 8); - buf[0] = 0x09; - - // SCSI-2 p.104 4.4.3 Incorrect logical unit handling - if (((cdb[1] >> 5) & 0x07) != disk.lun) { - buf[0] = 0x7f; - } - - buf[2] = 0x02; - buf[3] = 0x02; - buf[4] = 36 - 5 + 8; // required + 8 byte extension - - // Fill with blanks - memset(&buf[8], 0x20, buf[4] - 3); - - // Vendor name - memcpy(&buf[8], BENDER_SIGNATURE, strlen(BENDER_SIGNATURE)); - - // Product name - memcpy(&buf[16], "RASCSI BRIDGE", 13); - - // Revision (XM6 version number) - sprintf(rev, "0%01d%01d%01d", - (int)major, (int)(minor >> 4), (int)(minor & 0x0f)); - memcpy(&buf[32], rev, 4); - - // Optional function valid flag - buf[36] = '0'; - -#if defined(RASCSI) && !defined(BAREMETAL) - // TAP Enable - if (m_bTapEnable) { - buf[37] = '1'; - } -#endif // RASCSI && !BAREMETAL - - // CFileSys Enable - buf[38] = '1'; - - // Size of data that can be returned - size = (buf[4] + 5); - - // Limit if the other buffer is small - if (size > (int)cdb[4]) { - size = (int)cdb[4]; - } - - // Success - disk.code = DISK_NOERROR; - return size; -} - -//--------------------------------------------------------------------------- -// -// TEST UNIT READY -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIBR::TestUnitReady(const DWORD* /*cdb*/) -{ - ASSERT(this); - - // TEST UNIT READY Success - disk.code = DISK_NOERROR; - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// GET MESSAGE(10) -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIBR::GetMessage10(const DWORD *cdb, BYTE *buf) -{ - int type; - int phase; -#if defined(RASCSI) && !defined(BAREMETAL) - int func; - int total_len; - int i; -#endif // RASCSI && !BAREMETAL - - ASSERT(this); - - // Type - type = cdb[2]; - -#if defined(RASCSI) && !defined(BAREMETAL) - // Function number - func = cdb[3]; -#endif // RASCSI && !BAREMETAL - - // Phase - phase = cdb[9]; - - switch (type) { -#if defined(RASCSI) && !defined(BAREMETAL) - case 1: // Ethernet - // Do not process if TAP is invalid - if (!m_bTapEnable) { - return 0; - } - - switch (func) { - case 0: // Get MAC address - return GetMacAddr(buf); - - case 1: // Received packet acquisition (size/buffer) - if (phase == 0) { - // Get packet size - ReceivePacket(); - buf[0] = (BYTE)(packet_len >> 8); - buf[1] = (BYTE)packet_len; - return 2; - } else { - // Get package data - GetPacketBuf(buf); - return packet_len; - } - - case 2: // Received packet acquisition (size + buffer simultaneously) - ReceivePacket(); - buf[0] = (BYTE)(packet_len >> 8); - buf[1] = (BYTE)packet_len; - GetPacketBuf(&buf[2]); - return packet_len + 2; - - case 3: // Simultaneous acquisition of multiple packets (size + buffer simultaneously) - // Currently the maximum number of packets is 10 - // Isn't it too fast if I increase more? - total_len = 0; - for (i = 0; i < 10; i++) { - ReceivePacket(); - *buf++ = (BYTE)(packet_len >> 8); - *buf++ = (BYTE)packet_len; - total_len += 2; - if (packet_len == 0) - break; - GetPacketBuf(buf); - buf += packet_len; - total_len += packet_len; - } - return total_len; - } - break; -#endif // RASCSI && !BAREMETAL - - case 2: // Host Drive - switch (phase) { - case 0: // Get result code - return ReadFsResult(buf); - - case 1: // Return data acquisition - return ReadFsOut(buf); - - case 2: // Return additional data acquisition - return ReadFsOpt(buf); - } - break; - } - - // Error - ASSERT(FALSE); - return 0; -} - -//--------------------------------------------------------------------------- -// -// SEND MESSAGE(10) -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIBR::SendMessage10(const DWORD *cdb, BYTE *buf) -{ - int type; - int func; - int phase; - int len; - - ASSERT(this); - ASSERT(cdb); - ASSERT(buf); - - // Type - type = cdb[2]; - - // Function number - func = cdb[3]; - - // Phase - phase = cdb[9]; - - // Get the number of lights - len = cdb[6]; - len <<= 8; - len |= cdb[7]; - len <<= 8; - len |= cdb[8]; - - switch (type) { -#if defined(RASCSI) && !defined(BAREMETAL) - case 1: // Ethernet - // Do not process if TAP is invalid - if (!m_bTapEnable) { - return FALSE; - } - - switch (func) { - case 0: // MAC address setting - SetMacAddr(buf); - return TRUE; - - case 1: // Send packet - SendPacket(buf, len); - return TRUE; - } - break; -#endif // RASCSI && !BAREMETAL - - case 2: // Host drive - switch (phase) { - case 0: // issue command - WriteFs(func, buf); - return TRUE; - - case 1: // additional data writing - WriteFsOpt(buf, len); - return TRUE; - } - break; - } - - // Error - ASSERT(FALSE); - return FALSE; -} - -#if defined(RASCSI) && !defined(BAREMETAL) -//--------------------------------------------------------------------------- -// -// Get MAC Address -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIBR::GetMacAddr(BYTE *mac) -{ - ASSERT(this); - ASSERT(mac); - - memcpy(mac, mac_addr, 6); - return 6; -} - -//--------------------------------------------------------------------------- -// -// Set MAC Address -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::SetMacAddr(BYTE *mac) -{ - ASSERT(this); - ASSERT(mac); - - memcpy(mac_addr, mac, 6); -} - -//--------------------------------------------------------------------------- -// -// Receive Packet -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::ReceivePacket() -{ - static const BYTE bcast_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - - ASSERT(this); - ASSERT(tap); - - // previous packet has not been received - if (packet_enable) { - return; - } - - // Receive packet - packet_len = tap->Rx(packet_buf); - - // Check if received packet - if (memcmp(packet_buf, mac_addr, 6) != 0) { - if (memcmp(packet_buf, bcast_addr, 6) != 0) { - packet_len = 0; - return; - } - } - - // Discard if it exceeds the buffer size - if (packet_len > 2048) { - packet_len = 0; - return; - } - - // Store in receive buffer - if (packet_len > 0) { - packet_enable = TRUE; - } -} - -//--------------------------------------------------------------------------- -// -// Get Packet -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::GetPacketBuf(BYTE *buf) -{ - int len; - - ASSERT(this); - ASSERT(tap); - ASSERT(buf); - - // Size limit - len = packet_len; - if (len > 2048) { - len = 2048; - } - - // Copy - memcpy(buf, packet_buf, len); - - // Received - packet_enable = FALSE; -} - -//--------------------------------------------------------------------------- -// -// Send Packet -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::SendPacket(BYTE *buf, int len) -{ - ASSERT(this); - ASSERT(tap); - ASSERT(buf); - - tap->Tx(buf, len); -} -#endif // RASCSI && !BAREMETAL - -//--------------------------------------------------------------------------- -// -// $40 - Device Boot -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_InitDevice(BYTE *buf) -{ - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - fs->Reset(); - fsresult = fs->InitDevice((Human68k::argument_t*)buf); -} - -//--------------------------------------------------------------------------- -// -// $41 - Directory Check -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_CheckDir(BYTE *buf) -{ - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - fsresult = fs->CheckDir(nUnit, pNamests); -} - -//--------------------------------------------------------------------------- -// -// $42 - Create Directory -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_MakeDir(BYTE *buf) -{ - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - fsresult = fs->MakeDir(nUnit, pNamests); -} - -//--------------------------------------------------------------------------- -// -// $43 - Remove Directory -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_RemoveDir(BYTE *buf) -{ - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - fsresult = fs->RemoveDir(nUnit, pNamests); -} - -//--------------------------------------------------------------------------- -// -// $44 - Rename -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Rename(BYTE *buf) -{ - DWORD nUnit; - Human68k::namests_t *pNamests; - Human68k::namests_t* pNamestsNew; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - pNamestsNew = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - fsresult = fs->Rename(nUnit, pNamests, pNamestsNew); -} - -//--------------------------------------------------------------------------- -// -// $45 - Delete File -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Delete(BYTE *buf) -{ - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - fsresult = fs->Delete(nUnit, pNamests); -} - -//--------------------------------------------------------------------------- -// -// $46 - Get / Set file attributes -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Attribute(BYTE *buf) -{ - DWORD nUnit; - Human68k::namests_t *pNamests; - DWORD nHumanAttribute; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - dp = (DWORD*)&buf[i]; - nHumanAttribute = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->Attribute(nUnit, pNamests, nHumanAttribute); -} - -//--------------------------------------------------------------------------- -// -// $47 - File Search -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Files(BYTE *buf) -{ - DWORD nUnit; - DWORD nKey; - Human68k::namests_t *pNamests; - Human68k::files_t *files; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - files = (Human68k::files_t*)&buf[i]; - i += sizeof(Human68k::files_t); - - files->sector = ntohl(files->sector); - files->offset = ntohs(files->offset); - files->time = ntohs(files->time); - files->date = ntohs(files->date); - files->size = ntohl(files->size); - - fsresult = fs->Files(nUnit, nKey, pNamests, files); - - files->sector = htonl(files->sector); - files->offset = htons(files->offset); - files->time = htons(files->time); - files->date = htons(files->date); - files->size = htonl(files->size); - - i = 0; - memcpy(&fsout[i], files, sizeof(Human68k::files_t)); - i += sizeof(Human68k::files_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $48 - File next search -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_NFiles(BYTE *buf) -{ - DWORD nUnit; - DWORD nKey; - Human68k::files_t *files; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - files = (Human68k::files_t*)&buf[i]; - i += sizeof(Human68k::files_t); - - files->sector = ntohl(files->sector); - files->offset = ntohs(files->offset); - files->time = ntohs(files->time); - files->date = ntohs(files->date); - files->size = ntohl(files->size); - - fsresult = fs->NFiles(nUnit, nKey, files); - - files->sector = htonl(files->sector); - files->offset = htons(files->offset); - files->time = htons(files->time); - files->date = htons(files->date); - files->size = htonl(files->size); - - i = 0; - memcpy(&fsout[i], files, sizeof(Human68k::files_t)); - i += sizeof(Human68k::files_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $49 - File Creation -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Create(BYTE *buf) -{ - DWORD nUnit; - DWORD nKey; - Human68k::namests_t *pNamests; - Human68k::fcb_t *pFcb; - DWORD nAttribute; - BOOL bForce; - DWORD *dp; - BOOL *bp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - dp = (DWORD*)&buf[i]; - nAttribute = ntohl(*dp); - i += sizeof(DWORD); - - bp = (BOOL*)&buf[i]; - bForce = ntohl(*bp); - i += sizeof(BOOL); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->Create(nUnit, nKey, pNamests, pFcb, nAttribute, bForce); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $4A - Open File -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Open(BYTE *buf) -{ - DWORD nUnit; - DWORD nKey; - Human68k::namests_t *pNamests; - Human68k::fcb_t *pFcb; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pNamests = (Human68k::namests_t*)&buf[i]; - i += sizeof(Human68k::namests_t); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->Open(nUnit, nKey, pNamests, pFcb); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $4B - Close File -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Close(BYTE *buf) -{ - DWORD nUnit; - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->Close(nUnit, nKey, pFcb); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $4C - Read File -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Read(BYTE *buf) -{ - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nSize; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - dp = (DWORD*)&buf[i]; - nSize = ntohl(*dp); - i += sizeof(DWORD); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->Read(nKey, pFcb, fsopt, nSize); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; - - fsoptlen = fsresult; -} - -//--------------------------------------------------------------------------- -// -// $4D - Write file -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Write(BYTE *buf) -{ - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nSize; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - dp = (DWORD*)&buf[i]; - nSize = ntohl(*dp); - i += sizeof(DWORD); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->Write(nKey, pFcb, fsopt, nSize); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $4E - Seek file -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Seek(BYTE *buf) -{ - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nMode; - int nOffset; - DWORD *dp; - int *ip; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - dp = (DWORD*)&buf[i]; - nMode = ntohl(*dp); - i += sizeof(DWORD); - - ip = (int*)&buf[i]; - nOffset = ntohl(*ip); - i += sizeof(int); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->Seek(nKey, pFcb, nMode, nOffset); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $4F - File Timestamp Get / Set -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_TimeStamp(BYTE *buf) -{ - DWORD nUnit; - DWORD nKey; - Human68k::fcb_t *pFcb; - DWORD nHumanTime; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nKey = ntohl(*dp); - i += sizeof(DWORD); - - pFcb = (Human68k::fcb_t*)&buf[i]; - i += sizeof(Human68k::fcb_t); - - dp = (DWORD*)&buf[i]; - nHumanTime = ntohl(*dp); - i += sizeof(DWORD); - - pFcb->fileptr = ntohl(pFcb->fileptr); - pFcb->mode = ntohs(pFcb->mode); - pFcb->time = ntohs(pFcb->time); - pFcb->date = ntohs(pFcb->date); - pFcb->size = ntohl(pFcb->size); - - fsresult = fs->TimeStamp(nUnit, nKey, pFcb, nHumanTime); - - pFcb->fileptr = htonl(pFcb->fileptr); - pFcb->mode = htons(pFcb->mode); - pFcb->time = htons(pFcb->time); - pFcb->date = htons(pFcb->date); - pFcb->size = htonl(pFcb->size); - - i = 0; - memcpy(&fsout[i], pFcb, sizeof(Human68k::fcb_t)); - i += sizeof(Human68k::fcb_t); - - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $50 - Get Capacity -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_GetCapacity(BYTE *buf) -{ - DWORD nUnit; - Human68k::capacity_t cap; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->GetCapacity(nUnit, &cap); - - cap.freearea = htons(cap.freearea); - cap.clusters = htons(cap.clusters); - cap.sectors = htons(cap.sectors); - cap.bytes = htons(cap.bytes); - - memcpy(fsout, &cap, sizeof(Human68k::capacity_t)); - fsoutlen = sizeof(Human68k::capacity_t); -} - -//--------------------------------------------------------------------------- -// -// $51 - Drive status inspection/control -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_CtrlDrive(BYTE *buf) -{ - DWORD nUnit; - Human68k::ctrldrive_t *pCtrlDrive; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - pCtrlDrive = (Human68k::ctrldrive_t*)&buf[i]; - i += sizeof(Human68k::ctrldrive_t); - - fsresult = fs->CtrlDrive(nUnit, pCtrlDrive); - - memcpy(fsout, pCtrlDrive, sizeof(Human68k::ctrldrive_t)); - fsoutlen = sizeof(Human68k::ctrldrive_t); -} - -//--------------------------------------------------------------------------- -// -// $52 - Get DPB -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_GetDPB(BYTE *buf) -{ - DWORD nUnit; - Human68k::dpb_t dpb; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->GetDPB(nUnit, &dpb); - - dpb.sector_size = htons(dpb.sector_size); - dpb.fat_sector = htons(dpb.fat_sector); - dpb.file_max = htons(dpb.file_max); - dpb.data_sector = htons(dpb.data_sector); - dpb.cluster_max = htons(dpb.cluster_max); - dpb.root_sector = htons(dpb.root_sector); - - memcpy(fsout, &dpb, sizeof(Human68k::dpb_t)); - fsoutlen = sizeof(Human68k::dpb_t); -} - -//--------------------------------------------------------------------------- -// -// $53 - Read Sector -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_DiskRead(BYTE *buf) -{ - DWORD nUnit; - DWORD nSector; - DWORD nSize; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nSector = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nSize = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->DiskRead(nUnit, fsout, nSector, nSize); - fsoutlen = 0x200; -} - -//--------------------------------------------------------------------------- -// -// $54 - Write Sector -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_DiskWrite(BYTE *buf) -{ - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->DiskWrite(nUnit); -} - -//--------------------------------------------------------------------------- -// -// $55 - IOCTRL -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Ioctrl(BYTE *buf) -{ - DWORD nUnit; - DWORD nFunction; - Human68k::ioctrl_t *pIoctrl; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - dp = (DWORD*)&buf[i]; - nFunction = ntohl(*dp); - i += sizeof(DWORD); - - pIoctrl = (Human68k::ioctrl_t*)&buf[i]; - i += sizeof(Human68k::ioctrl_t); - - switch (nFunction) { - case 2: - case -2: - pIoctrl->param = htonl(pIoctrl->param); - break; - } - - fsresult = fs->Ioctrl(nUnit, nFunction, pIoctrl); - - switch (nFunction) { - case 0: - pIoctrl->media = htons(pIoctrl->media); - break; - case 1: - case -3: - pIoctrl->param = htonl(pIoctrl->param); - break; - } - - i = 0; - memcpy(&fsout[i], pIoctrl, sizeof(Human68k::ioctrl_t)); - i += sizeof(Human68k::ioctrl_t); - fsoutlen = i; -} - -//--------------------------------------------------------------------------- -// -// $56 - Flush -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Flush(BYTE *buf) -{ - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->Flush(nUnit); -} - -//--------------------------------------------------------------------------- -// -// $57 - Check Media -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_CheckMedia(BYTE *buf) -{ - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->CheckMedia(nUnit); -} - -//--------------------------------------------------------------------------- -// -// $58 - Lock -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::FS_Lock(BYTE *buf) -{ - DWORD nUnit; - DWORD *dp; - int i; - - ASSERT(this); - ASSERT(fs); - ASSERT(buf); - - i = 0; - dp = (DWORD*)buf; - nUnit = ntohl(*dp); - i += sizeof(DWORD); - - fsresult = fs->Lock(nUnit); -} - -//--------------------------------------------------------------------------- -// -// Read Filesystem (result code) -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIBR::ReadFsResult(BYTE *buf) -{ - DWORD *dp; - - ASSERT(this); - ASSERT(buf); - - dp = (DWORD*)buf; - *dp = htonl(fsresult); - return sizeof(DWORD); -} - -//--------------------------------------------------------------------------- -// -// Read Filesystem (return data) -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIBR::ReadFsOut(BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - memcpy(buf, fsout, fsoutlen); - return fsoutlen; -} - -//--------------------------------------------------------------------------- -// -// Read file system (return option data) -// -//--------------------------------------------------------------------------- -int FASTCALL SCSIBR::ReadFsOpt(BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - memcpy(buf, fsopt, fsoptlen); - return fsoptlen; -} - -//--------------------------------------------------------------------------- -// -// Write Filesystem -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::WriteFs(int func, BYTE *buf) -{ - ASSERT(this); - ASSERT(buf); - - fsresult = FS_FATAL_INVALIDCOMMAND; - fsoutlen = 0; - fsoptlen = 0; - - // コマンド分岐 - func &= 0x1f; - switch (func) { - case 0x00: return FS_InitDevice(buf); // $40 - start device - case 0x01: return FS_CheckDir(buf); // $41 - directory check - case 0x02: return FS_MakeDir(buf); // $42 - create directory - case 0x03: return FS_RemoveDir(buf); // $43 - remove directory - case 0x04: return FS_Rename(buf); // $44 - change file name - case 0x05: return FS_Delete(buf); // $45 - delete file - case 0x06: return FS_Attribute(buf); // $46 - Get/set file attribute - case 0x07: return FS_Files(buf); // $47 - file search - case 0x08: return FS_NFiles(buf); // $48 - next file search - case 0x09: return FS_Create(buf); // $49 - create file - case 0x0A: return FS_Open(buf); // $4A - File open - case 0x0B: return FS_Close(buf); // $4B - File close - case 0x0C: return FS_Read(buf); // $4C - read file - case 0x0D: return FS_Write(buf); // $4D - write file - case 0x0E: return FS_Seek(buf); // $4E - File seek - case 0x0F: return FS_TimeStamp(buf); // $4F - Get/set file modification time - case 0x10: return FS_GetCapacity(buf); // $50 - get capacity - case 0x11: return FS_CtrlDrive(buf); // $51 - Drive control/state check - case 0x12: return FS_GetDPB(buf); // $52 - Get DPB - case 0x13: return FS_DiskRead(buf); // $53 - read sector - case 0x14: return FS_DiskWrite(buf); // $54 - write sector - case 0x15: return FS_Ioctrl(buf); // $55 - IOCTRL - case 0x16: return FS_Flush(buf); // $56 - flush - case 0x17: return FS_CheckMedia(buf); // $57 - check media exchange - case 0x18: return FS_Lock(buf); // $58 - exclusive control - } -} - -//--------------------------------------------------------------------------- -// -// File system write (input option data) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIBR::WriteFsOpt(BYTE *buf, int num) -{ - ASSERT(this); - - memcpy(fsopt, buf, num); -} - -//=========================================================================== -// -// SASI Device -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -#ifdef RASCSI -SASIDEV::SASIDEV() -#else -SASIDEV::SASIDEV(Device *dev) -#endif // RASCSI -{ - int i; - -#ifndef RASCSI - // Remember host device - host = dev; -#endif // RASCSI - - // Work initialization - ctrl.phase = BUS::busfree; - ctrl.id = -1; - ctrl.bus = NULL; - memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd)); - ctrl.status = 0x00; - ctrl.message = 0x00; -#ifdef RASCSI - ctrl.execstart = 0; -#endif // RASCSI - ctrl.bufsize = 0x800; - ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); - memset(ctrl.buffer, 0x00, ctrl.bufsize); - ctrl.blocks = 0; - ctrl.next = 0; - ctrl.offset = 0; - ctrl.length = 0; - - // Logical unit initialization - for (i = 0; i < UnitMax; i++) { - ctrl.unit[i] = NULL; - } -} - -//--------------------------------------------------------------------------- -// -// Destructor -// -//--------------------------------------------------------------------------- -SASIDEV::~SASIDEV() -{ - // Free the buffer - if (ctrl.buffer) { - free(ctrl.buffer); - ctrl.buffer = NULL; - } -} - -//--------------------------------------------------------------------------- -// -// Device reset -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Reset() -{ - int i; - - ASSERT(this); - - // Work initialization - memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd)); - ctrl.phase = BUS::busfree; - ctrl.status = 0x00; - ctrl.message = 0x00; -#ifdef RASCSI - ctrl.execstart = 0; -#endif // RASCSI - memset(ctrl.buffer, 0x00, ctrl.bufsize); - ctrl.blocks = 0; - ctrl.next = 0; - ctrl.offset = 0; - ctrl.length = 0; - - // Unit initialization - for (i = 0; i < UnitMax; i++) { - if (ctrl.unit[i]) { - ctrl.unit[i]->Reset(); - } - } -} - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Save -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::Save(Fileio *fio, int /*ver*/) -{ - DWORD sz; - - ASSERT(this); - ASSERT(fio); - - // Save size - sz = 2120; - if (!fio->Write(&sz, sizeof(sz))) { - return FALSE; - } - - // Save entity - PROP_EXPORT(fio, ctrl.phase); - PROP_EXPORT(fio, ctrl.id); - PROP_EXPORT(fio, ctrl.cmd); - PROP_EXPORT(fio, ctrl.status); - PROP_EXPORT(fio, ctrl.message); - if (!fio->Write(ctrl.buffer, 0x800)) { - return FALSE; - } - PROP_EXPORT(fio, ctrl.blocks); - PROP_EXPORT(fio, ctrl.next); - PROP_EXPORT(fio, ctrl.offset); - PROP_EXPORT(fio, ctrl.length); - - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Load -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::Load(Fileio *fio, int ver) -{ - DWORD sz; - - ASSERT(this); - ASSERT(fio); - - // Not saved before version 3.11 - if (ver <= 0x0311) { - return TRUE; - } - - // Load size and check if the size matches - if (!fio->Read(&sz, sizeof(sz))) { - return FALSE; - } - if (sz != 2120) { - return FALSE; - } - - // Load the entity - PROP_IMPORT(fio, ctrl.phase); - PROP_IMPORT(fio, ctrl.id); - PROP_IMPORT(fio, ctrl.cmd); - PROP_IMPORT(fio, ctrl.status); - PROP_IMPORT(fio, ctrl.message); - if (!fio->Read(ctrl.buffer, 0x800)) { - return FALSE; - } - PROP_IMPORT(fio, ctrl.blocks); - PROP_IMPORT(fio, ctrl.next); - PROP_IMPORT(fio, ctrl.offset); - PROP_IMPORT(fio, ctrl.length); - - return TRUE; -} -#endif // RASCSI - -//--------------------------------------------------------------------------- -// -// Connect the controller -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Connect(int id, BUS *bus) -{ - ASSERT(this); - - ctrl.id = id; - ctrl.bus = bus; -} - -//--------------------------------------------------------------------------- -// -// Get the logical unit -// -//--------------------------------------------------------------------------- -Disk* FASTCALL SASIDEV::GetUnit(int no) -{ - ASSERT(this); - ASSERT(no < UnitMax); - - return ctrl.unit[no]; -} - -//--------------------------------------------------------------------------- -// -// Set the logical unit -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::SetUnit(int no, Disk *dev) -{ - ASSERT(this); - ASSERT(no < UnitMax); - - ctrl.unit[no] = dev; -} - -//--------------------------------------------------------------------------- -// -// Check to see if this has a valid logical unit -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::HasUnit() -{ - int i; - - ASSERT(this); - - for (i = 0; i < UnitMax; i++) { - if (ctrl.unit[i]) { - return TRUE; - } - } - - return FALSE; -} - -//--------------------------------------------------------------------------- -// -// Get internal data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::GetCTRL(ctrl_t *buffer) -{ - ASSERT(this); - ASSERT(buffer); - - // reference the internal structure - *buffer = ctrl; -} - -//--------------------------------------------------------------------------- -// -// Get a busy unit -// -//--------------------------------------------------------------------------- -Disk* FASTCALL SASIDEV::GetBusyUnit() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - return ctrl.unit[lun]; -} - -//--------------------------------------------------------------------------- -// -// Run -// -//--------------------------------------------------------------------------- -BUS::phase_t FASTCALL SASIDEV::Process() -{ - ASSERT(this); - - // Do nothing if not connected - if (ctrl.id < 0 || ctrl.bus == NULL) { - return ctrl.phase; - } - - // Get bus information - ctrl.bus->Aquire(); - - // For the monitor tool, we shouldn't need to reset. We're just logging information - // Reset - if (ctrl.bus->GetRST()) { -#if defined(DISK_LOG) - Log(Log::Normal, "RESET signal received"); -#endif // DISK_LOG - - // Reset the controller - Reset(); - - // Reset the bus - ctrl.bus->Reset(); - return ctrl.phase; - } - - // Phase processing - switch (ctrl.phase) { - // Bus free - case BUS::busfree: - BusFree(); - break; - - // Selection - case BUS::selection: - Selection(); - break; - - // Data out (MCI=000) - case BUS::dataout: - DataOut(); - break; - - // Data in (MCI=001) - case BUS::datain: - DataIn(); - break; - - // Command (MCI=010) - case BUS::command: - Command(); - break; - - // Status (MCI=011) - case BUS::status: - Status(); - break; - - // Msg in (MCI=111) - case BUS::msgin: - MsgIn(); - break; - - // Other - default: - ASSERT(FALSE); - break; - } - - return ctrl.phase; -} - -//--------------------------------------------------------------------------- -// -// Bus free phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::BusFree() -{ - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::busfree) { - -#if defined(DISK_LOG) - Log(Log::Normal, "Bus free phase"); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::busfree; - - // Set Signal lines - ctrl.bus->SetREQ(FALSE); - ctrl.bus->SetMSG(FALSE); - ctrl.bus->SetCD(FALSE); - ctrl.bus->SetIO(FALSE); - ctrl.bus->SetBSY(FALSE); - - // Initialize status and message - ctrl.status = 0x00; - ctrl.message = 0x00; - return; - } - - // Move to selection phase - if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) { - Selection(); - } -} - -//--------------------------------------------------------------------------- -// -// Selection phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Selection() -{ - DWORD id; - - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::selection) { - // Invalid if IDs do not match - id = 1 << ctrl.id; - if ((ctrl.bus->GetDAT() & id) == 0) { - return; - } - - // Return if there is no unit - if (!HasUnit()) { - return; - } - -#if defined(DISK_LOG) - Log(Log::Normal, - "Selection Phase ID=%d (with device)", ctrl.id); -#endif // DISK_LOG - - // Phase change - ctrl.phase = BUS::selection; - - // Raiase BSY and respond - ctrl.bus->SetBSY(TRUE); - return; - } - - // Command phase shifts when selection is completed - if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) { - Command(); - } -} - -//--------------------------------------------------------------------------- -// -// Command phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Command() -{ -#ifdef RASCSI - int count; - int i; -#endif // RASCSI - - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::command) { - -#if defined(DISK_LOG) - Log(Log::Normal, "Command Phase"); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::command; - - // Signal line operated by the target - ctrl.bus->SetMSG(FALSE); - ctrl.bus->SetCD(TRUE); - ctrl.bus->SetIO(FALSE); - - // Data transfer is 6 bytes x 1 block - ctrl.offset = 0; - ctrl.length = 6; - ctrl.blocks = 1; - -#ifdef RASCSI - // Command reception handshake (10 bytes are automatically received at the first command) - count = ctrl.bus->CommandHandShake(ctrl.buffer); - - // If no byte can be received move to the status phase - if (count == 0) { - Error(); - return; - } - - // Check 10-byte CDB - if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) { - ctrl.length = 10; - } - - // If not able to receive all, move to the status phase - if (count != (int)ctrl.length) { - Error(); - return; - } - - // Command data transfer - for (i = 0; i < (int)ctrl.length; i++) { - ctrl.cmd[i] = (DWORD)ctrl.buffer[i]; - } - - // Clear length and block - ctrl.length = 0; - ctrl.blocks = 0; - - // Execution Phase - Execute(); -#else - // Request the command - ctrl.bus->SetREQ(TRUE); - return; -#endif // RASCSI - } -#ifndef RASCSI - // Requesting - if (ctrl.bus->GetREQ()) { - // Sent by the initiator - if (ctrl.bus->GetACK()) { - Receive(); - } - } else { - // Request the initator to - if (!ctrl.bus->GetACK()) { - ReceiveNext(); - } - } -#endif // RASCSI -} - -//--------------------------------------------------------------------------- -// -// Execution Phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Execute() -{ - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "Execution Phase Command %02X", ctrl.cmd[0]); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::execute; - - // Initialization for data transfer - ctrl.offset = 0; - ctrl.blocks = 1; -#ifdef RASCSI - ctrl.execstart = SysTimer::GetTimerLow(); -#endif // RASCSI - - // Process by command - switch (ctrl.cmd[0]) { - // TEST UNIT READY - case 0x00: - CmdTestUnitReady(); - return; - - // REZERO UNIT - case 0x01: - CmdRezero(); - return; - - // REQUEST SENSE - case 0x03: - CmdRequestSense(); - return; - - // FORMAT UNIT - case 0x04: - CmdFormat(); - return; - - // FORMAT UNIT - case 0x06: - CmdFormat(); - return; - - // REASSIGN BLOCKS - case 0x07: - CmdReassign(); - return; - - // READ(6) - case 0x08: - CmdRead6(); - return; - - // WRITE(6) - case 0x0a: - CmdWrite6(); - return; - - // SEEK(6) - case 0x0b: - CmdSeek6(); - return; - - // ASSIGN(SASIのみ) - case 0x0e: - CmdAssign(); - return; - - // SPECIFY(SASIのみ) - case 0xc2: - CmdSpecify(); - return; - } - - // Unsupported command - Log(Log::Warning, "Unsupported command $%02X", ctrl.cmd[0]); - CmdInvalid(); -} - -//--------------------------------------------------------------------------- -// -// Status phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Status() -{ -#ifdef RASCSI - DWORD min_exec_time; - DWORD time; -#endif // RASCSI - - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::status) { - -#ifdef RASCSI - // Minimum execution time - if (ctrl.execstart > 0) { - min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; - time = SysTimer::GetTimerLow() - ctrl.execstart; - if (time < min_exec_time) { - SysTimer::SleepUsec(min_exec_time - time); - } - ctrl.execstart = 0; - } else { - SysTimer::SleepUsec(5); - } -#endif // RASCSI - -#if defined(DISK_LOG) - Log(Log::Normal, "Status phase"); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::status; - - // Signal line operated by the target - ctrl.bus->SetMSG(FALSE); - ctrl.bus->SetCD(TRUE); - ctrl.bus->SetIO(TRUE); - - // Data transfer is 1 byte x 1 block - ctrl.offset = 0; - ctrl.length = 1; - ctrl.blocks = 1; - ctrl.buffer[0] = (BYTE)ctrl.status; - -#ifndef RASCSI - // Request status - ctrl.bus->SetDAT(ctrl.buffer[0]); - ctrl.bus->SetREQ(TRUE); - -#if defined(DISK_LOG) - Log(Log::Normal, "Status Phase $%02X", ctrl.status); -#endif // DISK_LOG -#endif // RASCSI - return; - } - -#ifdef RASCSI - // Send - Send(); -#else - // Requesting - if (ctrl.bus->GetREQ()) { - // Initiator received - if (ctrl.bus->GetACK()) { - SendNext(); - } - } else { - // Initiator requests next - if (!ctrl.bus->GetACK()) { - Send(); - } - } -#endif // RASCSI -} - -//--------------------------------------------------------------------------- -// -// Message in phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::MsgIn() -{ - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::msgin) { - -#if defined(DISK_LOG) - Log(Log::Normal, "Message in phase"); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::msgin; - - // Signal line operated by the target - ctrl.bus->SetMSG(TRUE); - ctrl.bus->SetCD(TRUE); - ctrl.bus->SetIO(TRUE); - - // length, blocks are already set - ASSERT(ctrl.length > 0); - ASSERT(ctrl.blocks > 0); - ctrl.offset = 0; - -#ifndef RASCSI - // Request message - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); - ctrl.bus->SetREQ(TRUE); - -#if defined(DISK_LOG) - Log(Log::Normal, "Message in phase $%02X", ctrl.buffer[ctrl.offset]); -#endif // DISK_LOG -#endif // RASCSI - return; - } - -#ifdef RASCSI - //Send - Send(); -#else - // Requesting - if (ctrl.bus->GetREQ()) { - // Initator received - if (ctrl.bus->GetACK()) { - SendNext(); - } - } else { - // Initiator requests next - if (!ctrl.bus->GetACK()) { - Send(); - } - } -#endif // RASCSI -} - -//--------------------------------------------------------------------------- -// -// Data-in Phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::DataIn() -{ -#ifdef RASCSI - DWORD min_exec_time; - DWORD time; -#endif // RASCSI - - ASSERT(this); - ASSERT(ctrl.length >= 0); - - // Phase change - if (ctrl.phase != BUS::datain) { - -#ifdef RASCSI - // Minimum execution time - if (ctrl.execstart > 0) { - min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; - time = SysTimer::GetTimerLow() - ctrl.execstart; - if (time < min_exec_time) { - SysTimer::SleepUsec(min_exec_time - time); - } - ctrl.execstart = 0; - } -#endif // RASCSI - - // If the length is 0, go to the status phase - if (ctrl.length == 0) { - Status(); - return; - } - -#if defined(DISK_LOG) - Log(Log::Normal, "Data-in Phase"); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::datain; - - // Signal line operated by the target - ctrl.bus->SetMSG(FALSE); - ctrl.bus->SetCD(FALSE); - ctrl.bus->SetIO(TRUE); - - // length, blocks are already set - ASSERT(ctrl.length > 0); - ASSERT(ctrl.blocks > 0); - ctrl.offset = 0; - -#ifndef RASCSI - // Assert the DAT signal - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); - - // Request data - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - -#ifdef RASCSI - // Send - Send(); -#else - // Requesting - if (ctrl.bus->GetREQ()) { - // Initator received - if (ctrl.bus->GetACK()) { - SendNext(); - } - } else { - // Initiator requests next - if (!ctrl.bus->GetACK()) { - Send(); - } - } -#endif // RASCSI -} - -//--------------------------------------------------------------------------- -// -// Data out phase -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::DataOut() -{ -#ifdef RASCSI - DWORD min_exec_time; - DWORD time; -#endif // RASCSI - - ASSERT(this); - ASSERT(ctrl.length >= 0); - - // Phase change - if (ctrl.phase != BUS::dataout) { - -#ifdef RASCSI - // Minimum execution time - if (ctrl.execstart > 0) { - min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi; - time = SysTimer::GetTimerLow() - ctrl.execstart; - if (time < min_exec_time) { - SysTimer::SleepUsec(min_exec_time - time); - } - ctrl.execstart = 0; - } -#endif // RASCSI - - // If the length is 0, go to the status phase - if (ctrl.length == 0) { - Status(); - return; - } - -#if defined(DISK_LOG) - Log(Log::Normal, "Data out phase"); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::dataout; - - // Signal line operated by the target - ctrl.bus->SetMSG(FALSE); - ctrl.bus->SetCD(FALSE); - ctrl.bus->SetIO(FALSE); - - // length, blocks are already calculated - ASSERT(ctrl.length > 0); - ASSERT(ctrl.blocks > 0); - ctrl.offset = 0; - -#ifndef RASCSI - // Request data - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - -#ifdef RASCSI - // Receive - Receive(); -#else - // Requesting - if (ctrl.bus->GetREQ()) { - // Sent by the initiator - if (ctrl.bus->GetACK()) { - Receive(); - } - } else { - // Request the initator to - if (!ctrl.bus->GetACK()) { - ReceiveNext(); - } - } -#endif // RASCSI -} - -//--------------------------------------------------------------------------- -// -// Error -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Error() -{ - DWORD lun; - - ASSERT(this); - - // Get bus information - ctrl.bus->Aquire(); - - // Reset check - if (ctrl.bus->GetRST()) { - // Reset the controller - Reset(); - - // Reset the bus - ctrl.bus->Reset(); - return; - } - - // Bus free for status phase and message in phase - if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) { - BusFree(); - return; - } - -#if defined(DISK_LOG) - Log(Log::Warning, "Error occured (going to status phase)"); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - - // Set status and message(CHECK CONDITION) - ctrl.status = (lun << 5) | 0x02; - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// TEST UNIT READY -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdTestUnitReady() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "TEST UNIT READY Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->TestUnitReady(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// REZERO UNIT -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdRezero() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "REZERO UNIT Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Rezero(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// REQUEST SENSE -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdRequestSense() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "REQUEST SENSE Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length > 0); - -#if defined(DISK_LOG) - Log(Log::Normal, "Sense key $%02X", ctrl.buffer[2]); -#endif // DISK_LOG - - // Read phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// FORMAT UNIT -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdFormat() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "FORMAT UNIT Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Format(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// REASSIGN BLOCKS -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdReassign() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "REASSIGN BLOCKS Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Reassign(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// READ(6) -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdRead6() -{ - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Get record number and block number - record = ctrl.cmd[1] & 0x1f; - record <<= 8; - record |= ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - ctrl.blocks = ctrl.cmd[4]; - if (ctrl.blocks == 0) { - ctrl.blocks = 0x100; - } - -#if defined(DISK_LOG) - Log(Log::Normal, - "READ(6) command record=%06X blocks=%d", record, ctrl.blocks); -#endif // DISK_LOG - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Read phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// WRITE(6) -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdWrite6() -{ - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Get record number and block number - record = ctrl.cmd[1] & 0x1f; - record <<= 8; - record |= ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - ctrl.blocks = ctrl.cmd[4]; - if (ctrl.blocks == 0) { - ctrl.blocks = 0x100; - } - -#if defined(DISK_LOG) - Log(Log::Normal, - "WRITE(6) command record=%06X blocks=%d", record, ctrl.blocks); -#endif // DISK_LOG - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->WriteCheck(record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Write phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// SEEK(6) -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdSeek6() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "SEEK(6) Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Seek(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// ASSIGN -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdAssign() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "ASSIGN Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Assign(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // Request 4 bytes of data - ctrl.length = 4; - - // Write phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// SPECIFY -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdSpecify() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "SPECIFY Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Assign(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // Request 10 bytes of data - ctrl.length = 10; - - // Write phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// Unsupported command -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::CmdInvalid() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "Command not supported"); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (ctrl.unit[lun]) { - // Command processing on drive - ctrl.unit[lun]->InvalidCmd(); - } - - // Failure (Error) - Error(); -} - -//=========================================================================== -// -// Data transfer -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Data transmission -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Send() -{ -#ifdef RASCSI - int len; -#endif // RASCSI - BOOL result; - - ASSERT(this); - ASSERT(!ctrl.bus->GetREQ()); - ASSERT(ctrl.bus->GetIO()); - -#ifdef RASCSI - // Check that the length isn't 0 - if (ctrl.length != 0) { - len = ctrl.bus->SendHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); - - // If you can not send it all, move on to the status phase - if (len != (int)ctrl.length) { - Error(); - return; - } - - // Offset and Length - ctrl.offset += ctrl.length; - ctrl.length = 0; - return; - } -#else - // Offset and Length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // Immediately after ACK is asserted, if the data - // has been set by SendNext, raise the request - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; - } -#endif // RASCSI - - // Remove block and initialize the result - ctrl.blocks--; - result = TRUE; - - // Process after data collection (read/data-in only) - if (ctrl.phase == BUS::datain) { - if (ctrl.blocks != 0) { - // Set next buffer (set offset, length) - result = XferIn(ctrl.buffer); - //** printf("xfer in: %d \n",result); - -#ifndef RASCSI - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); -#endif // RASCSI - } - } - - // If result FALSE, move to the status phase - if (!result) { - Error(); - return; - } - - // Continue sending if block != 0 - if (ctrl.blocks != 0){ - ASSERT(ctrl.length > 0); - ASSERT(ctrl.offset == 0); -#ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - - // Move to the next phase - switch (ctrl.phase) { - // Message in phase - case BUS::msgin: - // Bus free phase - BusFree(); - break; - - // Data-in Phase - case BUS::datain: - // status phase - Status(); - break; - - // Status phase - case BUS::status: - // Message in phase - ctrl.length = 1; - ctrl.blocks = 1; - ctrl.buffer[0] = (BYTE)ctrl.message; - MsgIn(); - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Continue sending data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::SendNext() -{ - ASSERT(this); - - // Req is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(ctrl.bus->GetIO()); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - // If there is data in the buffer, set it first. - if (ctrl.length > 1) { - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset + 1]); - } -} -#endif // RASCSI - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Receive data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Receive() -{ - DWORD data; - - ASSERT(this); - - // Req is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(!ctrl.bus->GetIO()); - - // Get data - data = (DWORD)ctrl.bus->GetDAT(); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - switch (ctrl.phase) { - // Command phase - case BUS::command: - ctrl.cmd[ctrl.offset] = data; -#if defined(DISK_LOG) - Log(Log::Normal, "Command phase $%02X", data); -#endif // DISK_LOG - - // Set the length again with the first data (offset 0) - if (ctrl.offset == 0) { - if (ctrl.cmd[0] >= 0x20 && ctrl.cmd[0] <= 0x7D) { - // 10 byte CDB - ctrl.length = 10; - } - } - break; - - // Data out phase - case BUS::dataout: - ctrl.buffer[ctrl.offset] = (BYTE)data; - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} -#endif // RASCSI - -#ifdef RASCSI -//--------------------------------------------------------------------------- -// -// Receive data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Receive() -#else -//--------------------------------------------------------------------------- -// -// Continue receiving data -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::ReceiveNext() -#endif // RASCSI -{ -#ifdef RASCSI - int len; -#endif // RASCSI - BOOL result; - - ASSERT(this); - - // REQ is low - ASSERT(!ctrl.bus->GetREQ()); - ASSERT(!ctrl.bus->GetIO()); - -#ifdef RASCSI - // Length != 0 if received - if (ctrl.length != 0) { - // Receive - len = ctrl.bus->ReceiveHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); - - // If not able to receive all, move to status phase - if (len != (int)ctrl.length) { - Error(); - return; - } - - // Offset and Length - ctrl.offset += ctrl.length; - ctrl.length = 0; - return; - } -#else - // Offset and Length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // If length != 0, set req again - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; - } -#endif // RASCSI - - // Remove the control block and initialize the result - ctrl.blocks--; - result = TRUE; - - // Process the data out phase - if (ctrl.phase == BUS::dataout) { - if (ctrl.blocks == 0) { - // End with this buffer - result = XferOut(FALSE); - } else { - // Continue to next buffer (set offset, length) - result = XferOut(TRUE); - } - } - - // If result is false, move to the status phase - if (!result) { - Error(); - return; - } - - // Continue to receive is block != 0 - if (ctrl.blocks != 0){ - ASSERT(ctrl.length > 0); - ASSERT(ctrl.offset == 0); -#ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - - // Move to the next phase - switch (ctrl.phase) { -#ifndef RASCSI - // Command phase - case BUS::command: - // Execution Phase - Execute(); - break; -#endif // RASCSI - - // Data out phase - case BUS::dataout: - // Flush - FlushUnit(); - - // status phase - Status(); - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} - -//--------------------------------------------------------------------------- -// -// Data transfer IN -// *Reset offset and length -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::XferIn(BYTE *buf) -{ - DWORD lun; - - ASSERT(this); - ASSERT(ctrl.phase == BUS::datain); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - return FALSE; - } - - // Limited to read commands - switch (ctrl.cmd[0]) { - // READ(6) - case 0x08: - // READ(10) - case 0x28: - // Read from disk - ctrl.length = ctrl.unit[lun]->Read(buf, ctrl.next); - ctrl.next++; - - // If there is an error, go to the status phase - if (ctrl.length <= 0) { - // Cancel data-in - return FALSE; - } - - // If things are normal, work setting - ctrl.offset = 0; - break; - - // Other (impossible) - default: - ASSERT(FALSE); - return FALSE; - } - - // Succeeded in setting the buffer - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Data transfer OUT -// *If cont=true, reset the offset and length -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SASIDEV::XferOut(BOOL cont) -{ - DWORD lun; - SCSIBR *bridge; - - ASSERT(this); - ASSERT(ctrl.phase == BUS::dataout); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - return FALSE; - } - - // MODE SELECT or WRITE system - switch (ctrl.cmd[0]) { - // MODE SELECT - case 0x15: - // MODE SELECT(10) - case 0x55: - if (!ctrl.unit[lun]->ModeSelect( - ctrl.cmd, ctrl.buffer, ctrl.offset)) { - // MODE SELECT failed - return FALSE; - } - break; - - // WRITE(6) - case 0x0a: - // WRITE(10) - case 0x2a: - // Replace the host bridge with SEND MESSAGE 10 - if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { - bridge = (SCSIBR*)ctrl.unit[lun]; - if (!bridge->SendMessage10(ctrl.cmd, ctrl.buffer)) { - // write failed - return FALSE; - } - - // If normal, work setting - ctrl.offset = 0; - break; - } - - // WRITE AND VERIFY - case 0x2e: - // Write - if (!ctrl.unit[lun]->Write(ctrl.buffer, ctrl.next - 1)) { - // Write failed - return FALSE; - } - - // If you do not need the next block, end here - ctrl.next++; - if (!cont) { - break; - } - - // Check the next block - ctrl.length = ctrl.unit[lun]->WriteCheck(ctrl.next - 1); - if (ctrl.length <= 0) { - // Cannot write - return FALSE; - } - - // If normal, work setting - ctrl.offset = 0; - break; - - // SPECIFY(SASI only) - case 0xc2: - break; - - default: - ASSERT(FALSE); - break; - } - - // Buffer saved successfully - return TRUE; -} - -//--------------------------------------------------------------------------- -// -// Logical unit flush -// -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::FlushUnit() -{ - DWORD lun; - - ASSERT(this); - ASSERT(ctrl.phase == BUS::dataout); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - return; - } - - // WRITE system only - switch (ctrl.cmd[0]) { - // WRITE(6) - case 0x0a: - // WRITE(10) - case 0x2a: - // WRITE AND VERIFY - case 0x2e: - // Flush - if (!ctrl.unit[lun]->IsCacheWB()) { - ctrl.unit[lun]->Flush(); - } - break; - // Mode Select (6) - case 0x15: - // MODE SELECT(10) - case 0x55: - // Debug code related to Issue #2 on github, where we get an unhandled Model select when - // the mac is rebooted - // https://github.com/akuker/RASCSI/issues/2 - Log(Log::Warning, "Received \'Mode Select\'\n"); - Log(Log::Warning, " Operation Code: [%02X]\n", ctrl.cmd[0]); - Log(Log::Warning, " Logical Unit %01X, PF %01X, SP %01X [%02X]\n", ctrl.cmd[1] >> 5, 1 & (ctrl.cmd[1] >> 4), ctrl.cmd[1] & 1, ctrl.cmd[1]); - Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[2]); - Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[3]); - Log(Log::Warning, " Parameter List Len %02X\n", ctrl.cmd[4]); - Log(Log::Warning, " Reserved: %02X\n", ctrl.cmd[5]); - Log(Log::Warning, " Ctrl Len: %08X\n",ctrl.length); - - if (!ctrl.unit[lun]->ModeSelect( - ctrl.cmd, ctrl.buffer, ctrl.offset)) { - // MODE SELECT failed - Log(Log::Warning, "Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]); - return; - } - break; - - default: - Log(Log::Warning, "Received an invalid flush command %02X!!!!!\n",ctrl.cmd[0]); - ASSERT(FALSE); - break; - } -} - -#ifdef DISK_LOG -//--------------------------------------------------------------------------- -// -// Get the current phase as a string -// -//--------------------------------------------------------------------------- -void SASIDEV::GetPhaseStr(char *str) -{ - switch(this->GetPhase()) - { - case BUS::busfree: - strcpy(str,"busfree "); - break; - case BUS::arbitration: - strcpy(str,"arbitration"); - break; - case BUS::selection: - strcpy(str,"selection "); - break; - case BUS::reselection: - strcpy(str,"reselection"); - break; - case BUS::command: - strcpy(str,"command "); - break; - case BUS::execute: - strcpy(str,"execute "); - break; - case BUS::datain: - strcpy(str,"datain "); - break; - case BUS::dataout: - strcpy(str,"dataout "); - break; - case BUS::status: - strcpy(str,"status "); - break; - case BUS::msgin: - strcpy(str,"msgin "); - break; - case BUS::msgout: - strcpy(str,"msgout "); - break; - case BUS::reserved: - strcpy(str,"reserved "); - break; - } -} -#endif - -//--------------------------------------------------------------------------- -// -// Log output -// -// TODO: This function needs some cleanup. Its very kludgey -//--------------------------------------------------------------------------- -void FASTCALL SASIDEV::Log(Log::loglevel level, const char *format, ...) -{ -#if !defined(BAREMETAL) -#ifdef DISK_LOG - char buffer[0x200]; - char buffer2[0x250]; - char buffer3[0x250]; - char phase_str[20]; -#endif - va_list args; - va_start(args, format); - - if(this->GetID() != 6) - { - return; - } - -#ifdef RASCSI -#ifndef DISK_LOG - if (level == Log::Warning) { - return; - } -#endif // DISK_LOG -#endif // RASCSI - -#ifdef DISK_LOG - // format - vsprintf(buffer, format, args); - - // end variable length argument - va_end(args); - - // Add the date/timestamp - // current date/time based on current system - time_t now = time(0); - // convert now to string form - char* dt = ctime(&now); - - - strcpy(buffer2, "["); - strcat(buffer2, dt); - // Get rid of the carriage return - buffer2[strlen(buffer2)-1] = '\0'; - strcat(buffer2, "] "); - - // Get the phase - this->GetPhaseStr(phase_str); - sprintf(buffer3, "[%d][%s] ", this->GetID(), phase_str); - strcat(buffer2,buffer3); - strcat(buffer2, buffer); - - - // Log output -#ifdef RASCSI - printf("%s\n", buffer2); -#else - host->GetVM()->GetLog()->Format(level, host, buffer); -#endif // RASCSI -#endif // BAREMETAL -#endif // DISK_LOG -} - -//=========================================================================== -// -// SCSI Device -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Constructor -// -//--------------------------------------------------------------------------- -#ifdef RASCSI -SCSIDEV::SCSIDEV() : SASIDEV() -#else -SCSIDEV::SCSIDEV(Device *dev) : SASIDEV(dev) -#endif -{ - // Synchronous transfer work initialization - scsi.syncenable = FALSE; - scsi.syncperiod = 50; - scsi.syncoffset = 0; - scsi.atnmsg = FALSE; - scsi.msc = 0; - memset(scsi.msb, 0x00, sizeof(scsi.msb)); -} - -//--------------------------------------------------------------------------- -// -// Device reset -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Reset() -{ - ASSERT(this); - - // Work initialization - scsi.atnmsg = FALSE; - scsi.msc = 0; - memset(scsi.msb, 0x00, sizeof(scsi.msb)); - - // Base class - SASIDEV::Reset(); -} - -//--------------------------------------------------------------------------- -// -// Process -// -//--------------------------------------------------------------------------- -BUS::phase_t FASTCALL SCSIDEV::Process() -{ - ASSERT(this); - - // Do nothing if not connected - if (ctrl.id < 0 || ctrl.bus == NULL) { - return ctrl.phase; - } - - // Get bus information - ctrl.bus->Aquire(); - - // Reset - if (ctrl.bus->GetRST()) { -#if defined(DISK_LOG) - Log(Log::Normal, "RESET信号受信"); -#endif // DISK_LOG - - // Reset the controller - Reset(); - - // Reset the bus - ctrl.bus->Reset(); - return ctrl.phase; - } - - // Phase processing - switch (ctrl.phase) { - // Bus free phase - case BUS::busfree: - BusFree(); - break; - - // Selection phase - case BUS::selection: - Selection(); - break; - - // Data out (MCI=000) - case BUS::dataout: - DataOut(); - break; - - // Data in (MCI=001) - case BUS::datain: - DataIn(); - break; - - // Command (MCI=010) - case BUS::command: - Command(); - break; - - // Status (MCI=011) - case BUS::status: - Status(); - break; - - // Message out (MCI=110) - case BUS::msgout: - MsgOut(); - break; - - // Message in (MCI=111) - case BUS::msgin: - MsgIn(); - break; - - // Other - default: - ASSERT(FALSE); - break; - } - - return ctrl.phase; -} - -//--------------------------------------------------------------------------- -// -// Phaes -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// Bus free phase -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::BusFree() -{ - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::busfree) { - -#if defined(DISK_LOG) - Log(Log::Normal, "Bus free phase"); -#endif // DISK_LOG - - // Phase setting - ctrl.phase = BUS::busfree; - - // Signal line - ctrl.bus->SetREQ(FALSE); - ctrl.bus->SetMSG(FALSE); - ctrl.bus->SetCD(FALSE); - ctrl.bus->SetIO(FALSE); - ctrl.bus->SetBSY(FALSE); - - // Initialize status and message - ctrl.status = 0x00; - ctrl.message = 0x00; - - // Initialize ATN message reception status - scsi.atnmsg = FALSE; - return; - } - - // Move to selection phase - if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) { - Selection(); - } -} - -//--------------------------------------------------------------------------- -// -// Selection Phase -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Selection() -{ - DWORD id; - - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::selection) { - // invalid if IDs do not match - id = 1 << ctrl.id; - if ((ctrl.bus->GetDAT() & id) == 0) { - return; - } - - // End if there is no valid unit - if (!HasUnit()) { - return; - } - -#if defined(DISK_LOG) - Log(Log::Normal, - "Selection Phase ID=%d (with device)", ctrl.id); -#endif // DISK_LOG - - // Phase setting - ctrl.phase = BUS::selection; - - // Raise BSY and respond - ctrl.bus->SetBSY(TRUE); - return; - } - - // Selection completed - if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) { - // Message out phase if ATN=1, otherwise command phase - if (ctrl.bus->GetATN()) { - MsgOut(); - } else { - Command(); - } - } -} - -//--------------------------------------------------------------------------- -// -// Execution Phase -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Execute() -{ - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "Execution phase command $%02X", ctrl.cmd[0]); -#endif // DISK_LOG - - // Phase Setting - ctrl.phase = BUS::execute; - - // Initialization for data transfer - ctrl.offset = 0; - ctrl.blocks = 1; -#ifdef RASCSI - ctrl.execstart = SysTimer::GetTimerLow(); -#endif // RASCSI - - // Process by command - switch (ctrl.cmd[0]) { - // TEST UNIT READY - case 0x00: - CmdTestUnitReady(); - return; - - // REZERO - case 0x01: - CmdRezero(); - return; - - // REQUEST SENSE - case 0x03: - CmdRequestSense(); - return; - - // FORMAT UNIT - case 0x04: - CmdFormat(); - return; - - // REASSIGN BLOCKS - case 0x07: - CmdReassign(); - return; - - // READ(6) - case 0x08: - CmdRead6(); - return; - - // WRITE(6) - case 0x0a: - CmdWrite6(); - return; - - // SEEK(6) - case 0x0b: - CmdSeek6(); - return; - - // INQUIRY - case 0x12: - CmdInquiry(); - return; - - // MODE SELECT - case 0x15: - CmdModeSelect(); - return; - - // MDOE SENSE - case 0x1a: - CmdModeSense(); - return; - - // START STOP UNIT - case 0x1b: - CmdStartStop(); - return; - - // SEND DIAGNOSTIC - case 0x1d: - CmdSendDiag(); - return; - - // PREVENT/ALLOW MEDIUM REMOVAL - case 0x1e: - CmdRemoval(); - return; - - // READ CAPACITY - case 0x25: - CmdReadCapacity(); - return; - - // READ(10) - case 0x28: - CmdRead10(); - return; - - // WRITE(10) - case 0x2a: - CmdWrite10(); - return; - - // SEEK(10) - case 0x2b: - CmdSeek10(); - return; - - // WRITE and VERIFY - case 0x2e: - CmdWrite10(); - return; - - // VERIFY - case 0x2f: - CmdVerify(); - return; - - // SYNCHRONIZE CACHE - case 0x35: - CmdSynchronizeCache(); - return; - - // READ DEFECT DATA(10) - case 0x37: - CmdReadDefectData10(); - return; - - // READ TOC - case 0x43: - CmdReadToc(); - return; - - // PLAY AUDIO(10) - case 0x45: - CmdPlayAudio10(); - return; - - // PLAY AUDIO MSF - case 0x47: - CmdPlayAudioMSF(); - return; - - // PLAY AUDIO TRACK - case 0x48: - CmdPlayAudioTrack(); - return; - - // MODE SELECT(10) - case 0x55: - CmdModeSelect10(); - return; - - // MDOE SENSE(10) - case 0x5a: - CmdModeSense10(); - return; - - // SPECIFY (SASI only/Suppress warning when using SxSI) - case 0xc2: - CmdInvalid(); - return; - - } - - // No other support - Log(Log::Normal, "Unsupported command received: $%02X", ctrl.cmd[0]); - CmdInvalid(); -} - -//--------------------------------------------------------------------------- -// -// Message out phase -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::MsgOut() -{ - ASSERT(this); - - // Phase change - if (ctrl.phase != BUS::msgout) { - -#if defined(DISK_LOG) - Log(Log::Normal, "Message Out Phase"); -#endif // DISK_LOG - - // Message out phase after selection - // process the IDENTIFY message - if (ctrl.phase == BUS::selection) { - scsi.atnmsg = TRUE; - scsi.msc = 0; - memset(scsi.msb, 0x00, sizeof(scsi.msb)); - } - - // Phase Setting - ctrl.phase = BUS::msgout; - - // Signal line operated by the target - ctrl.bus->SetMSG(TRUE); - ctrl.bus->SetCD(TRUE); - ctrl.bus->SetIO(FALSE); - - // Data transfer is 1 byte x 1 block - ctrl.offset = 0; - ctrl.length = 1; - ctrl.blocks = 1; - -#ifndef RASCSI - // Request message - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - -#ifdef RASCSI - // Receive - Receive(); -#else - // Requesting - if (ctrl.bus->GetREQ()) { - // Sent by the initiator - if (ctrl.bus->GetACK()) { - Receive(); - } - } else { - // Request the initator to - if (!ctrl.bus->GetACK()) { - ReceiveNext(); - } - } -#endif // RASCSI -} - -//--------------------------------------------------------------------------- -// -// Common Error Handling -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Error() -{ - ASSERT(this); - - // Get bus information - ctrl.bus->Aquire(); - - // Reset check - if (ctrl.bus->GetRST()) { - // Reset the controller - Reset(); - - // Reset the bus - ctrl.bus->Reset(); - return; - } - - // Bus free for status phase and message in phase - if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) { - BusFree(); - return; - } - -#if defined(DISK_LOG) - Log(Log::Normal, "Error (to status phase)"); -#endif // DISK_LOG - - // Set status and message(CHECK CONDITION) - ctrl.status = 0x02; - ctrl.message = 0x00; - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// Command -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// INQUIRY -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdInquiry() -{ - Disk *disk; - int lun; - DWORD major; - DWORD minor; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "INQUIRY Command"); -#endif // DISK_LOG - - // Find a valid unit - disk = NULL; - for (lun = 0; lun < UnitMax; lun++) { - if (ctrl.unit[lun]) { - disk = ctrl.unit[lun]; - break; - } - } - - // Processed on the disk side (it is originally processed by the controller) - if (disk) { -#ifdef RASCSI - major = (DWORD)(RASCSI >> 8); - minor = (DWORD)(RASCSI & 0xff); -#else - host->GetVM()->GetVersion(major, minor); -#endif // RASCSI - ctrl.length = - ctrl.unit[lun]->Inquiry(ctrl.cmd, ctrl.buffer, major, minor); - } else { - ctrl.length = 0; - } - - if (ctrl.length <= 0) { - // failure (error) - Error(); - return; - } - - // Add synchronous transfer support information - if (scsi.syncenable) { - ctrl.buffer[7] |= (1 << 4); - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSelect() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "MODE SELECT Command"); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->SelectCheck(ctrl.cmd); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// MODE SENSE -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSense() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "MODE SENSE Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ModeSense(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length >= 0); - if (ctrl.length == 0) { - Log(Log::Warning, - "Not supported MODE SENSE page $%02X", ctrl.cmd[2]); - - // Failure (Error) - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// START STOP UNIT -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdStartStop() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "START STOP UNIT Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->StartStop(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// SEND DIAGNOSTIC -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSendDiag() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "SEND DIAGNOSTIC Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->SendDiag(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// PREVENT/ALLOW MEDIUM REMOVAL -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdRemoval() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "PREVENT/ALLOW MEDIUM REMOVAL Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Removal(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// READ CAPACITY -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReadCapacity() -{ - DWORD lun; - int length; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "READ CAPACITY Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - length = ctrl.unit[lun]->ReadCapacity(ctrl.cmd, ctrl.buffer); - ASSERT(length >= 0); - if (length <= 0) { - Error(); - return; - } - - // Length setting - ctrl.length = length; - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// READ(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdRead10() -{ - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Receive message if host bridge - if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { - CmdGetMessage10(); - return; - } - - // Get record number and block number - record = ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - record <<= 8; - record |= ctrl.cmd[4]; - record <<= 8; - record |= ctrl.cmd[5]; - ctrl.blocks = ctrl.cmd[7]; - ctrl.blocks <<= 8; - ctrl.blocks |= ctrl.cmd[8]; - -#if defined(DISK_LOG) - Log(Log::Normal, "READ(10) command record=%08X block=%d", record, ctrl.blocks); -#endif // DISK_LOG - - // Do not process 0 blocks - if (ctrl.blocks == 0) { - Status(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// WRITE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdWrite10() -{ - DWORD lun; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Receive message with host bridge - if (ctrl.unit[lun]->GetID() == MAKEID('S', 'C', 'B', 'R')) { - CmdSendMessage10(); - return; - } - - // Get record number and block number - record = ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - record <<= 8; - record |= ctrl.cmd[4]; - record <<= 8; - record |= ctrl.cmd[5]; - ctrl.blocks = ctrl.cmd[7]; - ctrl.blocks <<= 8; - ctrl.blocks |= ctrl.cmd[8]; - -#if defined(DISK_LOG) - Log(Log::Normal, - "WRTIE(10) command record=%08X blocks=%d", record, ctrl.blocks); -#endif // DISK_LOG - - // Do not process 0 blocks - if (ctrl.blocks == 0) { - Status(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->WriteCheck(record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// SEEK(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSeek10() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "SEEK(10) Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->Seek(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// VERIFY -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdVerify() -{ - DWORD lun; - BOOL status; - DWORD record; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Get record number and block number - record = ctrl.cmd[2]; - record <<= 8; - record |= ctrl.cmd[3]; - record <<= 8; - record |= ctrl.cmd[4]; - record <<= 8; - record |= ctrl.cmd[5]; - ctrl.blocks = ctrl.cmd[7]; - ctrl.blocks <<= 8; - ctrl.blocks |= ctrl.cmd[8]; - -#if defined(DISK_LOG) - Log(Log::Normal, - "VERIFY command record=%08X blocks=%d", record, ctrl.blocks); -#endif // DISK_LOG - - // Do not process 0 blocks - if (ctrl.blocks == 0) { - Status(); - return; - } - - // if BytChk=0 - if ((ctrl.cmd[1] & 0x02) == 0) { - // Command processing on drive - status = ctrl.unit[lun]->Seek(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); - return; - } - - // Test loading - ctrl.length = ctrl.unit[lun]->Read(ctrl.buffer, record); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.next = record + 1; - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// SYNCHRONIZE CACHE -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSynchronizeCache() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Make it do something (not implemented)... - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// READ DEFECT DATA(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReadDefectData10() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "READ DEFECT DATA(10) Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ReadDefectData10(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length >= 0); - - if (ctrl.length <= 4) { - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// READ TOC -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdReadToc() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ReadToc(ctrl.cmd, ctrl.buffer); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdPlayAudio10() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->PlayAudio(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO MSF -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdPlayAudioMSF() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->PlayAudioMSF(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// PLAY AUDIO TRACK -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdPlayAudioTrack() -{ - DWORD lun; - BOOL status; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - status = ctrl.unit[lun]->PlayAudioTrack(ctrl.cmd); - if (!status) { - // Failure (Error) - Error(); - return; - } - - // status phase - Status(); -} - -//--------------------------------------------------------------------------- -// -// MODE SELECT10 -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSelect10() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "MODE SELECT10 Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->SelectCheck10(ctrl.cmd); - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Data out phase - DataOut(); -} - -//--------------------------------------------------------------------------- -// -// MODE SENSE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdModeSense10() -{ - DWORD lun; - - ASSERT(this); - -#if defined(DISK_LOG) - Log(Log::Normal, "MODE SENSE(10) Command "); -#endif // DISK_LOG - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Command processing on drive - ctrl.length = ctrl.unit[lun]->ModeSense10(ctrl.cmd, ctrl.buffer); - ASSERT(ctrl.length >= 0); - if (ctrl.length == 0) { - Log(Log::Warning, - "Not supported MODE SENSE(10) page $%02X", ctrl.cmd[2]); - - // Failure (Error) - Error(); - return; - } - - // Data-in Phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// GET MESSAGE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdGetMessage10() -{ - DWORD lun; - SCSIBR *bridge; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Error if not a host bridge - if (ctrl.unit[lun]->GetID() != MAKEID('S', 'C', 'B', 'R')) { - Error(); - return; - } - - // Reallocate buffer (because it is not transfer for each block) - if (ctrl.bufsize < 0x1000000) { - free(ctrl.buffer); - ctrl.bufsize = 0x1000000; - ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); - } - - // Process with drive - bridge = (SCSIBR*)ctrl.unit[lun]; - ctrl.length = bridge->GetMessage10(ctrl.cmd, ctrl.buffer); - - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.blocks = 1; - ctrl.next = 1; - - // Data in phase - DataIn(); -} - -//--------------------------------------------------------------------------- -// -// SEND MESSAGE(10) -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::CmdSendMessage10() -{ - DWORD lun; - - ASSERT(this); - - // Logical Unit - lun = (ctrl.cmd[1] >> 5) & 0x07; - if (!ctrl.unit[lun]) { - Error(); - return; - } - - // Error if not a host bridge - if (ctrl.unit[lun]->GetID() != MAKEID('S', 'C', 'B', 'R')) { - Error(); - return; - } - - // Reallocate buffer (because it is not transfer for each block) - if (ctrl.bufsize < 0x1000000) { - free(ctrl.buffer); - ctrl.bufsize = 0x1000000; - ctrl.buffer = (BYTE *)malloc(ctrl.bufsize); - } - - // Set transfer amount - ctrl.length = ctrl.cmd[6]; - ctrl.length <<= 8; - ctrl.length |= ctrl.cmd[7]; - ctrl.length <<= 8; - ctrl.length |= ctrl.cmd[8]; - - if (ctrl.length <= 0) { - // Failure (Error) - Error(); - return; - } - - // Set next block - ctrl.blocks = 1; - ctrl.next = 1; - - // Light phase - DataOut(); -} - -//=========================================================================== -// -// Data Transfer -// -//=========================================================================== - -//--------------------------------------------------------------------------- -// -// Send data -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Send() -{ -#ifdef RASCSI - int len; -#endif // RASCSI - BOOL result; - - ASSERT(this); - ASSERT(!ctrl.bus->GetREQ()); - ASSERT(ctrl.bus->GetIO()); - -#ifdef RASCSI - //if Length! = 0, send - if (ctrl.length != 0) { - len = ctrl.bus->SendHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); - - // If you cannot send all, move to status phase - if (len != (int)ctrl.length) { - Error(); - return; - } - - // offset and length - ctrl.offset += ctrl.length; - ctrl.length = 0; - return; - } -#else - // offset and length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // Immediately after ACK is asserted, if the data has been - // set by SendNext, raise the request - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; - } -#endif // RASCSI - - // Block subtraction, result initialization - ctrl.blocks--; - result = TRUE; - - // Processing after data collection (read/data-in only) - if (ctrl.phase == BUS::datain) { - if (ctrl.blocks != 0) { - // // set next buffer (set offset, length) - result = XferIn(ctrl.buffer); -#ifndef RASCSI - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset]); -#endif // RASCSI - } - } - - // If result FALSE, move to status phase - if (!result) { - Error(); - return; - } - - // Continue sending if block !=0 - if (ctrl.blocks != 0){ - ASSERT(ctrl.length > 0); - ASSERT(ctrl.offset == 0); -#ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - - // Move to next phase - switch (ctrl.phase) { - // Message in phase - case BUS::msgin: - // Completed sending response to extended message of IDENTIFY message - if (scsi.atnmsg) { - // flag off - scsi.atnmsg = FALSE; - - // command phase - Command(); - } else { - // Bus free phase - BusFree(); - } - break; - - // Data-in Phase - case BUS::datain: - // status phase - Status(); - break; - - // status phase - case BUS::status: - // Message in phase - ctrl.length = 1; - ctrl.blocks = 1; - ctrl.buffer[0] = (BYTE)ctrl.message; - MsgIn(); - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Continue data transmission..... -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::SendNext() -{ - ASSERT(this); - - // REQ is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(ctrl.bus->GetIO()); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - // If there is data in the buffer, set it first - if (ctrl.length > 1) { - ctrl.bus->SetDAT(ctrl.buffer[ctrl.offset + 1]); - } -} -#endif // RASCSI - -#ifndef RASCSI -//--------------------------------------------------------------------------- -// -// Receive data -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Receive() -{ - DWORD data; - - ASSERT(this); - - // Req is up - ASSERT(ctrl.bus->GetREQ()); - ASSERT(!ctrl.bus->GetIO()); - - // Get data - data = (DWORD)ctrl.bus->GetDAT(); - - // Signal line operated by the target - ctrl.bus->SetREQ(FALSE); - - switch (ctrl.phase) { - // Command phase - case BUS::command: - ctrl.cmd[ctrl.offset] = data; -#if defined(DISK_LOG) - Log(Log::Normal, "Command phase $%02X", data); -#endif // DISK_LOG - - // Set the length again with the first data (offset 0) - if (ctrl.offset == 0) { - if (ctrl.cmd[0] >= 0x20) { - // 10バイトCDB - ctrl.length = 10; - } - } - break; - - // Message out phase - case BUS::msgout: - ctrl.message = data; -#if defined(DISK_LOG) - Log(Log::Normal, "Message out phase $%02X", data); -#endif // DISK_LOG - break; - - // Data out phase - case BUS::dataout: - ctrl.buffer[ctrl.offset] = (BYTE)data; - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} -#endif // RASCSI - -#ifdef RASCSI -//--------------------------------------------------------------------------- -// -// Receive Data -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::Receive() -#else -//--------------------------------------------------------------------------- -// -// Continue receiving data -// -//--------------------------------------------------------------------------- -void FASTCALL SCSIDEV::ReceiveNext() -#endif // RASCSI -{ -#ifdef RASCSI - int len; -#endif // RASCSI - BOOL result; - int i; - BYTE data; - - ASSERT(this); - - // REQ is low - ASSERT(!ctrl.bus->GetREQ()); - ASSERT(!ctrl.bus->GetIO()); - -#ifdef RASCSI - // Length != 0 if received - if (ctrl.length != 0) { - // Receive - len = ctrl.bus->ReceiveHandShake( - &ctrl.buffer[ctrl.offset], ctrl.length); - - // If not able to receive all, move to status phase - if (len != (int)ctrl.length) { - Error(); - return; - } - - // Offset and Length - ctrl.offset += ctrl.length; - ctrl.length = 0;; - return; - } -#else - // Offset and Length - ASSERT(ctrl.length >= 1); - ctrl.offset++; - ctrl.length--; - - // If length!=0, set req again - if (ctrl.length != 0) { - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); - return; - } -#endif // RASCSI - - // Block subtraction, result initialization - ctrl.blocks--; - result = TRUE; - - // Processing after receiving data (by phase) - switch (ctrl.phase) { - - // Data out phase - case BUS::dataout: - if (ctrl.blocks == 0) { - // End with this buffer - result = XferOut(FALSE); - } else { - // Continue to next buffer (set offset, length) - result = XferOut(TRUE); - } - break; - - // Message out phase - case BUS::msgout: - ctrl.message = ctrl.buffer[0]; - if (!XferMsg(ctrl.message)) { - // Immediately free the bus if message output fails - BusFree(); - return; - } - - // Clear message data in preparation for message-in - ctrl.message = 0x00; - break; - - default: - break; - } - - // If result FALSE, move to status phase - if (!result) { - Error(); - return; - } - - // Continue to receive if block !=0 - if (ctrl.blocks != 0){ - ASSERT(ctrl.length > 0); - ASSERT(ctrl.offset == 0); -#ifndef RASCSI - // Signal line operated by the target - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - - // Move to next phase - switch (ctrl.phase) { - // Command phase - case BUS::command: -#ifdef RASCSI - // Command data transfer - len = 6; - if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) { - // 10 byte CDB - len = 10; - } - for (i = 0; i < len; i++) { - ctrl.cmd[i] = (DWORD)ctrl.buffer[i]; -#if defined(DISK_LOG) - Log(Log::Normal, "Command $%02X", ctrl.cmd[i]); -#endif // DISK_LOG - } -#endif // RASCSI - - // Execution Phase - Execute(); - break; - - // Message out phase - case BUS::msgout: - // Continue message out phase as long as ATN keeps asserting - if (ctrl.bus->GetATN()) { - // Data transfer is 1 byte x 1 block - ctrl.offset = 0; - ctrl.length = 1; - ctrl.blocks = 1; -#ifndef RASCSI - // Request message - ctrl.bus->SetREQ(TRUE); -#endif // RASCSI - return; - } - - // Parsing messages sent by ATN - if (scsi.atnmsg) { - i = 0; - while (i < scsi.msc) { - // Message type - data = scsi.msb[i]; - - // ABORT - if (data == 0x06) { -#if defined(DISK_LOG) - Log(Log::Normal, - "Message code ABORT $%02X", data); -#endif // DISK_LOG - BusFree(); - return; - } - - // BUS DEVICE RESET - if (data == 0x0C) { -#if defined(DISK_LOG) - Log(Log::Normal, - "Message code BUS DEVICE RESET $%02X", data); -#endif // DISK_LOG - scsi.syncoffset = 0; - BusFree(); - return; - } - - // IDENTIFY - if (data >= 0x80) { -#if defined(DISK_LOG) - Log(Log::Normal, - "Message code IDENTIFY $%02X", data); -#endif // DISK_LOG - } - - // Extended Message - if (data == 0x01) { -#if defined(DISK_LOG) - Log(Log::Normal, - "Message code EXTENDED MESSAGE $%02X", data); -#endif // DISK_LOG - - // Check only when synchronous transfer is possible - if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) { - ctrl.length = 1; - ctrl.blocks = 1; - ctrl.buffer[0] = 0x07; - MsgIn(); - return; - } - - // Transfer period factor (limited to 50 x 4 = 200ns) - scsi.syncperiod = scsi.msb[i + 3]; - if (scsi.syncperiod > 50) { - scsi.syncoffset = 50; - } - - // REQ/ACK offset(limited to 16) - scsi.syncoffset = scsi.msb[i + 4]; - if (scsi.syncoffset > 16) { - scsi.syncoffset = 16; - } - - // STDR response message generation - ctrl.length = 5; - ctrl.blocks = 1; - ctrl.buffer[0] = 0x01; - ctrl.buffer[1] = 0x03; - ctrl.buffer[2] = 0x01; - ctrl.buffer[3] = (BYTE)scsi.syncperiod; - ctrl.buffer[4] = (BYTE)scsi.syncoffset; - MsgIn(); - return; - } - - // next - i++; - } - } - - // Initialize ATN message reception status - scsi.atnmsg = FALSE; - - // Command phase - Command(); - break; - - // Data out phase - case BUS::dataout: - // Flush unit - FlushUnit(); - - // status phase - Status(); - break; - - // Other (impossible) - default: - ASSERT(FALSE); - break; - } -} - -//--------------------------------------------------------------------------- -// -// Transfer MSG -// -//--------------------------------------------------------------------------- -BOOL FASTCALL SCSIDEV::XferMsg(DWORD msg) -{ - ASSERT(this); - ASSERT(ctrl.phase == BUS::msgout); - - // Save message out data - if (scsi.atnmsg) { - scsi.msb[scsi.msc] = (BYTE)msg; - scsi.msc++; - scsi.msc %= 256; - } - - return TRUE; -} diff --git a/src/raspberrypi/disk.h b/src/raspberrypi/disk.h deleted file mode 100644 index 10593e63..00000000 --- a/src/raspberrypi/disk.h +++ /dev/null @@ -1,1148 +0,0 @@ -//--------------------------------------------------------------------------- -// -// X68000 EMULATOR "XM6" -// -// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) -// Copyright (C) 2014-2020 GIMONS -// -// XM6i -// Copyright (C) 2010-2015 isaki@NetBSD.org -// -// Imported sava's Anex86/T98Next image and MO format support patch. -// Comments translated to english by akuker. -// -// [ Disk ] -// -//--------------------------------------------------------------------------- - -#if !defined(disk_h) -#define disk_h - -#include "log.h" -#include "scsi.h" - -//--------------------------------------------------------------------------- -// -// Error definition (sense code returned by REQUEST SENSE) -// -// MSB Reserved (0x00) -// Sense Key -// Additional Sense Code (ASC) -// LSB Additional Sense Code Qualifier(ASCQ) -// -//--------------------------------------------------------------------------- -#define DISK_NOERROR 0x00000000 // NO ADDITIONAL SENSE INFO. -#define DISK_DEVRESET 0x00062900 // POWER ON OR RESET OCCURED -#define DISK_NOTREADY 0x00023a00 // MEDIUM NOT PRESENT -#define DISK_ATTENTION 0x00062800 // MEDIUM MAY HAVE CHANGED -#define DISK_PREVENT 0x00045302 // MEDIUM REMOVAL PREVENTED -#define DISK_READFAULT 0x00031100 // UNRECOVERED READ ERROR -#define DISK_WRITEFAULT 0x00030300 // PERIPHERAL DEVICE WRITE FAULT -#define DISK_WRITEPROTECT 0x00042700 // WRITE PROTECTED -#define DISK_MISCOMPARE 0x000e1d00 // MISCOMPARE DURING VERIFY -#define DISK_INVALIDCMD 0x00052000 // INVALID COMMAND OPERATION CODE -#define DISK_INVALIDLBA 0x00052100 // LOGICAL BLOCK ADDR. OUT OF RANGE -#define DISK_INVALIDCDB 0x00052400 // INVALID FIELD IN CDB -#define DISK_INVALIDLUN 0x00052500 // LOGICAL UNIT NOT SUPPORTED -#define DISK_INVALIDPRM 0x00052600 // INVALID FIELD IN PARAMETER LIST -#define DISK_INVALIDMSG 0x00054900 // INVALID MESSAGE ERROR -#define DISK_PARAMLEN 0x00051a00 // PARAMETERS LIST LENGTH ERROR -#define DISK_PARAMNOT 0x00052601 // PARAMETERS NOT SUPPORTED -#define DISK_PARAMVALUE 0x00052602 // PARAMETERS VALUE INVALID -#define DISK_PARAMSAVE 0x00053900 // SAVING PARAMETERS NOT SUPPORTED -#define DISK_NODEFECT 0x00010000 // DEFECT LIST NOT FOUND - -#if 0 -#define DISK_AUDIOPROGRESS 0x00??0011 // AUDIO PLAY IN PROGRESS -#define DISK_AUDIOPAUSED 0x00??0012 // AUDIO PLAY PAUSED -#define DISK_AUDIOSTOPPED 0x00??0014 // AUDIO PLAY STOPPED DUE TO ERROR -#define DISK_AUDIOCOMPLETE 0x00??0013 // AUDIO PLAY SUCCESSFULLY COMPLETED -#endif - -//=========================================================================== -// -// Disk Track -// -//=========================================================================== -class DiskTrack -{ -public: - // Internal data definition - typedef struct { - int track; // Track Number - int size; // Sector Size(8 or 9) - int sectors; // Number of sectors(<=0x100) - DWORD length; // Data buffer length - BYTE *buffer; // Data buffer - BOOL init; // Is it initilized? - BOOL changed; // Changed flag - DWORD maplen; // Changed map length - BOOL *changemap; // Changed map - BOOL raw; // RAW mode flag - off64_t imgoffset; // Offset to actual data - } disktrk_t; - -public: - // Basic Functions - DiskTrack(); - // Constructor - virtual ~DiskTrack(); - // Destructor - void FASTCALL Init(int track, int size, int sectors, BOOL raw = FALSE, - off64_t imgoff = 0); - // Initialization - BOOL FASTCALL Load(const Filepath& path); - // Load - BOOL FASTCALL Save(const Filepath& path); - // Save - - // Read / Write - BOOL FASTCALL Read(BYTE *buf, int sec) const; - // Sector Read - BOOL FASTCALL Write(const BYTE *buf, int sec); - // Sector Write - - // Other - int FASTCALL GetTrack() const { return dt.track; } - // Get track - BOOL FASTCALL IsChanged() const { return dt.changed; } - // Changed flag check - -private: - // Internal data - disktrk_t dt; - // Internal data -}; - -//=========================================================================== -// -// Disk Cache -// -//=========================================================================== -class DiskCache -{ -public: - // Internal data definition - typedef struct { - DiskTrack *disktrk; // Disk Track - DWORD serial; // Serial - } cache_t; - - // Number of caches - enum { - CacheMax = 16 // Number of tracks to cache - }; - -public: - // Basic Functions - DiskCache(const Filepath& path, int size, int blocks, - off64_t imgoff = 0); - // Constructor - virtual ~DiskCache(); - // Destructor - void FASTCALL SetRawMode(BOOL raw); - // CD-ROM raw mode setting - - // Access - BOOL FASTCALL Save(); - // Save and release all - BOOL FASTCALL Read(BYTE *buf, int block); - // Sector Read - BOOL FASTCALL Write(const BYTE *buf, int block); - // Sector Write - BOOL FASTCALL GetCache(int index, int& track, DWORD& serial) const; - // Get cache information - -private: - // Internal Management - void FASTCALL Clear(); - // Clear all tracks - DiskTrack* FASTCALL Assign(int track); - // Load track - BOOL FASTCALL Load(int index, int track, DiskTrack *disktrk = NULL); - // Load track - void FASTCALL Update(); - // Update serial number - - // Internal data - cache_t cache[CacheMax]; - // Cache management - DWORD serial; - // Last serial number - Filepath sec_path; - // Path - int sec_size; - // Sector size (8 or 9 or 11) - int sec_blocks; - // Blocks per sector - BOOL cd_raw; - // CD-ROM RAW mode - off64_t imgoffset; - // Offset to actual data -}; - -//=========================================================================== -// -// Disk -// -//=========================================================================== -class Disk -{ -public: - // Internal data structure - typedef struct { - DWORD id; // Media ID - BOOL ready; // Valid Disk - BOOL writep; // Write protected - BOOL readonly; // Read only - BOOL removable; // Removable - BOOL lock; // Locked - BOOL attn; // Attention - BOOL reset; // Reset - int size; // Sector Size - DWORD blocks; // Total number of sectors - DWORD lun; // LUN - DWORD code; // Status code - DiskCache *dcache; // Disk cache - off64_t imgoffset; // Offset to actual data - } disk_t; - -public: - // Basic Functions - Disk(); - // Constructor - virtual ~Disk(); - // Destructor - virtual void FASTCALL Reset(); - // Device Reset -#ifndef RASCSI - virtual BOOL FASTCALL Save(Fileio *fio, int ver); - // Save - virtual BOOL FASTCALL Load(Fileio *fio, int ver); - // Load -#endif // RASCSI - - // ID - DWORD FASTCALL GetID() const { return disk.id; } - // Get media ID - BOOL FASTCALL IsNULL() const; - // NULL check - BOOL FASTCALL IsSASI() const; - // SASI Check - - // Media Operations - virtual BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); - // Open - void FASTCALL GetPath(Filepath& path) const; - // Get the path - void FASTCALL Eject(BOOL force); - // Eject - BOOL FASTCALL IsReady() const { return disk.ready; } - // Ready check - void FASTCALL WriteP(BOOL flag); - // Set Write Protect flag - BOOL FASTCALL IsWriteP() const { return disk.writep; } - // Get write protect flag - BOOL FASTCALL IsReadOnly() const { return disk.readonly; } - // Get read only flag - BOOL FASTCALL IsRemovable() const { return disk.removable; } - // Get is removable flag - BOOL FASTCALL IsLocked() const { return disk.lock; } - // Get locked status - BOOL FASTCALL IsAttn() const { return disk.attn; } - // Get attention flag - BOOL FASTCALL Flush(); - // Flush the cache - void FASTCALL GetDisk(disk_t *buffer) const; - // Get the internal data struct - - // Properties - void FASTCALL SetLUN(DWORD lun) { disk.lun = lun; } - // LUN set - DWORD FASTCALL GetLUN() { return disk.lun; } - // LUN get - // commands - virtual int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - virtual int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf); - // REQUEST SENSE command - int FASTCALL SelectCheck(const DWORD *cdb); - // SELECT check - int FASTCALL SelectCheck10(const DWORD *cdb); - // SELECT(10) check - virtual BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); - // MODE SELECT command - virtual int FASTCALL ModeSense(const DWORD *cdb, BYTE *buf); - // MODE SENSE command - virtual int FASTCALL ModeSense10(const DWORD *cdb, BYTE *buf); - // MODE SENSE(10) command - int FASTCALL ReadDefectData10(const DWORD *cdb, BYTE *buf); - // READ DEFECT DATA(10) command - virtual BOOL FASTCALL TestUnitReady(const DWORD *cdb); - // TEST UNIT READY command - BOOL FASTCALL Rezero(const DWORD *cdb); - // REZERO command - BOOL FASTCALL Format(const DWORD *cdb); - // FORMAT UNIT command - BOOL FASTCALL Reassign(const DWORD *cdb); - // REASSIGN UNIT command - virtual int FASTCALL Read(BYTE *buf, DWORD block); - // READ command - int FASTCALL WriteCheck(DWORD block); - // WRITE check - BOOL FASTCALL Write(const BYTE *buf, DWORD block); - // WRITE command - BOOL FASTCALL Seek(const DWORD *cdb); - // SEEK command - BOOL FASTCALL Assign(const DWORD *cdb); - // ASSIGN command - BOOL FASTCALL Specify(const DWORD *cdb); - // SPECIFY command - BOOL FASTCALL StartStop(const DWORD *cdb); - // START STOP UNIT command - BOOL FASTCALL SendDiag(const DWORD *cdb); - // SEND DIAGNOSTIC command - BOOL FASTCALL Removal(const DWORD *cdb); - // PREVENT/ALLOW MEDIUM REMOVAL command - int FASTCALL ReadCapacity(const DWORD *cdb, BYTE *buf); - // READ CAPACITY command - BOOL FASTCALL Verify(const DWORD *cdb); - // VERIFY command - virtual int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf); - // READ TOC command - virtual BOOL FASTCALL PlayAudio(const DWORD *cdb); - // PLAY AUDIO command - virtual BOOL FASTCALL PlayAudioMSF(const DWORD *cdb); - // PLAY AUDIO MSF command - virtual BOOL FASTCALL PlayAudioTrack(const DWORD *cdb); - // PLAY AUDIO TRACK command - void FASTCALL InvalidCmd() { disk.code = DISK_INVALIDCMD; } - // Unsupported command - - // Other - BOOL IsCacheWB() { return cache_wb; } - // Get cache writeback mode - void SetCacheWB(BOOL enable) { cache_wb = enable; } - // Set cache writeback mode - -protected: - // Internal processing - virtual int FASTCALL AddError(BOOL change, BYTE *buf); - // Add error - virtual int FASTCALL AddFormat(BOOL change, BYTE *buf); - // Add format - virtual int FASTCALL AddDrive(BOOL change, BYTE *buf); - // Add drive - int FASTCALL AddOpt(BOOL change, BYTE *buf); - // Add optical - int FASTCALL AddCache(BOOL change, BYTE *buf); - // Add cache - int FASTCALL AddCDROM(BOOL change, BYTE *buf); - // Add CD-ROM - int FASTCALL AddCDDA(BOOL change, BYTE *buf); - // Add CD_DA - virtual int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); - // Add vendor special info - BOOL FASTCALL CheckReady(); - // Check if ready - - // Internal data - disk_t disk; - // Internal disk data - Filepath diskpath; - // File path (for GetPath) - BOOL cache_wb; - // Cache mode -}; - -//=========================================================================== -// -// SASI Hard Disk -// -//=========================================================================== -class SASIHD : public Disk -{ -public: - // Basic Functions - SASIHD(); - // Constructor - void FASTCALL Reset(); - // Reset - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); - // Open - // commands - int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf); - // REQUEST SENSE command -}; - -//=========================================================================== -// -// SCSI Hard Disk -// -//=========================================================================== -class SCSIHD : public Disk -{ -public: - // Basic Functions - SCSIHD(); - // Constructor - void FASTCALL Reset(); - // Reset - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); - // Open - - // commands - int FASTCALL Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); - // MODE SELECT(6) command -}; - -//=========================================================================== -// -// SCSI hard disk (PC-9801-55 NEC genuine /Anex86/T98Next) -// -//=========================================================================== -class SCSIHD_NEC : public SCSIHD -{ -public: - // Basic Functions - SCSIHD_NEC(); - // Constructor - - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); - // Open - - // commands - int FASTCALL Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - - // Internal processing - int FASTCALL AddError(BOOL change, BYTE *buf); - // Add error - int FASTCALL AddFormat(BOOL change, BYTE *buf); - // Add format - int FASTCALL AddDrive(BOOL change, BYTE *buf); - // Add drive - -private: - int cylinders; - // Number of cylinders - int heads; - // Number of heads - int sectors; - // Number of sectors - int sectorsize; - // Sector size - off64_t imgoffset; - // Image offset - off64_t imgsize; - // Image size -}; - -//=========================================================================== -// -// SCSI Hard Disk(Genuine Apple Macintosh) -// -//=========================================================================== -class SCSIHD_APPLE : public SCSIHD -{ -public: - // Basic Functions - SCSIHD_APPLE(); - // Constructor - // commands - int FASTCALL Inquiry( - const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - - // Internal processing - int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); - // Add vendor special page -}; - -//=========================================================================== -// -// SCSI magneto-optical disk -// -//=========================================================================== -class SCSIMO : public Disk -{ -public: - // Basic Functions - SCSIMO(); - // Constructor - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); - // Open -#ifndef RASCSI - BOOL FASTCALL Load(Fileio *fio, int ver); - // Load -#endif // RASCSI - - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); - // MODE SELECT(6) command - - // Internal processing - int FASTCALL AddVendor(int page, BOOL change, BYTE *buf); - // Add vendor special page -}; - -//--------------------------------------------------------------------------- -// -// Class precedence definition -// -//--------------------------------------------------------------------------- -class SCSICD; - -//=========================================================================== -// -// CD-ROM Track -// -//=========================================================================== -class CDTrack -{ -public: - // Basic Functions - CDTrack(SCSICD *scsicd); - // Constructor - virtual ~CDTrack(); - // Destructor - BOOL FASTCALL Init(int track, DWORD first, DWORD last); - // Initialization - - // Properties - void FASTCALL SetPath(BOOL cdda, const Filepath& path); - // Set the path - void FASTCALL GetPath(Filepath& path) const; - // Get the path - void FASTCALL AddIndex(int index, DWORD lba); - // Add index - DWORD FASTCALL GetFirst() const; - // Get the start LBA - DWORD FASTCALL GetLast() const; - // Get the last LBA - DWORD FASTCALL GetBlocks() const; - // Get the number of blocks - int FASTCALL GetTrackNo() const; - // Get the track number - BOOL FASTCALL IsValid(DWORD lba) const; - // Is this a valid LBA? - BOOL FASTCALL IsAudio() const; - // Is this an audio track? - -private: - SCSICD *cdrom; - // Parent device - BOOL valid; - // Valid track - int track_no; - // Track number - DWORD first_lba; - // First LBA - DWORD last_lba; - // Last LBA - BOOL audio; - // Audio track flag - BOOL raw; - // RAW data flag - Filepath imgpath; - // Image file path -}; - -//=========================================================================== -// -// CD-DA Buffer -// -//=========================================================================== -class CDDABuf -{ -public: - // Basic Functions - CDDABuf(); - // Constructor - virtual ~CDDABuf(); - // Destructor -#if 0 - BOOL Init(); - // Initialization - BOOL FASTCALL Load(const Filepath& path); - // Load - BOOL FASTCALL Save(const Filepath& path); - // Save - - // API - void FASTCALL Clear(); - // Clear the buffer - BOOL FASTCALL Open(Filepath& path); - // File specification - BOOL FASTCALL GetBuf(DWORD *buffer, int frames); - // Get the buffer - BOOL FASTCALL IsValid(); - // Check if Valid - BOOL FASTCALL ReadReq(); - // Read Request - BOOL FASTCALL IsEnd() const; - // Finish check - -private: - Filepath wavepath; - // Wave path - BOOL valid; - // Open result (is it valid?) - DWORD *buf; - // Data buffer - DWORD read; - // Read pointer - DWORD write; - // Write pointer - DWORD num; - // Valid number of data - DWORD rest; - // Remaining file size -#endif -}; - -//=========================================================================== -// -// SCSI CD-ROM -// -//=========================================================================== -class SCSICD : public Disk -{ -public: - // Number of tracks - enum { - TrackMax = 96 // Maximum number of tracks - }; - -public: - // Basic Functions - SCSICD(); - // Constructor - virtual ~SCSICD(); - // Destructor - BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); - // Open -#ifndef RASCSI - BOOL FASTCALL Load(Fileio *fio, int ver); - // Load -#endif // RASCSI - - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - int FASTCALL Read(BYTE *buf, DWORD block); - // READ command - int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf); - // READ TOC command - BOOL FASTCALL PlayAudio(const DWORD *cdb); - // PLAY AUDIO command - BOOL FASTCALL PlayAudioMSF(const DWORD *cdb); - // PLAY AUDIO MSF command - BOOL FASTCALL PlayAudioTrack(const DWORD *cdb); - // PLAY AUDIO TRACK command - - // CD-DA - BOOL FASTCALL NextFrame(); - // Frame notification - void FASTCALL GetBuf(DWORD *buffer, int samples, DWORD rate); - // Get CD-DA buffer - - // LBA-MSF変換 - void FASTCALL LBAtoMSF(DWORD lba, BYTE *msf) const; - // LBA→MSF conversion - DWORD FASTCALL MSFtoLBA(const BYTE *msf) const; - // MSF→LBA conversion - -private: - // Open - BOOL FASTCALL OpenCue(const Filepath& path); - // Open(CUE) - BOOL FASTCALL OpenIso(const Filepath& path); - // Open(ISO) - BOOL FASTCALL OpenPhysical(const Filepath& path); - // Open(Physical) - BOOL rawfile; - // RAW flag - - // Track management - void FASTCALL ClearTrack(); - // Clear the track - int FASTCALL SearchTrack(DWORD lba) const; - // Track search - CDTrack* track[TrackMax]; - // Track opbject references - int tracks; - // Effective number of track objects - int dataindex; - // Current data track - int audioindex; - // Current audio track - - int frame; - // Frame number - -#if 0 - CDDABuf da_buf; - // CD-DA buffer - int da_num; - // Number of CD-DA tracks - int da_cur; - // CD-DA current track - int da_next; - // CD-DA next track - BOOL da_req; - // CD-DA data request -#endif -}; - -//=========================================================================== -// -// SCSI Host Bridge -// -//=========================================================================== -#if defined(RASCSI) && !defined(BAREMETAL) -class CTapDriver; -#endif // RASCSI && !BAREMETAL -class CFileSys; -class SCSIBR : public Disk -{ -public: - // Basic Functions - SCSIBR(); - // Constructor - virtual ~SCSIBR(); - // Destructor - - // commands - int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); - // INQUIRY command - BOOL FASTCALL TestUnitReady(const DWORD *cdb); - // TEST UNIT READY command - int FASTCALL GetMessage10(const DWORD *cdb, BYTE *buf); - // GET MESSAGE10 command - BOOL FASTCALL SendMessage10(const DWORD *cdb, BYTE *buf); - // SEND MESSAGE10 command - -private: -#if defined(RASCSI) && !defined(BAREMETAL) - int FASTCALL GetMacAddr(BYTE *buf); - // Get MAC address - void FASTCALL SetMacAddr(BYTE *buf); - // Set MAC address - void FASTCALL ReceivePacket(); - // Receive a packet - void FASTCALL GetPacketBuf(BYTE *buf); - // Get a packet - void FASTCALL SendPacket(BYTE *buf, int len); - // Send a packet - - CTapDriver *tap; - // TAP driver - BOOL m_bTapEnable; - // TAP valid flag - BYTE mac_addr[6]; - // MAC Addres - int packet_len; - // Receive packet size - BYTE packet_buf[0x1000]; - // Receive packet buffer - BOOL packet_enable; - // Received packet valid -#endif // RASCSI && !BAREMETAL - - int FASTCALL ReadFsResult(BYTE *buf); - // Read filesystem (result code) - int FASTCALL ReadFsOut(BYTE *buf); - // Read filesystem (return data) - int FASTCALL ReadFsOpt(BYTE *buf); - // Read file system (optional data) - void FASTCALL WriteFs(int func, BYTE *buf); - // File system write (execute) - void FASTCALL WriteFsOpt(BYTE *buf, int len); - // File system write (optional data) - // Command handlers - void FASTCALL FS_InitDevice(BYTE *buf); - // $40 - boot - void FASTCALL FS_CheckDir(BYTE *buf); - // $41 - directory check - void FASTCALL FS_MakeDir(BYTE *buf); - // $42 - create directory - void FASTCALL FS_RemoveDir(BYTE *buf); - // $43 - delete directory - void FASTCALL FS_Rename(BYTE *buf); - // $44 - change filename - void FASTCALL FS_Delete(BYTE *buf); - // $45 - delete file - void FASTCALL FS_Attribute(BYTE *buf); - // $46 - get/set file attributes - void FASTCALL FS_Files(BYTE *buf); - // $47 - file search - void FASTCALL FS_NFiles(BYTE *buf); - // $48 - find next file - void FASTCALL FS_Create(BYTE *buf); - // $49 - create file - void FASTCALL FS_Open(BYTE *buf); - // $4A - open file - void FASTCALL FS_Close(BYTE *buf); - // $4B - close file - void FASTCALL FS_Read(BYTE *buf); - // $4C - read file - void FASTCALL FS_Write(BYTE *buf); - // $4D - write file - void FASTCALL FS_Seek(BYTE *buf); - // $4E - seek file - void FASTCALL FS_TimeStamp(BYTE *buf); - // $4F - get/set file time - void FASTCALL FS_GetCapacity(BYTE *buf); - // $50 - get capacity - void FASTCALL FS_CtrlDrive(BYTE *buf); - // $51 - drive status check/control - void FASTCALL FS_GetDPB(BYTE *buf); - // $52 - get DPB - void FASTCALL FS_DiskRead(BYTE *buf); - // $53 - read sector - void FASTCALL FS_DiskWrite(BYTE *buf); - // $54 - write sector - void FASTCALL FS_Ioctrl(BYTE *buf); - // $55 - IOCTRL - void FASTCALL FS_Flush(BYTE *buf); - // $56 - flush cache - void FASTCALL FS_CheckMedia(BYTE *buf); - // $57 - check media - void FASTCALL FS_Lock(BYTE *buf); - // $58 - get exclusive control - - CFileSys *fs; - // File system accessor - DWORD fsresult; - // File system access result code - BYTE fsout[0x800]; - // File system access result buffer - DWORD fsoutlen; - // File system access result buffer size - BYTE fsopt[0x1000000]; - // File system access buffer - DWORD fsoptlen; - // File system access buffer size -}; - -//=========================================================================== -// -// SASI Controller -// -//=========================================================================== -class SASIDEV -{ -public: - // Maximum number of logical units - enum { - UnitMax = 8 - }; - -#ifdef RASCSI - // For timing adjustments - enum { - min_exec_time_sasi = 100, // SASI BOOT/FORMAT 30:NG 35:OK - min_exec_time_scsi = 50 - }; -#endif // RASCSI - - // Internal data definition - typedef struct { - // 全般 - BUS::phase_t phase; // Transition phase - int id; // Controller ID (0-7) - BUS *bus; // Bus - - // commands - DWORD cmd[10]; // Command data - DWORD status; // Status data - DWORD message; // Message data - -#ifdef RASCSI - // Run - DWORD execstart; // Execution start time -#endif // RASCSI - - // Transfer - BYTE *buffer; // Transfer data buffer - int bufsize; // Transfer data buffer size - DWORD blocks; // Number of transfer block - DWORD next; // Next record - DWORD offset; // Transfer offset - DWORD length; // Transfer remaining length - - // Logical unit - Disk *unit[UnitMax]; - // Logical Unit - } ctrl_t; - -public: - // Basic Functions -#ifdef RASCSI - SASIDEV(); -#else - SASIDEV(Device *dev); -#endif //RASCSI - - // Constructor - virtual ~SASIDEV(); - // Destructor - virtual void FASTCALL Reset(); - // Device Reset -#ifndef RASCSI - virtual BOOL FASTCALL Save(Fileio *fio, int ver); - // Save - virtual BOOL FASTCALL Load(Fileio *fio, int ver); - // Load -#endif //RASCSI - - // External API - virtual BUS::phase_t FASTCALL Process(); - // Run - - // Connect - void FASTCALL Connect(int id, BUS *sbus); - // Controller connection - Disk* FASTCALL GetUnit(int no); - // Get logical unit - void FASTCALL SetUnit(int no, Disk *dev); - // Logical unit setting - BOOL FASTCALL HasUnit(); - // Has a valid logical unit - - // Other - BUS::phase_t FASTCALL GetPhase() {return ctrl.phase;} - // Get the phase -#ifdef DISK_LOG - // Function to get the current phase as a String. - void FASTCALL GetPhaseStr(char *str); -#endif - - int FASTCALL GetID() {return ctrl.id;} - // Get the ID - void FASTCALL GetCTRL(ctrl_t *buffer); - // Get the internal information - ctrl_t* FASTCALL GetWorkAddr() { return &ctrl; } - // Get the internal information address - virtual BOOL FASTCALL IsSASI() const {return TRUE;} - // SASI Check - virtual BOOL FASTCALL IsSCSI() const {return FALSE;} - // SCSI check - Disk* FASTCALL GetBusyUnit(); - // Get the busy unit - -protected: - // Phase processing - virtual void FASTCALL BusFree(); - // Bus free phase - virtual void FASTCALL Selection(); - // Selection phase - virtual void FASTCALL Command(); - // Command phase - virtual void FASTCALL Execute(); - // Execution phase - void FASTCALL Status(); - // Status phase - void FASTCALL MsgIn(); - // Message in phase - void FASTCALL DataIn(); - // Data in phase - void FASTCALL DataOut(); - // Data out phase - virtual void FASTCALL Error(); - // Common error handling - - // commands - void FASTCALL CmdTestUnitReady(); - // TEST UNIT READY command - void FASTCALL CmdRezero(); - // REZERO UNIT command - void FASTCALL CmdRequestSense(); - // REQUEST SENSE command - void FASTCALL CmdFormat(); - // FORMAT command - void FASTCALL CmdReassign(); - // REASSIGN BLOCKS command - void FASTCALL CmdRead6(); - // READ(6) command - void FASTCALL CmdWrite6(); - // WRITE(6) command - void FASTCALL CmdSeek6(); - // SEEK(6) command - void FASTCALL CmdAssign(); - // ASSIGN command - void FASTCALL CmdSpecify(); - // SPECIFY command - void FASTCALL CmdInvalid(); - // Unsupported command - - // データ転送 - virtual void FASTCALL Send(); - // Send data -#ifndef RASCSI - virtual void FASTCALL SendNext(); - // Continue sending data -#endif // RASCSI - virtual void FASTCALL Receive(); - // Receive data -#ifndef RASCSI - virtual void FASTCALL ReceiveNext(); - // Continue receiving data -#endif // RASCSI - BOOL FASTCALL XferIn(BYTE* buf); - // Data transfer IN - BOOL FASTCALL XferOut(BOOL cont); - // Data transfer OUT - - // Special operations - void FASTCALL FlushUnit(); - // Flush the logical unit - - // Log - void FASTCALL Log(Log::loglevel level, const char *format, ...); - // Log output - -protected: -#ifndef RASCSI - Device *host; - // Host device -#endif // RASCSI - - ctrl_t ctrl; - // Internal data -}; - -//=========================================================================== -// -// SCSI Device (Interits SASI device) -// -//=========================================================================== -class SCSIDEV : public SASIDEV -{ -public: - // Internal data definition - typedef struct { - // Synchronous transfer - BOOL syncenable; // Synchronous transfer possible - int syncperiod; // Synchronous transfer period - int syncoffset; // Synchronous transfer offset - int syncack; // Number of synchronous transfer ACKs - - // ATN message - BOOL atnmsg; - int msc; - BYTE msb[256]; - } scsi_t; - -public: - // Basic Functions -#ifdef RASCSI - SCSIDEV(); -#else - SCSIDEV(Device *dev); -#endif // RASCSI - // Constructor - - void FASTCALL Reset(); - // Device Reset - - // 外部API - BUS::phase_t FASTCALL Process(); - // Run - - void FASTCALL SyncTransfer(BOOL enable) { scsi.syncenable = enable; } - // Synchronouse transfer enable setting - - // Other - BOOL FASTCALL IsSASI() const {return FALSE;} - // SASI Check - BOOL FASTCALL IsSCSI() const {return TRUE;} - // SCSI check - -private: - // Phase - void FASTCALL BusFree(); - // Bus free phase - void FASTCALL Selection(); - // Selection phase - void FASTCALL Execute(); - // Execution phase - void FASTCALL MsgOut(); - // Message out phase - void FASTCALL Error(); - // Common erorr handling - - // commands - void FASTCALL CmdInquiry(); - // INQUIRY command - void FASTCALL CmdModeSelect(); - // MODE SELECT command - void FASTCALL CmdModeSense(); - // MODE SENSE command - void FASTCALL CmdStartStop(); - // START STOP UNIT command - void FASTCALL CmdSendDiag(); - // SEND DIAGNOSTIC command - void FASTCALL CmdRemoval(); - // PREVENT/ALLOW MEDIUM REMOVAL command - void FASTCALL CmdReadCapacity(); - // READ CAPACITY command - void FASTCALL CmdRead10(); - // READ(10) command - void FASTCALL CmdWrite10(); - // WRITE(10) command - void FASTCALL CmdSeek10(); - // SEEK(10) command - void FASTCALL CmdVerify(); - // VERIFY command - void FASTCALL CmdSynchronizeCache(); - // SYNCHRONIZE CACHE command - void FASTCALL CmdReadDefectData10(); - // READ DEFECT DATA(10) command - void FASTCALL CmdReadToc(); - // READ TOC command - void FASTCALL CmdPlayAudio10(); - // PLAY AUDIO(10) command - void FASTCALL CmdPlayAudioMSF(); - // PLAY AUDIO MSF command - void FASTCALL CmdPlayAudioTrack(); - // PLAY AUDIO TRACK INDEX command - void FASTCALL CmdModeSelect10(); - // MODE SELECT(10) command - void FASTCALL CmdModeSense10(); - // MODE SENSE(10) command - void FASTCALL CmdGetMessage10(); - // GET MESSAGE(10) command - void FASTCALL CmdSendMessage10(); - // SEND MESSAGE(10) command - - // データ転送 - void FASTCALL Send(); - // Send data -#ifndef RASCSI - void FASTCALL SendNext(); - // Continue sending data -#endif // RASCSI - void FASTCALL Receive(); - // Receive data -#ifndef RASCSI - void FASTCALL ReceiveNext(); - // Continue receiving data -#endif // RASCSI - BOOL FASTCALL XferMsg(DWORD msg); - // Data transfer message - - scsi_t scsi; - // Internal data -}; - - - -#endif // disk_h diff --git a/src/raspberrypi/fileio.h b/src/raspberrypi/fileio.h index fd9c5ce4..da574712 100644 --- a/src/raspberrypi/fileio.h +++ b/src/raspberrypi/fileio.h @@ -11,6 +11,8 @@ #if !defined(fileio_h) #define fileio_h +#include "filepath.h" + #ifdef BAREMETAL #include "ff.h" #endif // BAREMETAL diff --git a/src/raspberrypi/filepath.h b/src/raspberrypi/filepath.h index b7a1ced9..93ef98a1 100644 --- a/src/raspberrypi/filepath.h +++ b/src/raspberrypi/filepath.h @@ -11,6 +11,8 @@ #if !defined(filepath_h) #define filepath_h +class Fileio; + //--------------------------------------------------------------------------- // // 定数定義 diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 3c24eee5..6e088dc8 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -13,9 +13,19 @@ #include "xm6.h" #include "filepath.h" #include "fileio.h" -#include "disk.h" +#include "devices/disk.h" +#include "devices/sasihd.h" +#include "devices/scsihd.h" +#include "devices/scsihd_apple.h" +#include "devices/scsihd_nec.h" +#include "devices/scsicd.h" +#include "devices/scsimo.h" +#include "devices/scsi_host_bridge.h" +#include "controllers/scsidev_ctrl.h" +#include "controllers/sasidev_ctrl.h" #include "gpiobus.h" + //--------------------------------------------------------------------------- // // Constant declarations