Author : Takeda.Toshiya
Date : 2016.03.01-
- [ SCSI hard disk drive ]
+ [ SCSI/SASI hard disk drive ]
*/
#include "scsi_hdd.h"
+#include "harddisk.h"
#include "../fifo.h"
-void SCSI_HDD::initialize_max_logical_block_addr()
+void SCSI_HDD::release()
{
- if(max_logical_block_addr == 0) {
- FILEIO* fio = new FILEIO();
- uint32_t file_size = 0;
-
- if(default_drive_size == 0) {
- default_drive_size = 0x2800000; // 40MB
+ for(int i = 0; i < 8; i++) {
+ if(disk[i] != NULL) {
+ disk[i]->close();
+ delete disk[i];
}
- if(FILEIO::IsFileExisting(create_local_path(_T("SCSI%d.DAT"), scsi_id))) {
- if(fio->Fopen(create_local_path(_T("SCSI%d.DAT"), scsi_id), FILEIO_READ_WRITE_BINARY)) {
- if((file_size = fio->FileLength()) == 0) {
- uint32_t remain = (file_size = default_drive_size);
- void *tmp = calloc(1, SCSI_BUFFER_SIZE);
- while(remain > 0) {
- uint32_t length = min(remain, (uint32_t)SCSI_BUFFER_SIZE);
- fio->Fwrite(tmp, length, 1);
- remain -= length;
- }
- free(tmp);
+ }
+ SCSI_DEV::release();
+}
+
+void SCSI_HDD::reset()
+{
+ if(!is_hot_swappable) {
+ for(int drv = 0; drv < 8; drv++) {
+ if(disk[drv] != NULL) {
+ if(image_path[drv][0] != _T('\0') && FILEIO::IsFileExisting(image_path[drv])) {
+ disk[drv]->open(image_path[drv], sector_size[drv]);
+ } else {
+ disk[drv]->close();
}
- fio->Fclose();
}
+ }
+ }
+ SCSI_DEV::reset();
+}
+
+void SCSI_HDD::open(int drv, const _TCHAR* file_path, int default_sector_size)
+{
+ if(drv < 8 && disk[drv] != NULL) {
+ if(!is_hot_swappable) {
+ my_tcscpy_s(image_path[drv], _MAX_PATH, file_path);
+ sector_size[drv] = default_sector_size;
} else {
- if(fio->Fopen(create_local_path(_T("SCSI%d.DAT"), scsi_id), FILEIO_WRITE_BINARY)) {
- uint32_t remain = (file_size = default_drive_size);
- void *tmp = calloc(1, SCSI_BUFFER_SIZE);
- while(remain > 0) {
- uint32_t length = min(remain, (uint32_t)SCSI_BUFFER_SIZE);
- fio->Fwrite(tmp, length, 1);
- remain -= length;
- }
- free(tmp);
- fio->Fclose();
- }
+ disk[drv]->open(file_path, default_sector_size);
+ }
+ }
+}
+
+void SCSI_HDD::close(int drv)
+{
+ if(drv < 8 && disk[drv] != NULL) {
+ if(!is_hot_swappable) {
+ image_path[drv][0] = _T('\0');
+ } else {
+ disk[drv]->close();
+ }
+ }
+}
+
+bool SCSI_HDD::mounted(int drv)
+{
+ if(drv < 8 && disk[drv] != NULL) {
+ if(!is_hot_swappable) {
+ return (image_path[drv][0] != _T('\0'));
+ } else {
+ return disk[drv]->mounted();
+ }
+ }
+ return false;
+}
+
+bool SCSI_HDD::accessed(int drv)
+{
+ if(drv < 8 && disk[drv] != NULL) {
+ return disk[drv]->accessed();
+ }
+ return false;
+}
+
+bool SCSI_HDD::is_device_existing()
+{
+ for(int i = 0; i < 8; i++) {
+ if(disk[i] != NULL && disk[i]->mounted()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t SCSI_HDD::physical_block_size()
+{
+ HARDDISK *unit = disk[get_logical_unit_number()];
+
+ if(unit != NULL && unit->mounted()) {
+ return unit->sector_size;
+ }
+ return 0;// 512;
+}
+
+uint32_t SCSI_HDD::logical_block_size()
+{
+ HARDDISK *unit = disk[get_logical_unit_number()];
+
+ if(unit != NULL && unit->mounted()) {
+ return unit->sector_size;
+ }
+ return 0;// 512;
+}
+
+uint32_t SCSI_HDD::max_logical_block_addr()
+{
+ HARDDISK *unit = disk[get_logical_unit_number()];
+
+ if(unit != NULL && unit->mounted() && unit->sector_num > 0) {
+ return unit->sector_num - 1;
+ }
+ return 0;
+}
+
+bool SCSI_HDD::read_buffer(int length)
+{
+ if(!(command[0] == SCSI_CMD_READ6 || command[0] == SCSI_CMD_READ10 || command[0] == SCSI_CMD_READ12)) {
+ for(int i = 0; i < length; i++) {
+ buffer->write(0);
+ position++;
+ }
+ set_sense_code(SCSI_SENSE_NOSENSE);
+ return true;
+ }
+ HARDDISK *unit = disk[get_logical_unit_number()];
+
+ if(!(unit != NULL && unit->mounted())) {
+ set_sense_code(SCSI_SENSE_NOTREADY);
+ return false;
+ }
+ while(length > 0) {
+ uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
+ int tmp_length = min(length, (int)sizeof(tmp_buffer));
+
+ if(!unit->read_buffer((long)position, tmp_length, tmp_buffer)) {
+ set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
+ return false;
}
- if(file_size != 0) {
- max_logical_block_addr = (file_size / logical_block_size) - 1;
+ for(int i = 0; i < tmp_length; i++) {
+ buffer->write(tmp_buffer[i]);
+ }
+ length -= tmp_length;
+ position += tmp_length;
+ }
+ set_sense_code(SCSI_SENSE_NOSENSE);
+ return true;
+}
+
+bool SCSI_HDD::write_buffer(int length)
+{
+ if(!(command[0] == SCSI_CMD_WRITE6 || command[0] == SCSI_CMD_WRITE10 || command[0] == SCSI_CMD_WRITE12)) {
+ for(int i = 0; i < length; i++) {
+ buffer->read();
+ position++;
}
- delete fio;
+ set_sense_code(SCSI_SENSE_NOSENSE);
+ return true;
+ }
+ HARDDISK *unit = disk[get_logical_unit_number()];
+
+ if(!(unit != NULL && unit->mounted())) {
+ set_sense_code(SCSI_SENSE_NOTREADY);
+ return false;
}
+ while(length > 0) {
+ uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
+ int tmp_length = min(length, (int)sizeof(tmp_buffer));
+
+ for(int i = 0; i < tmp_length; i++) {
+ tmp_buffer[i] = buffer->read();
+ }
+ if(!unit->write_buffer((long)position, tmp_length, tmp_buffer)) {
+ set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
+ return false;
+ }
+ length -= tmp_length;
+ position += tmp_length;
+ }
+ set_sense_code(SCSI_SENSE_NOSENSE);
+ return true;
}
-void SCSI_HDD::start_command()
+#define STATE_VERSION 3
+
+bool SCSI_HDD::process_state(FILEIO* state_fio, bool loading)
+{
+ if(!state_fio->StateCheckUint32(STATE_VERSION)) {
+ return false;
+ }
+ if(!state_fio->StateCheckInt32(this_device_id)) {
+ return false;
+ }
+ /*
+ for(int drv = 0; drv < 8; drv++) {
+ if(disk[drv] != NULL) {
+ if(!disk[drv]->process_state(state_fio, loading)) {
+ return false;
+ }
+ }
+ }
+ */
+ state_fio->StateArray(&image_path[0][0], sizeof(image_path), 1);
+ state_fio->StateArray(sector_size, sizeof(sector_size), 1);
+ return SCSI_DEV::process_state(state_fio, loading);
+}
+
+// SASI hard disk drive
+
+void SASI_HDD::start_command()
{
switch(command[0]) {
- case SCSI_CMD_INQUIRY:
+ case SCSI_CMD_REQ_SENSE:
#ifdef _SCSI_DEBUG_LOG
- emu->out_debug_log(_T("[SCSI_HDD:ID=%d] Command: Inquiry\n"), scsi_id);
+ this->out_debug_log(_T("[SASI_HDD:ID=%d] Command: Request Sense\n"), scsi_id);
#endif
// start position
- position = ((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]) * logical_block_size;
+ position = (command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3];
+ position *= physical_block_size();
// transfer length
- remain = 32;
+ remain = 4;
// create sense data table
buffer->clear();
- buffer->write(0x00);
- buffer->write(0x00);
- buffer->write(0x02); // ANSI SCSI2
- buffer->write(0x01); // ANSI-CCS
- buffer->write(0x1c);
- buffer->write(0x00);
- buffer->write(0x00);
- buffer->write(0x18);
- for(int i = 0; i < (int)strlen(vendor_id) && i < 8; i++) {
- buffer->write(vendor_id[i]);
- }
- for(int i = strlen(vendor_id); i < 8; i++) {
- buffer->write(0x20);
- }
- for(int i = 0; i < (int)strlen(product_id) && i < 16; i++) {
- buffer->write(vendor_id[i]);
- }
- for(int i = strlen(product_id); i < 16; i++) {
- buffer->write(0x20);
- }
- // set first data
- set_dat(buffer->read());
+ buffer->write(get_sense_code());
+ buffer->write(((max_logical_block_addr() >> 16) & 0x1f) | (get_logical_unit_number() << 5));
+ buffer->write(((max_logical_block_addr() >> 8) & 0xff));
+ buffer->write(((max_logical_block_addr() >> 0) & 0xff));
// change to data in phase
+ set_dat(buffer->read());
set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
- break;
+ set_sense_code(SCSI_SENSE_NOSENSE);
+ return;
- case SCSI_CMD_RD_CAPAC:
+ case 0xc2:
#ifdef _SCSI_DEBUG_LOG
- emu->out_debug_log(_T("[SCSI_HDD:ID=%d] Command: Read Capacity\n"), scsi_id);
+ this->out_debug_log(_T("[SASI_HDD:ID=%d] Command: SASI Command 0xC2\n"), scsi_id);
#endif
- // initialize max logical block address
- initialize_max_logical_block_addr();
- // start position
- position = (command[2] * 0x1000000 + command[3] * 0x10000 + command[4] * 0x100 + command[5]) * logical_block_size;
// transfer length
- remain = 8;
- // create capacity data table
+ remain = 10; // DTC\8cn (\83g\83\89\83\93\83W\83X\83^\8bZ\8fpSPECIAL No.27, P.88)
+ // clear data buffer
buffer->clear();
- buffer->write((max_logical_block_addr >> 24) & 0xff);
- buffer->write((max_logical_block_addr >> 16) & 0xff);
- buffer->write((max_logical_block_addr >> 8) & 0xff);
- buffer->write((max_logical_block_addr >> 0) & 0xff);
- buffer->write(( logical_block_size >> 24) & 0xff);
- buffer->write(( logical_block_size >> 16) & 0xff);
- buffer->write(( logical_block_size >> 8) & 0xff);
- buffer->write(( logical_block_size >> 0) & 0xff);
- // set first data
- set_dat(buffer->read());
// change to data in phase
- set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
- break;
-
- default:
- SCSI_DEV::start_command();
- break;
- }
-}
-
-void SCSI_HDD::read_buffer(int length)
-{
- // make sure drive size is not zero
- initialize_max_logical_block_addr();
-
- // read blocks
- FILEIO* fio = new FILEIO();
- if(fio->Fopen(create_local_path(_T("SCSI%d.DAT"), scsi_id), FILEIO_READ_BINARY)) {
- // FIXME: consider the case that disk size is bigger than MAX_LONG
- fio->Fseek((long)position, FILEIO_SEEK_SET);
- while(length > 0) {
- uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
- int tmp_length = min(length, sizeof(tmp_buffer));
-
- fio->Fread(tmp_buffer, tmp_length, 1);
- for(int i = 0; i < tmp_length; i++) {
- buffer->write(tmp_buffer[i]);
- }
- length -= tmp_length;
- position += tmp_length;
- }
- fio->Fclose();
- }
- delete fio;
-}
-
-void SCSI_HDD::write_buffer(int length)
-{
- // make sure drive size is not zero
- initialize_max_logical_block_addr();
-
- // write blocks
- FILEIO* fio = new FILEIO();
- if(fio->Fopen(create_local_path(_T("SCSI%d.DAT"), scsi_id), FILEIO_READ_WRITE_BINARY)) {
- // FIXME: consider the case that disk size is bigger than MAX_LONG
- fio->Fseek((long)position, FILEIO_SEEK_SET);
- while(length > 0) {
- uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
- int tmp_length = min(length, sizeof(tmp_buffer));
-
- for(int i = 0; i < tmp_length; i++) {
- tmp_buffer[i] = buffer->read();
- }
- fio->Fwrite(tmp_buffer, tmp_length, 1);
- length -= tmp_length;
- position += tmp_length;
- }
- fio->Fclose();
+ set_phase_delay(SCSI_PHASE_DATA_OUT, 1.0);
+ return;
}
- delete fio;
+ // start standard command
+ SCSI_HDD::start_command();
}
-