summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/nscsi_cd.cpp
blob: 3496a84081b9a96eee9da5094dd2c718184e9f32 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                     
                


                             


                     
                                                                                
 
                                                                                                                         
                                                                                                                                            





                                          
                                       
                             

                                         






                                                                         
                        

 
                                                            

                                     

                  







                                                                                                        
 
                                                          


                                                                 





                                                                                    

                 
                                                           

 





                                                 

                                       

                   

                                
                                                 



                                                      

                      





                                       




                                                                                           
                                                                          
 
                                                        




                                                       
                                                                                    
                                                                                                                 








                                          




                                                                                    





                                                                                             


                                                                        









                                                      
                                                 



                                              




                                       
                                               
 

                                                                                                                    






                                                   

                                                           





                                              





                                       


                                                                                                         
                                                                                   
 
                                                        


                                              

                                                       
                                                                                   











                                                                                                    

                                                                                                                    







                                                                     

                                                               































                                                                                       
                                                                              











                                              





                                                              
                
                                                                     
 



                                                  
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#include "emu.h"
#include "machine/nscsi_cd.h"
#include "imagedev/chd_cd.h"

#define VERBOSE 1
#include "logmacro.h"

DEFINE_DEVICE_TYPE(NSCSI_CDROM, nscsi_cdrom_device, "scsi_cdrom", "SCSI CD-ROM")

nscsi_cdrom_device::nscsi_cdrom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	nscsi_full_device(mconfig, NSCSI_CDROM, tag, owner, clock), cdrom(nullptr), bytes_per_block(bytes_per_sector), lba(0), cur_sector(0)
{
}

void nscsi_cdrom_device::device_start()
{
	nscsi_full_device::device_start();
	save_item(NAME(sector_buffer));
	save_item(NAME(lba));
	save_item(NAME(cur_sector));
	save_item(NAME(bytes_per_block));
}

void nscsi_cdrom_device::device_reset()
{
	nscsi_full_device::device_reset();
	cdrom = subdevice<cdrom_image_device>("image")->get_cdrom_file();
	lba = 0;
	cur_sector = -1;
}

MACHINE_CONFIG_START(nscsi_cdrom_device::device_add_mconfig)
	MCFG_CDROM_ADD("image")
	MCFG_CDROM_INTERFACE("cdrom")
MACHINE_CONFIG_END

void nscsi_cdrom_device::set_block_size(u32 block_size) 
{
	assert_always(!started(), "block size should not be set after device start");
	assert_always(bytes_per_sector % block_size == 0, "block size must be a factor of sector size");

	bytes_per_block = block_size; 
};


uint8_t nscsi_cdrom_device::scsi_get_data(int id, int pos)
{
	if(id != 2)
		return nscsi_full_device::scsi_get_data(id, pos);
	int sector = (lba * bytes_per_block + pos) / bytes_per_sector;
	if(sector != cur_sector) {
		cur_sector = sector;
		if(!cdrom_read_data(cdrom, sector, sector_buffer, CD_TRACK_MODE1)) {
			LOG("CD READ ERROR sector %d!\n", sector);
			std::fill_n(sector_buffer, sizeof(sector_buffer), 0);
		}
	}
	return sector_buffer[pos & (bytes_per_sector - 1)];
}

void nscsi_cdrom_device::return_no_cd()
{
	sense(false, 3);
	scsi_status_complete(SS_CHECK_CONDITION);
}

void nscsi_cdrom_device::scsi_command()
{
	int blocks;

	switch(scsi_cmdbuf[0]) {
	case SC_TEST_UNIT_READY:
		LOG("command TEST UNIT READY\n");
		if(cdrom)
			scsi_status_complete(SS_GOOD);
		else
			return_no_cd();
		break;

	case SC_READ_6:
		if(!cdrom) {
			return_no_cd();
			break;
		}

		lba = ((scsi_cmdbuf[1] & 0x1f)<<16) | (scsi_cmdbuf[2]<<8) | scsi_cmdbuf[3];
		blocks = scsi_cmdbuf[4];
		if(!blocks)
			blocks = 256;

		LOG("command READ start=%08x blocks=%04x\n", lba, blocks);

		scsi_data_in(2, blocks*bytes_per_block);
		scsi_status_complete(SS_GOOD);
		break;

	case SC_INQUIRY: {
		int lun = get_lun(scsi_cmdbuf[1] >> 5);
		LOG("command INQUIRY lun=%d EVPD=%d page=%d alloc=%02x link=%02x\n",
					lun, scsi_cmdbuf[1] & 1, scsi_cmdbuf[2], scsi_cmdbuf[4], scsi_cmdbuf[5]);
		if(lun) {
			bad_lun();
			return;
		}

		int page = scsi_cmdbuf[2];
		int size = scsi_cmdbuf[4];
		switch(page) {
		case 0:
			std::fill_n(scsi_cmdbuf, 148, 0);

			// vendor and product information must be padded with spaces
			std::fill_n(&scsi_cmdbuf[8], 28, 0x20);

			scsi_cmdbuf[0] = 0x05; // device is present, device is CD/DVD (MMC-3)
			scsi_cmdbuf[1] = 0x80; // media is removable
			scsi_cmdbuf[2] = 0x05; // device complies with SPC-3 standard
			scsi_cmdbuf[3] = 0x02; // response data format = SPC-3 standard
			// some Konami games freak out if this isn't "Sony", so we'll lie
			// this is the actual drive on my Nagano '98 board
			strncpy((char *)&scsi_cmdbuf[8], "Sony", 4);
			strncpy((char *)&scsi_cmdbuf[16], "CDU-76S", 7);
			strncpy((char *)&scsi_cmdbuf[32], "1.0", 3);
			if(size > 148)
				size = 148;
			scsi_data_in(SBUF_MAIN, size);
			break;
		}
		scsi_status_complete(SS_GOOD);
		break;
	}

	case SC_START_STOP_UNIT:
		LOG("command START STOP UNIT\n");
		scsi_status_complete(SS_GOOD);
		break;

	case SC_READ_CAPACITY: {
		if(!cdrom) {
			return_no_cd();
			break;
		}

		LOG("command READ CAPACITY\n");

		// get the last used block on the disc
		const uint32_t temp = cdrom_get_track_start(cdrom, 0xaa) * (bytes_per_sector / bytes_per_block) - 1;

		scsi_cmdbuf[0] = (temp>>24) & 0xff;
		scsi_cmdbuf[1] = (temp>>16) & 0xff;
		scsi_cmdbuf[2] = (temp>>8) & 0xff;
		scsi_cmdbuf[3] = (temp & 0xff);
		scsi_cmdbuf[4] = 0;
		scsi_cmdbuf[5] = 0;
		scsi_cmdbuf[6] = (bytes_per_block>>8)&0xff;
		scsi_cmdbuf[7] = (bytes_per_block & 0xff);

		scsi_data_in(SBUF_MAIN, 8);
		scsi_status_complete(SS_GOOD);
		break;
	}

	case SC_READ_10:
		if(!cdrom) {
			return_no_cd();
			break;
		}

		lba = (scsi_cmdbuf[2]<<24) | (scsi_cmdbuf[3]<<16) | (scsi_cmdbuf[4]<<8) | scsi_cmdbuf[5];
		blocks = (scsi_cmdbuf[7] << 8) | scsi_cmdbuf[8];

		LOG("command READ EXTENDED start=%08x blocks=%04x\n", lba, blocks);

		scsi_data_in(2, blocks*bytes_per_block);
		scsi_status_complete(SS_GOOD);
		break;

	case SC_MODE_SENSE_6: {
		int lun = get_lun(scsi_cmdbuf[1] >> 5);
		LOG("command MODE SENSE 6 lun=%d page=%02x alloc=%02x link=%02x\n",
					lun, scsi_cmdbuf[2] & 0x3f, scsi_cmdbuf[4], scsi_cmdbuf[5]);
		if(lun) {
			bad_lun();
			return;
		}

		int page = scsi_cmdbuf[2] & 0x3f;
		int size = scsi_cmdbuf[4];
		int pos = 1;
		scsi_cmdbuf[pos++] = 0x00; // medium type
		scsi_cmdbuf[pos++] = 0x80; // WP, cache

		// get the last used block on the disc
		const uint32_t temp = cdrom_get_track_start(cdrom, 0xaa) * (bytes_per_sector / bytes_per_block) - 1;
		scsi_cmdbuf[pos++] = 0x08; // Block descriptor length

		scsi_cmdbuf[pos++] = (temp>>24) & 0xff;
		scsi_cmdbuf[pos++] = (temp>>16) & 0xff;
		scsi_cmdbuf[pos++] = (temp>>8) & 0xff;
		scsi_cmdbuf[pos++] = (temp & 0xff);
		scsi_cmdbuf[pos++] = 0;
		scsi_cmdbuf[pos++] = 0;
		scsi_cmdbuf[pos++] = (bytes_per_block>>8)&0xff;
		scsi_cmdbuf[pos++] = (bytes_per_block & 0xff);

		int pmax = page == 0x3f ? 0x3e : page;
		int pmin = page == 0x3f ? 0x00 : page;
		for(int page=pmax; page >= pmin; page--) {
			switch(page) {
			case 0x00: // Unit attention parameters page (weird)
				scsi_cmdbuf[pos++] = 0x80; // PS, page id
				scsi_cmdbuf[pos++] = 0x02; // Page length
				scsi_cmdbuf[pos++] = 0x00; // Meh
				scsi_cmdbuf[pos++] = 0x00; // Double meh
				break;

			case 0x02: // Disconnect/reconnect control parameters (guessed)
				scsi_cmdbuf[pos++] = 0x82; // PS, page id
				scsi_cmdbuf[pos++] = 0x0e; // Page length
				scsi_cmdbuf[pos++] = 0xe6; // Buffer full ratio, 90%
				scsi_cmdbuf[pos++] = 0x1a; // Buffer empty ratio, 10%
				scsi_cmdbuf[pos++] = 0x00; // Bus inactivity limit, 0
				scsi_cmdbuf[pos++] = 0x00;
				scsi_cmdbuf[pos++] = 0x00; // Disconnect time limit, 0
				scsi_cmdbuf[pos++] = 0x00;
				scsi_cmdbuf[pos++] = 0x00; // Connect time limit, 0
				scsi_cmdbuf[pos++] = 0x00;
				scsi_cmdbuf[pos++] = 0x00; // Maximum burst size, 0
				scsi_cmdbuf[pos++] = 0x00;
				scsi_cmdbuf[pos++] = 0x00; // EMDP, Dimm, DTDC
				scsi_cmdbuf[pos++] = 0x00; // Reserved
				scsi_cmdbuf[pos++] = 0x00; // Reserved
				scsi_cmdbuf[pos++] = 0x00; // Reserved
				break;

			default:
				LOG("mode sense page %02x unhandled\n", page);
				break;
			}
		}
		scsi_cmdbuf[0] = pos;
		if(pos > size)
			pos = size;

		scsi_data_in(0, pos);
		scsi_status_complete(SS_GOOD);
		break;
	}

	case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
		// TODO: support eject prevention
		LOG("command PREVENT ALLOW MEDIUM REMOVAL\n");
		scsi_status_complete(SS_GOOD);
		break;

	default:
		logerror("unhandled command %02x\n", scsi_cmdbuf[0]);

		nscsi_full_device::scsi_command();
		break;
	}
}