diff --git a/src/raspberrypi/.gitignore b/src/raspberrypi/.gitignore new file mode 100644 index 00000000..02c03f1e --- /dev/null +++ b/src/raspberrypi/.gitignore @@ -0,0 +1,2 @@ +*.o +*.bak diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index f2adee5f..96ea2bc0 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -1,8 +1,13 @@ +.DEFAULT_GOAL: all + CC = gcc -CFLAGS = -DDISK_LOG -O0 -g -Wall CXX = g++ + +CFLAGS = -DDISK_LOG -O0 -g -Wall CXXFLAGS = -DDISK_LOG -O0 -g -Wall + + # If its not specified, build for STANDARD configuration CONNECT_TYPE ?= STANDARD @@ -15,9 +20,13 @@ RASCSI = rascsi RASCTL = rasctl RASDUMP = rasdump SASIDUMP = sasidump +SCSIMON = scsimon +#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) $(SCSIMON) -BIN_ALL = $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) SRC_RASCSI = \ rascsi.cpp \ @@ -46,16 +55,29 @@ SRC_SASIDUMP = \ filepath.cpp \ fileio.cpp +SRC_SCSIMON = \ + scsimon.cpp \ + scsi.cpp \ + disk.cpp \ + gpiobus.cpp \ + ctapdriver.cpp \ + cfilesystem.cpp \ + filepath.cpp \ + fileio.cpp \ + scsimondev.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_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) +OBJ_SCSIMON := $(SRC_SCSIMON:%.cpp=%.o) +OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@ ALL: $(BIN_ALL) +all: $(BIN_ALL) $(RASCSI): $(OBJ_RASCSI) $(CXX) -o $@ $(OBJ_RASCSI) -lpthread @@ -69,8 +91,14 @@ $(RASDUMP): $(OBJ_RASDUMP) $(SASIDUMP): $(OBJ_SASIDUMP) $(CXX) -o $@ $(OBJ_SASIDUMP) +$(SCSIMON): $(OBJ_SCSIMON) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread + clean: rm -f $(OBJ_ALL) $(BIN_ALL) run: sudo ./$(RASCSI) -ID1 /home/pi/HARDDISK.HDA -ID6 /home/pi/marathon.iso + +.PHONY: Debug +Debug: scsimon diff --git a/src/raspberrypi/disk.cpp b/src/raspberrypi/disk.cpp index 217e6d1c..48cb38d3 100644 --- a/src/raspberrypi/disk.cpp +++ b/src/raspberrypi/disk.cpp @@ -4534,6 +4534,7 @@ void FASTCALL SCSICD::GetBuf( ASSERT(this); } + //=========================================================================== // // SCSI Host Bridge @@ -6294,6 +6295,7 @@ BUS::phase_t FASTCALL SASIDEV::Process() // 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) @@ -6373,7 +6375,7 @@ void FASTCALL SASIDEV::BusFree() // Phase Setting ctrl.phase = BUS::busfree; - // 信号線 + Set Signal lines ctrl.bus->SetREQ(FALSE); ctrl.bus->SetMSG(FALSE); ctrl.bus->SetCD(FALSE); @@ -6666,8 +6668,8 @@ void FASTCALL SASIDEV::Status() #ifndef RASCSI // Request status - ctrl.bus->SetDAT(ctrl.buffer[0]); - ctrl.bus->SetREQ(TRUE); +// ctrl.bus->SetDAT(ctrl.buffer[0]); +// ctrl.bus->SetREQ(TRUE); #if defined(DISK_LOG) Log(Log::Normal, "Status Phase $%02X", ctrl.status); @@ -8026,6 +8028,7 @@ void FASTCALL SCSIDEV::Reset() BUS::phase_t FASTCALL SCSIDEV::Process() { ASSERT(this); + printf("SCSIDEV::Process() %d\n", ctrl.id); // Do nothing if not connected if (ctrl.id < 0 || ctrl.bus == NULL) { diff --git a/src/raspberrypi/disk.h b/src/raspberrypi/disk.h index 69dc47a4..0299d3ca 100644 --- a/src/raspberrypi/disk.h +++ b/src/raspberrypi/disk.h @@ -229,6 +229,8 @@ public: // NULL check BOOL FASTCALL IsSASI() const; // SASI Check + virtual BOOL FASTCALL IsMonitor() const {return FALSE;} + // Check if this is a monitor device // Media Operations virtual BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE); @@ -933,6 +935,8 @@ public: // SASI Check virtual BOOL FASTCALL IsSCSI() const {return FALSE;} // SCSI check + virtual BOOL FASTCALL IsMonitor() const {return FALSE;} + // Check to see if this is a monitor device Disk* FASTCALL GetBusyUnit(); // Get the busy unit @@ -1141,4 +1145,6 @@ private: // Internal data }; + + #endif // disk_h diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index e0b8386a..a8cd7a36 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -65,7 +65,7 @@ DWORD bcm_host_get_peripheral_address(void) char buf[1024]; size_t len = sizeof(buf); DWORD address; - + if (sysctlbyname("hw.model", buf, &len, NULL, 0) || strstr(buf, "ARM1176JZ-S") != buf) { // Failed to get CPU model || Not BCM2835 @@ -88,7 +88,7 @@ extern uint32_t RPi_IO_Base_Addr; // Core frequency extern uint32_t RPi_Core_Freq; -#ifdef USE_SEL_EVENT_ENABLE +#ifdef USE_SEL_EVENT_ENABLE //--------------------------------------------------------------------------- // // Interrupt control function @@ -169,6 +169,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) // Open /dev/mem fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { + printf("Error: Unable to open /dev/mem. Are you running as root?\n"); return FALSE; } @@ -274,6 +275,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) PinConfig(PIN_DTD, GPIO_OUTPUT); // Set the ENABLE signal + // This is used to show that the application is running PinSetSignal(PIN_ENB, ENB_OFF); PinConfig(PIN_ENB, GPIO_OUTPUT); @@ -373,6 +375,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) MakeTable(); // Finally, enable ENABLE + // Show the user that this app is running SetControl(PIN_ENB, ENB_ON); return TRUE; @@ -518,7 +521,7 @@ DWORD FASTCALL GPIOBUS::Aquire() // Invert if negative logic (internal processing is unified to positive logic) signals = ~signals; #endif // SIGNAL_CONTROL_MODE - + return signals; } @@ -549,9 +552,15 @@ BOOL FASTCALL GPIOBUS::GetBSY() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetBSY(BOOL ast) { - // Set BSY signal - SetSignal(PIN_BSY, ast); - + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET BSY IN MONITOR MODE"); + } + else + { + // Set BSY signal + SetSignal(PIN_BSY, ast); + } if (actmode == TARGET) { if (ast) { // Turn on ACTIVE signal @@ -603,8 +612,14 @@ void FASTCALL GPIOBUS::SetSEL(BOOL ast) SetControl(PIN_ACT, ACT_ON); } - // Set SEL signal - SetSignal(PIN_SEL, ast); + if (actmode != MONITOR) + { + // Set SEL signal + SetSignal(PIN_SEL, ast); + } + else{ + printf("WARNING!!! SOMEONE TRIED TO SET SEL IN MONITOR MODE"); +} } //--------------------------------------------------------------------------- @@ -624,7 +639,14 @@ BOOL FASTCALL GPIOBUS::GetATN() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetATN(BOOL ast) { - SetSignal(PIN_ATN, ast); + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET ATN IN MONITOR MODE"); + } + else + { + SetSignal(PIN_ATN, ast); + } } //--------------------------------------------------------------------------- @@ -644,7 +666,14 @@ BOOL FASTCALL GPIOBUS::GetACK() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetACK(BOOL ast) { + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET ACK IN MONITOR MODE"); + } + else + { SetSignal(PIN_ACK, ast); + } } //--------------------------------------------------------------------------- @@ -664,7 +693,14 @@ BOOL FASTCALL GPIOBUS::GetRST() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetRST(BOOL ast) { + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET RST IN MONITOR MODE"); + } + else + { SetSignal(PIN_RST, ast); + } } //--------------------------------------------------------------------------- @@ -684,7 +720,13 @@ BOOL FASTCALL GPIOBUS::GetMSG() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetMSG(BOOL ast) { - SetSignal(PIN_MSG, ast); + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET MSG IN MONITOR MODE"); + } + else{ + SetSignal(PIN_MSG, ast); + } } //--------------------------------------------------------------------------- @@ -704,7 +746,14 @@ BOOL FASTCALL GPIOBUS::GetCD() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetCD(BOOL ast) { - SetSignal(PIN_CD, ast); + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET CD IN MONITOR MODE"); + } + else + { + SetSignal(PIN_CD, ast); + } } //--------------------------------------------------------------------------- @@ -754,6 +803,21 @@ BOOL FASTCALL GPIOBUS::GetIO() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetIO(BOOL ast) { + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET IO IN MONITOR MODE"); + SetControl(PIN_DTD, DTD_IN); + SetMode(PIN_DT0, IN); + SetMode(PIN_DT1, IN); + SetMode(PIN_DT2, IN); + SetMode(PIN_DT3, IN); + SetMode(PIN_DT4, IN); + SetMode(PIN_DT5, IN); + SetMode(PIN_DT6, IN); + SetMode(PIN_DT7, IN); + SetMode(PIN_DP, IN); + } + SetSignal(PIN_IO, ast); if (actmode == TARGET) { @@ -802,6 +866,12 @@ BOOL FASTCALL GPIOBUS::GetREQ() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetREQ(BOOL ast) { + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET REQ IN MONITOR MODE"); + return; + } + SetSignal(PIN_REQ, ast); } @@ -835,6 +905,14 @@ BYTE FASTCALL GPIOBUS::GetDAT() //--------------------------------------------------------------------------- void FASTCALL GPIOBUS::SetDAT(BYTE dat) { + + + if(actmode == MONITOR) + { + printf("WARNING!!! SOMEONE TRIED TO SET Data IN MONITOR MODE"); + return; + } + // Write to port #if SIGNAL_CONTROL_MODE == 0 DWORD fsel; @@ -1152,7 +1230,7 @@ int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count) } // Already waiting for REQ assertion - + // Assert the ACK signal SetSignal(PIN_ACK, ON); @@ -1396,7 +1474,7 @@ void FASTCALL GPIOBUS::SetMode(int pin, int mode) gpio[index] = data; gpfsel[index] = data; } - + //--------------------------------------------------------------------------- // // Get input signal value @@ -1406,7 +1484,7 @@ BOOL FASTCALL GPIOBUS::GetSignal(int pin) { return (signals >> pin) & 1; } - + //--------------------------------------------------------------------------- // // Set output signal value diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 4e55f007..dadf76ea 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -153,7 +153,7 @@ BOOL Init() // GPIOBUS creation bus = new GPIOBUS(); - + // GPIO Initialization if (!bus->Init()) { return FALSE; @@ -206,7 +206,7 @@ void Cleanup() // Cleanup the Bus bus->Cleanup(); - + // Discard the GPIOBUS object delete bus; @@ -306,7 +306,7 @@ void ListDevice(FILE *fp) FPRT(fp, "No device is installed.\n"); return; } - + FPRT(fp, "+----+----+------+-------------------------------------\n"); } @@ -862,7 +862,7 @@ static void *MonThread(void *param) { struct sched_param schedparam; struct sockaddr_in client; - socklen_t len; + socklen_t len; int fd; FILE *fp; char buf[BUFSIZ]; @@ -892,8 +892,8 @@ static void *MonThread(void *param) while (1) { // Wait for connection - memset(&client, 0, sizeof(client)); - len = sizeof(client); + memset(&client, 0, sizeof(client)); + len = sizeof(client); fd = accept(monsocket, (struct sockaddr*)&client, &len); if (fd < 0) { break; diff --git a/src/raspberrypi/scsimon.cpp b/src/raspberrypi/scsimon.cpp new file mode 100644 index 00000000..3bdf95bf --- /dev/null +++ b/src/raspberrypi/scsimon.cpp @@ -0,0 +1,1174 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// [ RaSCSI main ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "xm6.h" +#include "filepath.h" +#include "fileio.h" +#include "disk.h" +#include "gpiobus.h" + +//--------------------------------------------------------------------------- +// +// Constant declarations +// +//--------------------------------------------------------------------------- +#define CtrlMax 8 // Maximum number of SCSI controllers +#define UnitNum 2 // Number of units around controller +#ifdef BAREMETAL +#define FPRT(fp, ...) printf( __VA_ARGS__ ) +#else +#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ ) +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// Variable declarations +// +//--------------------------------------------------------------------------- +static volatile BOOL running; // Running flag +static volatile BOOL active; // Processing flag +SASIDEV *ctrl[CtrlMax]; // Controller +Disk *disk[CtrlMax * UnitNum]; // Disk +GPIOBUS *bus; // GPIO Bus +#ifdef BAREMETAL +FATFS fatfs; // FatFS +#else +int monsocket; // Monitor Socket +pthread_t monthread; // Monitor Thread +static void *MonThread(void *param); +#endif // BAREMETAL + +#ifndef BAREMETAL +//--------------------------------------------------------------------------- +// +// Signal Processing +// +//--------------------------------------------------------------------------- +void KillHandler(int sig) +{ + // Stop instruction + running = FALSE; +} +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// Banner Output +// +//--------------------------------------------------------------------------- +void Banner(int argc, char* argv[]) +{ + FPRT(stdout,"SCSI Target Emulator RaSCSI(*^..^*) "); + FPRT(stdout,"version %01d.%01d%01d(%s, %s)\n", + (int)((VERSION >> 8) & 0xf), + (int)((VERSION >> 4) & 0xf), + (int)((VERSION ) & 0xf), + __DATE__, + __TIME__); + FPRT(stdout,"Powered by XM6 TypeG Technology / "); + FPRT(stdout,"Copyright (C) 2016-2020 GIMONS\n"); + FPRT(stdout,"Connect type : %s\n", CONNECT_DESC); + + if (argc > 1 && strcmp(argv[1], "-h") == 0) { + FPRT(stdout,"\n"); + FPRT(stdout,"Usage: %s [-IDn FILE] ...\n\n", argv[0]); + FPRT(stdout," n is SCSI identification number(0-7).\n"); + FPRT(stdout," FILE is disk image file.\n\n"); + FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]); + FPRT(stdout," n is X68000 SASI HD number(0-15).\n"); + FPRT(stdout," FILE is disk image file.\n\n"); + FPRT(stdout," Image type is detected based on file extension.\n"); + FPRT(stdout," hdf : SASI HD image(XM6 SASI HD image)\n"); + FPRT(stdout," hds : SCSI HD image(XM6 SCSI HD image)\n"); + FPRT(stdout," hdn : SCSI HD image(NEC GENUINE)\n"); + FPRT(stdout," hdi : SCSI HD image(Anex86 HD image)\n"); + FPRT(stdout," nhd : SCSI HD image(T98Next HD image)\n"); + FPRT(stdout," hda : SCSI HD image(APPLE GENUINE)\n"); + FPRT(stdout," mos : SCSI MO image(XM6 SCSI MO image)\n"); + FPRT(stdout," iso : SCSI CD image(ISO 9660 image)\n"); + +#ifndef BAREMETAL + exit(0); +#endif // BAREMETAL + } +} + +//--------------------------------------------------------------------------- +// +// Initialization +// +//--------------------------------------------------------------------------- +BOOL Init() +{ + int i; + +#ifndef BAREMETAL + struct sockaddr_in server; + int yes; + + // Create socket for monitor + monsocket = socket(PF_INET, SOCK_STREAM, 0); + memset(&server, 0, sizeof(server)); + server.sin_family = PF_INET; + server.sin_port = htons(6868); + server.sin_addr.s_addr = htonl(INADDR_ANY); + + // allow address reuse + yes = 1; + if (setsockopt( + monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){ + return FALSE; + } + + // Bind + if (bind(monsocket, (struct sockaddr *)&server, + sizeof(struct sockaddr_in)) < 0) { + FPRT(stderr, "Error : Already running?\n"); + return FALSE; + } + + // Create Monitor Thread + pthread_create(&monthread, NULL, MonThread, NULL); + + // Interrupt handler settings + if (signal(SIGINT, KillHandler) == SIG_ERR) { + return FALSE; + } + if (signal(SIGHUP, KillHandler) == SIG_ERR) { + return FALSE; + } + if (signal(SIGTERM, KillHandler) == SIG_ERR) { + return FALSE; + } +#endif // BAREMETAL + + // GPIOBUS creation + bus = new GPIOBUS(); + + // GPIO Initialization + if (!bus->Init(BUS::MONITOR)) { + return FALSE; + } + + // Bus Reset + bus->Reset(); + bus->SetIO(FALSE); + + // Controller initialization + for (i = 0; i < CtrlMax; i++) { + ctrl[i] = NULL; + } + + // Disk Initialization + for (i = 0; i < CtrlMax; i++) { + disk[i] = NULL; + } + + // Other + running = FALSE; + active = FALSE; + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Cleanup +// +//--------------------------------------------------------------------------- +void Cleanup() +{ + int i; + + // Delete the disks + for (i = 0; i < CtrlMax * UnitNum; i++) { + if (disk[i]) { + delete disk[i]; + disk[i] = NULL; + } + } + + // Delete the Controllers + for (i = 0; i < CtrlMax; i++) { + if (ctrl[i]) { + delete ctrl[i]; + ctrl[i] = NULL; + } + } + + // Cleanup the Bus + bus->Cleanup(); + + // Discard the GPIOBUS object + delete bus; + +#ifndef BAREMETAL + // Close the monitor socket + if (monsocket >= 0) { + close(monsocket); + } +#endif // BAREMETAL +} + +//--------------------------------------------------------------------------- +// +// Reset +// +//--------------------------------------------------------------------------- +void Reset() +{ + int i; + + // Reset all of the controllers + for (i = 0; i < CtrlMax; i++) { + if (ctrl[i]) { + ctrl[i]->Reset(); + } + } + + // Reset the bus + bus->Reset(); +} + +//--------------------------------------------------------------------------- +// +// List Devices +// +//--------------------------------------------------------------------------- +void ListDevice(FILE *fp) +{ + int i; + int id; + int un; + Disk *pUnit; + Filepath filepath; + BOOL find; + char type[5]; + + find = FALSE; + type[4] = 0; + for (i = 0; i < CtrlMax * UnitNum; i++) { + // Initialize ID and unit number + id = i / UnitNum; + un = i % UnitNum; + pUnit = disk[i]; + + // skip if unit does not exist or null disk + if (pUnit == NULL || pUnit->IsNULL()) { + continue; + } + + // Output the header + if (!find) { + FPRT(fp, "\n"); + FPRT(fp, "+----+----+------+-------------------------------------\n"); + FPRT(fp, "| ID | UN | TYPE | DEVICE STATUS\n"); + FPRT(fp, "+----+----+------+-------------------------------------\n"); + find = TRUE; + } + + // ID,UNIT,Type,Device Status + type[0] = (char)(pUnit->GetID() >> 24); + type[1] = (char)(pUnit->GetID() >> 16); + type[2] = (char)(pUnit->GetID() >> 8); + type[3] = (char)(pUnit->GetID()); + FPRT(fp, "| %d | %d | %s | ", id, un, type); + + // mount status output + if (pUnit->GetID() == MAKEID('S', 'C', 'B', 'R')) { + FPRT(fp, "%s", "HOST BRIDGE"); + } else { + pUnit->GetPath(filepath); + FPRT(fp, "%s", + (pUnit->IsRemovable() && !pUnit->IsReady()) ? + "NO MEDIA" : filepath.GetPath()); + } + + // Write protection status + if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) { + FPRT(fp, "(WRITEPROTECT)"); + } + + // Goto the next line + FPRT(fp, "\n"); + } + + // If there is no controller, find will be null + if (!find) { + FPRT(fp, "No device is installed.\n"); + return; + } + + FPRT(fp, "+----+----+------+-------------------------------------\n"); +} + +//--------------------------------------------------------------------------- +// +// Controller Mapping +// +//--------------------------------------------------------------------------- +void MapControler(FILE *fp, Disk **map) +{ + int i; + int j; + int unitno; + int sasi_num; + int scsi_num; + BOOL is_monitor; + + // Replace the changed unit + for (i = 0; i < CtrlMax; i++) { + for (j = 0; j < UnitNum; j++) { + unitno = i * UnitNum + j; + if (disk[unitno] != map[unitno]) { + // Check if the original unit exists + if (disk[unitno]) { + // Disconnect it from the controller + if (ctrl[i]) { + ctrl[i]->SetUnit(j, NULL); + } + + // Free the Unit + delete disk[unitno]; + } + + // Setup a new unit + disk[unitno] = map[unitno]; + } + } + } + + // Reconfigure all of the controllers + for (i = 0; i < CtrlMax; i++) { + // Examine the unit configuration + sasi_num = 0; + scsi_num = 0; + for (j = 0; j < UnitNum; j++) { + unitno = i * UnitNum + j; + // branch by unit type + if (disk[unitno]) { + if (disk[unitno]->IsSASI()) { + // Drive is SASI, so increment SASI count + sasi_num++; + } else { + // Drive is SCSI, so increment SCSI count + scsi_num++; + } + } + + // Remove the unit + if (ctrl[i]) { + ctrl[i]->SetUnit(j, NULL); + } + } + + // If there are no units connected + if (sasi_num == 0 && scsi_num == 0) { + if (ctrl[i]) { + delete ctrl[i]; + ctrl[i] = NULL; + continue; + } + } + + // Mixture of SCSI and SASI + if (sasi_num > 0 && scsi_num > 0) { + FPRT(fp, "Error : SASI and SCSI can't be mixed\n"); + continue; + } + + if (sasi_num > 0) { + // Only SASI Unit(s) + + // Release the controller if it is not SASI + if (ctrl[i] && !ctrl[i]->IsSASI()) { + delete ctrl[i]; + ctrl[i] = NULL; + } + + // Create a new SASI controller + if (!ctrl[i]) { + ctrl[i] = new SASIDEV(); + ctrl[i]->Connect(i, bus); + } + } else { + // Only SCSI Unit(s) + + // Release the controller if it is not SCSI + if (ctrl[i] && !ctrl[i]->IsSCSI()) { + delete ctrl[i]; + ctrl[i] = NULL; + } + + // Create a new SCSI controller + if (!ctrl[i]) { + // Check to see if we need a standard SCSI controller + // or if we are creating a "monitor" controller + is_monitor = FALSE; + for (j = 0; j < UnitNum; j++) + { + unitno = i * UnitNum + j; + if( disk[unitno] && disk[unitno]->IsMonitor()) + { + is_monitor = TRUE; + break; + } + } + if(is_monitor == TRUE) + { + ctrl[i] = new SCSIMONDEV(); + } + else + { + ctrl[i] = new SCSIDEV(); + } + ctrl[i]->Connect(i, bus); + } + } + + // connect all units + for (j = 0; j < UnitNum; j++) { + unitno = i * UnitNum + j; + if (disk[unitno]) { + // Add the unit connection + ctrl[i]->SetUnit(j, disk[unitno]); + } + } + } +} + +//--------------------------------------------------------------------------- +// +// Command Processing +// +//--------------------------------------------------------------------------- +BOOL ProcessCmd(FILE *fp, int id, int un, int cmd, int type, char *file) +{ + Disk *map[CtrlMax * UnitNum]; + int len; + char *ext; + Filepath filepath; + Disk *pUnit; + + // Copy the Unit List + memcpy(map, disk, sizeof(disk)); + + // Check the Controller Number + if (id < 0 || id >= CtrlMax) { + FPRT(fp, "Error : Invalid ID\n"); + return FALSE; + } + + // Check the Unit Number + if (un < 0 || un >= UnitNum) { + FPRT(fp, "Error : Invalid unit number\n"); + return FALSE; + } + + // Connect Command + if (cmd == 0) { // ATTACH + // Distinguish between SASI and SCSI + ext = NULL; + pUnit = NULL; + if (type == 0) { + // Passed the check + if (!file) { + return FALSE; + } + + // Check that command is at least 5 characters long + len = strlen(file); + if (len < 5) { + return FALSE; + } + + // Check the extension + if (file[len - 4] != '.') { + return FALSE; + } + + // If the extension is not SASI type, replace with SCSI + ext = &file[len - 3]; + if (xstrcasecmp(ext, "hdf") != 0) { + type = 1; + } + } + + // Create a new drive, based upon type + switch (type) { + case 0: // HDF + pUnit = new SASIHD(); + break; + case 1: // HDS/HDN/HDI/NHD/HDA + if (ext == NULL) { + break; + } + if (xstrcasecmp(ext, "hdn") == 0 || + xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0) { + pUnit = new SCSIHD_NEC(); + } else if (xstrcasecmp(ext, "hda") == 0) { + pUnit = new SCSIHD_APPLE(); + } else { + pUnit = new SCSIHD(); + } + break; + case 2: // MO + pUnit = new SCSIMO(); + break; + case 3: // CD + pUnit = new SCSICD(); + break; + case 4: // BRIDGE + pUnit = new SCSIBR(); + break; + case 5: // Logger/Monitor device + pUnit = new MONITORHD(); + break; + default: + FPRT(fp, "Error : Invalid device type\n"); + return FALSE; + } + + // drive checks files + if (type <= 1 || (type <= 3 && xstrcasecmp(file, "-") != 0)) { + // Set the Path + filepath.SetPath(file); + + // Open the file path + if (!pUnit->Open(filepath)) { + FPRT(fp, "Error : File open error [%s]\n", file); + delete pUnit; + return FALSE; + } + } + + // Set the cache to write-through + pUnit->SetCacheWB(FALSE); + + // Replace with the newly created unit + map[id * UnitNum + un] = pUnit; + + // Re-map the controller + MapControler(fp, map); + return TRUE; + } + + // Is this a valid command? + if (cmd > 4) { + FPRT(fp, "Error : Invalid command\n"); + return FALSE; + } + + // Does the controller exist? + if (ctrl[id] == NULL) { + FPRT(fp, "Error : No such device\n"); + return FALSE; + } + + // Does the unit exist? + pUnit = disk[id * UnitNum + un]; + if (pUnit == NULL) { + FPRT(fp, "Error : No such device\n"); + return FALSE; + } + + // Disconnect Command + if (cmd == 1) { // DETACH + // Free the existing unit + map[id * UnitNum + un] = NULL; + + // Re-map the controller + MapControler(fp, map); + return TRUE; + } + + // Valid only for MO or CD + if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O') && + pUnit->GetID() != MAKEID('S', 'C', 'C', 'D')) { + FPRT(fp, "Error : Operation denied(Deveice isn't removable)\n"); + return FALSE; + } + + switch (cmd) { + case 2: // INSERT + // Set the file path + filepath.SetPath(file); + + // Open the file + if (pUnit->Open(filepath)) { + FPRT(fp, "Error : File open error [%s]\n", file); + return FALSE; + } + break; + + case 3: // EJECT + pUnit->Eject(TRUE); + break; + + case 4: // PROTECT + if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O')) { + FPRT(fp, "Error : Operation denied(Deveice isn't MO)\n"); + return FALSE; + } + pUnit->WriteP(!pUnit->IsWriteP()); + break; + default: + ASSERT(FALSE); + return FALSE; + } + + return TRUE; +} + +//--------------------------------------------------------------------------- +// +// Argument Parsing +// +//--------------------------------------------------------------------------- +BOOL ParseArgument(int argc, char* argv[]) +{ +#ifdef BAREMETAL + FRESULT fr; + FIL fp; + char line[512]; +#else + int i; +#endif // BAREMETAL + int id; + int un; + int type; + char *argID; + char *argPath; + int len; + char *ext; + +#ifdef BAREMETAL + // Mount the SD card + fr = f_mount(&fatfs, "", 1); + if (fr != FR_OK) { + FPRT(stderr, "Error : SD card mount failed.\n"); + return FALSE; + } + + // If there is no setting file, the processing is interrupted + fr = f_open(&fp, "rascsi.ini", FA_READ); + if (fr != FR_OK) { + return FALSE; + } +#else + // If the ID and path are not specified, the processing is interrupted + if (argc < 3) { + return TRUE; + } + i = 1; + argc--; +#endif // BAREMETAL + + // Start Decoding + + while (TRUE) { +#ifdef BAREMETAL + // Get one Line + memset(line, 0x00, sizeof(line)); + if (f_gets(line, sizeof(line) -1, &fp) == NULL) { + break; + } + + // Delete the CR/LF + len = strlen(line); + while (len > 0) { + if (line[len - 1] != '\r' && line[len - 1] != '\n') { + break; + } + line[len - 1] = '\0'; + len--; + } +#else + if (argc < 2) { + break; + } + + argc -= 2; +#endif // BAREMETAL + + // Get the ID and Path +#ifdef BAREMETAL + argID = &line[0]; + argPath = &line[4]; + line[3] = '\0'; + + // Check if the line is an empty string + if (argID[0] == '\0' || argPath[0] == '\0') { + continue; + } +#else + argID = argv[i++]; + argPath = argv[i++]; + + // Check if the argument is invalid + if (argID[0] != '-') { + FPRT(stderr, + "Error : Invalid argument(-IDn or -HDn) [%s]\n", argID); + goto parse_error; + } + argID++; +#endif // BAREMETAL + + if (strlen(argID) == 3 && xstrncasecmp(argID, "id", 2) == 0) { + // ID or ID Format + + // Check that the ID number is valid (0-7) + if (argID[2] < '0' || argID[2] > '7') { + FPRT(stderr, + "Error : Invalid argument(IDn n=0-7) [%c]\n", argID[2]); + goto parse_error; + } + + // The ID unit is good + id = argID[2] - '0'; + un = 0; + } else if (xstrncasecmp(argID, "hd", 2) == 0) { + // HD or HD format + + if (strlen(argID) == 3) { + // Check that the HD number is valid (0-9) + if (argID[2] < '0' || argID[2] > '9') { + FPRT(stderr, + "Error : Invalid argument(HDn n=0-15) [%c]\n", argID[2]); + goto parse_error; + } + + // ID was confirmed + id = (argID[2] - '0') / UnitNum; + un = (argID[2] - '0') % UnitNum; + } else if (strlen(argID) == 4) { + // Check that the HD number is valid (10-15) + if (argID[2] != '1' || argID[3] < '0' || argID[3] > '5') { + FPRT(stderr, + "Error : Invalid argument(HDn n=0-15) [%c]\n", argID[2]); + goto parse_error; + } + + // The ID unit is good - create the id and unit number + id = ((argID[3] - '0') + 10) / UnitNum; + un = ((argID[3] - '0') + 10) % UnitNum; +#ifdef BAREMETAL + argPath++; +#endif // BAREMETAL + } else { + FPRT(stderr, + "Error : Invalid argument(IDn or HDn) [%s]\n", argID); + goto parse_error; + } + } else { + FPRT(stderr, + "Error : Invalid argument(IDn or HDn) [%s]\n", argID); + goto parse_error; + } + + // Skip if there is already an active device + if (disk[id * UnitNum + un] && + !disk[id * UnitNum + un]->IsNULL()) { + continue; + } + + // Initialize device type + type = -1; + + // Check ethernet and host bridge + if (xstrcasecmp(argPath, "bridge") == 0) { + type = 4; + } else if (xstrcasecmp(argPath, "monitor") == 0){ + type = 5; + } else { + // Check the path length + len = strlen(argPath); + if (len < 5) { + FPRT(stderr, + "Error : Invalid argument(File path is short) [%s]\n", + argPath); + goto parse_error; + } + + // Does the file have an extension? + if (argPath[len - 4] != '.') { + FPRT(stderr, + "Error : Invalid argument(No extension) [%s]\n", argPath); + goto parse_error; + } + + // Figure out what the type is + ext = &argPath[len - 3]; + if (xstrcasecmp(ext, "hdf") == 0 || + xstrcasecmp(ext, "hds") == 0 || + xstrcasecmp(ext, "hdn") == 0 || + xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0 || + xstrcasecmp(ext, "hda") == 0) { + // HD(SASI/SCSI) + type = 0; + } else if (strcasecmp(ext, "mos") == 0) { + // MO + type = 2; + } else if (strcasecmp(ext, "iso") == 0) { + // CD + type = 3; + } else { + // Cannot determine the file type + FPRT(stderr, + "Error : Invalid argument(file type) [%s]\n", ext); + goto parse_error; + } + } + + // Execute the command + if (!ProcessCmd(stderr, id, un, 0, type, argPath)) { + goto parse_error; + } + } + +#ifdef BAREMETAL + // Close the configuration file + f_close(&fp); +#endif // BAREMETAL + + // Display the device list + ListDevice(stdout); + + return TRUE; + +parse_error: + +#ifdef BAREMETAL + // Close the configuration file + f_close(&fp); +#endif // BAREMETAL + + return FALSE; +} + +#ifndef BAREMETAL +//--------------------------------------------------------------------------- +// +// Pin the thread to a specific CPU +// +//--------------------------------------------------------------------------- +void FixCpu(int cpu) +{ + cpu_set_t cpuset; + int cpus; + + // Get the number of CPUs + CPU_ZERO(&cpuset); + sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); + cpus = CPU_COUNT(&cpuset); + + // Set the thread affinity + if (cpu < cpus) { + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); + } +} + +//--------------------------------------------------------------------------- +// +// Monitor Thread +// +//--------------------------------------------------------------------------- +static void *MonThread(void *param) +{ + struct sched_param schedparam; + struct sockaddr_in client; + socklen_t len; + int fd; + FILE *fp; + char buf[BUFSIZ]; + char *p; + int i; + char *argv[5]; + int id; + int un; + int cmd; + int type; + char *file; + + // Scheduler Settings + schedparam.sched_priority = 0; + sched_setscheduler(0, SCHED_IDLE, &schedparam); + + // Set the affinity to a specific processor core + FixCpu(2); + + // Wait for the execution to start + while (!running) { + usleep(1); + } + + // Setup the monitor socket to receive commands + listen(monsocket, 1); + + while (1) { + // Wait for connection + memset(&client, 0, sizeof(client)); + len = sizeof(client); + fd = accept(monsocket, (struct sockaddr*)&client, &len); + if (fd < 0) { + break; + } + + // Fetch the command + fp = fdopen(fd, "r+"); + p = fgets(buf, BUFSIZ, fp); + + // Failed to get the command + if (!p) { + goto next; + } + + // Remove the newline character + p[strlen(p) - 1] = 0; + + // List all of the devices + if (xstrncasecmp(p, "list", 4) == 0) { + ListDevice(fp); + goto next; + } + + // Parameter separation + argv[0] = p; + for (i = 1; i < 5; i++) { + // Skip parameter values + while (*p && (*p != ' ')) { + p++; + } + + // Replace spaces with null characters + while (*p && (*p == ' ')) { + *p++ = 0; + } + + // The parameters were lost + if (!*p) { + break; + } + + // Recognized as a parameter + argv[i] = p; + } + + // Failed to get all parameters + if (i < 5) { + goto next; + } + + // ID, unit, command, type, file + id = atoi(argv[0]); + un = atoi(argv[1]); + cmd = atoi(argv[2]); + type = atoi(argv[3]); + file = argv[4]; + + // Wait until we becom idle + while (active) { + usleep(500 * 1000); + } + + // Execute the command + ProcessCmd(fp, id, un, cmd, type, file); + +next: + // Release the connection + fclose(fp); + close(fd); + } + + return NULL; +} +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// Main processing +// +//--------------------------------------------------------------------------- +#ifdef BAREMETAL +extern "C" +int startrascsi(void) +{ + int argc = 0; + char** argv = NULL; +#else +int main(int argc, char* argv[]) +{ +#endif // BAREMETAL + int i; + int ret; + int actid; + DWORD now; + BUS::phase_t phase; + BYTE data; +#ifndef BAREMETAL + struct sched_param schparam; +#endif // BAREMETAL + + // Output the Banner + Banner(argc, argv); + + // Initialize + ret = 0; + if (!Init()) { + ret = EPERM; + goto init_exit; + } + + // Reset + Reset(); + +#ifdef BAREMETAL + // BUSY assert (to hold the host side) + bus->SetBSY(TRUE); +#endif + + // Argument parsing + if (!ParseArgument(argc, argv)) { + ret = EINVAL; + goto err_exit; + } + +#ifdef BAREMETAL + // Release the busy signal + bus->SetBSY(FALSE); +#endif + +#ifndef BAREMETAL + // Set the affinity to a specific processor core + FixCpu(3); + +#ifdef USE_SEL_EVENT_ENABLE + // Scheduling policy setting (highest priority) + schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &schparam); +#endif // USE_SEL_EVENT_ENABLE +#endif // BAREMETAL + + // Start execution + running = TRUE; + + // Main Loop + while (running) { + // Work initialization + actid = -1; + phase = BUS::busfree; + +#ifdef USE_SEL_EVENT_ENABLE + // SEL signal polling + if (bus->PollSelectEvent() < 0) { + // Stop on interrupt + if (errno == EINTR) { + break; + } + continue; + } + + // Get the bus + bus->Aquire(); +#else + bus->Aquire(); + if (!bus->GetSEL()) { +#if !defined(BAREMETAL) + usleep(0); +#endif // !BAREMETAL + continue; + } +#endif // USE_SEL_EVENT_ENABLE + + // Wait until BSY is released as there is a possibility for the + // initiator to assert it while setting the ID (for up to 3 seconds) + if (bus->GetBSY()) { + now = SysTimer::GetTimerLow(); + while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { + bus->Aquire(); + if (!bus->GetBSY()) { + break; + } + } + } + + // Stop because it the bus is busy or another device responded + if (bus->GetBSY() || !bus->GetSEL()) { + continue; + } + + // Notify all controllers + data = bus->GetDAT(); + for (i = 0; i < CtrlMax; i++) { + if (!ctrl[i] || (data & (1 << i)) == 0) { + continue; + } + + // Find the target that has moved to the selection phase + if (ctrl[i]->Process() == BUS::selection) { + // Get the target ID + actid = i; + + // Bus Selection phase + phase = BUS::selection; + break; + } + } + + // Return to bus monitoring if the selection phase has not started + if (phase != BUS::selection) { + continue; + } + + // Start target device + active = TRUE; + +#if !defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL) + // Scheduling policy setting (highest priority) + schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &schparam); +#endif // !USE_SEL_EVENT_ENABLE && !BAREMETAL + + // Loop until the bus is free + while (running) { + // Target drive + phase = ctrl[actid]->Process(); + + // End when the bus is free + if (phase == BUS::busfree) { + break; + } + } + +#if !defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL) + // Set the scheduling priority back to normal + schparam.sched_priority = 0; + sched_setscheduler(0, SCHED_OTHER, &schparam); +#endif // !USE_SEL_EVENT_ENABLE && !BAREMETAL + + // End the target travel + active = FALSE; + } + +err_exit: + // Cleanup + Cleanup(); + +init_exit: +#if !defined(BAREMETAL) + exit(ret); +#else + return ret; +#endif // BAREMETAL +} diff --git a/src/raspberrypi/scsimondev.cpp b/src/raspberrypi/scsimondev.cpp new file mode 100644 index 00000000..c6185fb4 --- /dev/null +++ b/src/raspberrypi/scsimondev.cpp @@ -0,0 +1,1902 @@ +//--------------------------------------------------------------------------- +// +// 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. +// +// [ SCSI Monitor Device] +// +//--------------------------------------------------------------------------- + +#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 + +// Temporary? Trick to make the ID understand that RASCSI is set +#ifndef RASCSI +#define RASCSI +#endif // RASCSI + +//=========================================================================== +// +// SCSI Device +// +//=========================================================================== + +//--------------------------------------------------------------------------- +// +// Constructor +// +//--------------------------------------------------------------------------- +#ifdef RASCSI +SCSIMONDEV::SCSIMONDEV() : SASIDEV() +#else +SCSIMONDEV::SCSIMONDEV(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 SCSIMONDEV::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 SCSIMONDEV::Process() +{ + ASSERT(this); + printf("SCSIMONDEV::Process() %d\n", ctrl.id); + + // 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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::CmdModeSense() +{ + DWORD lun; + + ASSERT(this); + +#if defined(DISK_LOG) + Log(Log::Normal, "MODE SENSE Command "); +#endif // DISK_LOG + + printf("Received a Mode Sense command. Contents...."); + for(int i=0; i<10; i++) + { + printf("%08X ", ctrl.cmd[i]); + } + printf("\n"); + + // 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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::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 SCSIMONDEV::Receive() +#else +//--------------------------------------------------------------------------- +// +// Continue receiving data +// +//--------------------------------------------------------------------------- +void FASTCALL SCSIMONDEV::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 SCSIMONDEV::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; +}