1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 #include "emu.h"
4 #include "bus/nscsi/hd.h"
5 #include "imagedev/harddriv.h"
6 
7 #define LOG_GENERAL (1U << 0)
8 #define LOG_COMMAND (1U << 1)
9 #define LOG_DATA    (1U << 2)
10 
11 #define VERBOSE 0
12 
13 #include "logmacro.h"
14 
15 DEFINE_DEVICE_TYPE(NSCSI_HARDDISK, nscsi_harddisk_device, "scsi_harddisk", "SCSI Hard Disk")
16 
nscsi_harddisk_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)17 nscsi_harddisk_device::nscsi_harddisk_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
18 	nscsi_harddisk_device(mconfig, NSCSI_HARDDISK, tag, owner, clock)
19 {
20 }
21 
nscsi_harddisk_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)22 nscsi_harddisk_device::nscsi_harddisk_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
23 	nscsi_full_device(mconfig, type, tag, owner, clock), image(*this, "image"), harddisk(nullptr), lba(0), cur_lba(0), blocks(0), bytes_per_sector(0)
24 {
25 }
26 
device_start()27 void nscsi_harddisk_device::device_start()
28 {
29 	nscsi_full_device::device_start();
30 	save_item(NAME(block));
31 	save_item(NAME(lba));
32 	save_item(NAME(cur_lba));
33 	save_item(NAME(blocks));
34 	save_item(NAME(bytes_per_sector));
35 }
36 
device_reset()37 void nscsi_harddisk_device::device_reset()
38 {
39 	nscsi_full_device::device_reset();
40 	harddisk = image->get_hard_disk_file();
41 	if(!harddisk) {
42 		scsi_id = -1;
43 		bytes_per_sector = 0;
44 	} else {
45 		const hard_disk_info *hdinfo = hard_disk_get_info(harddisk);
46 		bytes_per_sector = hdinfo->sectorbytes;
47 
48 		chd_file *chd = image->get_chd_file();
49 		if(chd != nullptr)
50 			chd->read_metadata(HARD_DISK_IDENT_METADATA_TAG, 0, m_inquiry_data);
51 	}
52 	cur_lba = -1;
53 }
54 
device_add_mconfig(machine_config & config)55 void nscsi_harddisk_device::device_add_mconfig(machine_config &config)
56 {
57 	HARDDISK(config, image).set_interface("scsi_hdd");
58 }
59 
scsi_get_data(int id,int pos)60 uint8_t nscsi_harddisk_device::scsi_get_data(int id, int pos)
61 {
62 	uint8_t data = 0;
63 	if(id != 2)
64 	{
65 		data = nscsi_full_device::scsi_get_data(id, pos);
66 	}
67 	else
68 	{
69 		int clba = lba + pos / bytes_per_sector;
70 		if(clba != cur_lba) {
71 			cur_lba = clba;
72 			if(!hard_disk_read(harddisk, cur_lba, block)) {
73 				LOG("HD READ ERROR !\n");
74 				memset(block, 0, sizeof(block));
75 			}
76 		}
77 		data = block[pos % bytes_per_sector];
78 	}
79 	LOGMASKED(LOG_DATA, "nscsi_hd: scsi_get_data, id:%d pos:%d data:%02x %c\n", id, pos, data, data >= 0x20 && data < 0x7f ? (char)data : ' ');
80 	return data;
81 }
82 
scsi_put_data(int id,int pos,uint8_t data)83 void nscsi_harddisk_device::scsi_put_data(int id, int pos, uint8_t data)
84 {
85 	LOGMASKED(LOG_DATA, "nscsi_hd: scsi_put_data, id:%d pos:%d data:%02x %c\n", id, pos, data, data >= 0x20 && data < 0x7f ? (char)data : ' ');
86 	if(id != 2) {
87 		nscsi_full_device::scsi_put_data(id, pos, data);
88 		return;
89 	}
90 
91 	int offset = pos % bytes_per_sector;
92 	block[offset] = data;
93 	cur_lba = lba + pos / bytes_per_sector;
94 	if(offset == bytes_per_sector-1) {
95 		if(!hard_disk_write(harddisk, cur_lba, block))
96 			LOG("HD WRITE ERROR !\n");
97 	}
98 }
99 
scsi_command()100 void nscsi_harddisk_device::scsi_command()
101 {
102 	if(scsi_cmdbuf[0] != SC_READ_6) {
103 		LOGMASKED(LOG_COMMAND, "%02x %02x %02x %02x %02x %02x\n",
104 			scsi_cmdbuf[0], scsi_cmdbuf[1], scsi_cmdbuf[2],
105 			scsi_cmdbuf[3], scsi_cmdbuf[4], scsi_cmdbuf[5]);
106 	}
107 
108 	switch(scsi_cmdbuf[0]) {
109 	case SC_TEST_UNIT_READY:
110 		LOG("command TEST UNIT READY\n");
111 		scsi_status_complete(SS_GOOD);
112 		break;
113 
114 	case SC_READ_6:
115 		lba = ((scsi_cmdbuf[1] & 0x1f)<<16) | (scsi_cmdbuf[2]<<8) | scsi_cmdbuf[3];
116 		blocks = scsi_cmdbuf[4];
117 		if(!blocks)
118 			blocks = 256;
119 
120 		LOG("command READ start=%08x blocks=%04x\n", lba, blocks);
121 
122 		if(hard_disk_read(harddisk, lba, block)) {
123 			scsi_data_in(2, blocks*bytes_per_sector);
124 			scsi_status_complete(SS_GOOD);
125 		}
126 		else
127 		{
128 			scsi_status_complete(SS_CHECK_CONDITION);
129 			sense(false, SK_ILLEGAL_REQUEST, SK_ASC_INVALID_FIELD_IN_CDB);
130 		}
131 		break;
132 
133 	case SC_WRITE_6:
134 		lba = ((scsi_cmdbuf[1] & 0x1f)<<16) | (scsi_cmdbuf[2]<<8) | scsi_cmdbuf[3];
135 		blocks = scsi_cmdbuf[4];
136 		if(!blocks)
137 			blocks = 256;
138 
139 		LOG("command WRITE start=%08x blocks=%04x\n", lba, blocks);
140 
141 		scsi_data_out(2, blocks*bytes_per_sector);
142 		scsi_status_complete(SS_GOOD);
143 		break;
144 
145 	case SC_INQUIRY: {
146 		int lun = get_lun(scsi_cmdbuf[1] >> 5);
147 		LOG("command INQUIRY lun=%d EVPD=%d page=%d alloc=%02x link=%02x\n",
148 			lun, scsi_cmdbuf[1] & 1, scsi_cmdbuf[2], scsi_cmdbuf[4], scsi_cmdbuf[5]);
149 
150 		int page = scsi_cmdbuf[2];
151 		int size = scsi_cmdbuf[4];
152 		switch(page) {
153 		case 0:
154 			std::fill_n(scsi_cmdbuf, 148, 0);
155 
156 			// vendor and product information must be padded with spaces
157 			std::fill_n(&scsi_cmdbuf[8], 28, 0x20);
158 
159 			// From Seagate SCSI Commands Reference Manual (http://www.seagate.com/staticfiles/support/disc/manuals/scsi/100293068a.pdf), page 73:
160 			// If the SCSI target device is not capable of supporting a peripheral device connected to this logical unit, the
161 			// device server shall set these fields to 7Fh (i.e., PERIPHERAL QUALIFIER field set to 011b and PERIPHERAL DEVICE
162 			// TYPE set to 1Fh).
163 			if (lun != 0)
164 				scsi_cmdbuf[0] = 0x7f;
165 			else
166 				scsi_cmdbuf[0] = 0x00; // device is direct-access (e.g. hard disk)
167 			scsi_cmdbuf[1] = 0x00; // media is not removable
168 			scsi_cmdbuf[2] = 0x05; // device complies with SPC-3 standard
169 			scsi_cmdbuf[3] = 0x01; // response data format = CCS
170 			scsi_cmdbuf[4] = 52;   // additional length
171 			if(m_inquiry_data.empty()) {
172 				LOG("IDNT tag not found in chd metadata, using default inquiry data\n");
173 
174 				// Apple HD SC setup utility needs to see this
175 				strcpy((char *)&scsi_cmdbuf[8], " SEAGATE");
176 				strcpy((char *)&scsi_cmdbuf[16], "          ST225N");
177 				strcpy((char *)&scsi_cmdbuf[32], "1.00");
178 				scsi_cmdbuf[36] = 0x00; // # of extents high
179 				scsi_cmdbuf[37] = 0x08; // # of extents low
180 				scsi_cmdbuf[38] = 0x00; // group 0 commands 0-1f
181 				scsi_cmdbuf[39] = 0x99; // commands 0,3,4,7
182 				scsi_cmdbuf[40] = 0xa0; // commands 8, a
183 				scsi_cmdbuf[41] = 0x27; // commands 12,15,16,17
184 				scsi_cmdbuf[42] = 0x34; // commands 1a,1b,1d
185 				scsi_cmdbuf[43] = 0x01; // group 1 commands 20-3f
186 				scsi_cmdbuf[44] = 0x04;
187 				scsi_cmdbuf[45] = 0xa0;
188 				scsi_cmdbuf[46] = 0x01;
189 				scsi_cmdbuf[47] = 0x18;
190 				scsi_cmdbuf[48] = 0x07; // group 7 commands e0-ff
191 				scsi_cmdbuf[49] = 0x00;
192 				scsi_cmdbuf[50] = 0xa0; // commands 8, a
193 				scsi_cmdbuf[51] = 0x00;
194 				scsi_cmdbuf[52] = 0x00;
195 				scsi_cmdbuf[53] = 0xff; // end of list
196 			}
197 			else
198 				std::copy_n(m_inquiry_data.begin(), std::min(m_inquiry_data.size(), size_t(48)), &scsi_cmdbuf[8]);
199 
200 			if(size > 56)
201 				size = 56;
202 			scsi_data_in(0, size);
203 			break;
204 		}
205 		scsi_status_complete(SS_GOOD);
206 		break;
207 	}
208 
209 	case SC_MODE_SENSE_6: {
210 		int lun = get_lun(scsi_cmdbuf[1] >> 5);
211 		LOG("command MODE SENSE 6 lun=%d page=%02x alloc=%02x link=%02x\n",
212 			lun, scsi_cmdbuf[2] & 0x3f, scsi_cmdbuf[4], scsi_cmdbuf[5]);
213 		if(lun) {
214 			bad_lun();
215 			return;
216 		}
217 
218 		int page = scsi_cmdbuf[2] & 0x3f;
219 		int size = scsi_cmdbuf[4];
220 		int pos = 1;
221 		scsi_cmdbuf[pos++] = 0x00; // medium type
222 		scsi_cmdbuf[pos++] = 0x00; // WP, cache
223 
224 		hard_disk_info *info = hard_disk_get_info(harddisk);
225 		uint32_t dsize = info->cylinders * info->heads * info->sectors - 1;
226 		scsi_cmdbuf[pos++] = 0x08; // Block descriptor length
227 		scsi_cmdbuf[pos++] = 0x00;
228 		scsi_cmdbuf[pos++] = (dsize>>16) & 0xff;
229 		scsi_cmdbuf[pos++] = (dsize>>8) & 0xff;
230 		scsi_cmdbuf[pos++] = (dsize & 0xff);
231 		scsi_cmdbuf[pos++] = 0x00;
232 		scsi_cmdbuf[pos++] = (info->sectorbytes>>16)&0xff;
233 		scsi_cmdbuf[pos++] = (info->sectorbytes>>8)&0xff;
234 		scsi_cmdbuf[pos++] = (info->sectorbytes & 0xff);
235 
236 		int pmax = page == 0x3f ? 0x3e : page;
237 		int pmin = page == 0x3f ? 0x00 : page;
238 
239 		bool fail = false;
240 		for(int p=pmax; p >= pmin; p--) {
241 			switch(p) {
242 			case 0x00: // Unit attention parameters page (weird)
243 				scsi_cmdbuf[pos++] = 0x80; // PS, page id
244 				scsi_cmdbuf[pos++] = 0x02; // Page length
245 				scsi_cmdbuf[pos++] = 0x00; // Meh
246 				scsi_cmdbuf[pos++] = 0x00; // Double meh
247 				break;
248 
249 			case 0x01: // read-write error recovery page
250 				scsi_cmdbuf[pos++] = 0x01; // !PS, page id
251 				scsi_cmdbuf[pos++] = 0x0a; // page length
252 				scsi_cmdbuf[pos++] = 0; // various bits
253 				scsi_cmdbuf[pos++] = 0; // read retry count
254 				scsi_cmdbuf[pos++] = 0; // correction span
255 				scsi_cmdbuf[pos++] = 0; // head offset count
256 				scsi_cmdbuf[pos++] = 0; // data strobe offset count
257 				scsi_cmdbuf[pos++] = 0; // reserved
258 				scsi_cmdbuf[pos++] = 0; // write retry count
259 				scsi_cmdbuf[pos++] = 0; // reserved
260 				scsi_cmdbuf[pos++] = 0; // recovery time limit (msb)
261 				scsi_cmdbuf[pos++] = 0; // recovery time limit (lsb)
262 				break;
263 
264 			case 0x02: // disconnect-reconnect page
265 				scsi_cmdbuf[pos++] = 0x02; // !PS, page id
266 				scsi_cmdbuf[pos++] = 0x0e; // page length
267 				scsi_cmdbuf[pos++] = 0; // buffer full ratio
268 				scsi_cmdbuf[pos++] = 0; // buffer empty ratio
269 				scsi_cmdbuf[pos++] = 0; // bus inactivity limit (msb)
270 				scsi_cmdbuf[pos++] = 0; // bus inactivity limit (lsb)
271 				scsi_cmdbuf[pos++] = 0; // disconnect time limit (msb)
272 				scsi_cmdbuf[pos++] = 0; // disconnect time limit (lsb)
273 				scsi_cmdbuf[pos++] = 0; // connect time limit (msb)
274 				scsi_cmdbuf[pos++] = 0; // connect time limit (lsb)
275 				scsi_cmdbuf[pos++] = 0; // maximum burst size (msb)
276 				scsi_cmdbuf[pos++] = 0; // maximum burst size (lsb)
277 				scsi_cmdbuf[pos++] = 0; // reserved
278 				scsi_cmdbuf[pos++] = 0; // reserved
279 				scsi_cmdbuf[pos++] = 0; // reserved
280 				scsi_cmdbuf[pos++] = 0; // reserved
281 				break;
282 
283 			case 0x03:  { // Format parameters page
284 				scsi_cmdbuf[pos++] = 0x83; // PS, page id
285 				scsi_cmdbuf[pos++] = 0x16; // Page length
286 				scsi_cmdbuf[pos++] = (info->cylinders * info->heads) >> 8; // Track/zone
287 				scsi_cmdbuf[pos++] = info->cylinders * info->heads;        // Track/zone
288 				scsi_cmdbuf[pos++] = 0x00; // Alt sect/zone
289 				scsi_cmdbuf[pos++] = 0x00; // Alt sect/zone
290 				scsi_cmdbuf[pos++] = 0x00; // Alt track/zone
291 				scsi_cmdbuf[pos++] = 0x00; // Alt track/zone
292 				scsi_cmdbuf[pos++] = 0x00; // Alt track/volume
293 				scsi_cmdbuf[pos++] = 0x00; // Alt track/volume
294 				scsi_cmdbuf[pos++] = info->sectors >> 8; // Sectors/track
295 				scsi_cmdbuf[pos++] = info->sectors;      // Sectors/track
296 				scsi_cmdbuf[pos++] = info->sectorbytes >> 8; // Bytes/sector
297 				scsi_cmdbuf[pos++] = info->sectorbytes;      // Bytes/sector
298 				scsi_cmdbuf[pos++] = 0x00; // Interleave
299 				scsi_cmdbuf[pos++] = 0x00; // Interleave
300 				scsi_cmdbuf[pos++] = 0x00; // Track skew
301 				scsi_cmdbuf[pos++] = 0x00; // Track skew
302 				scsi_cmdbuf[pos++] = 0x00; // Cylinder skew
303 				scsi_cmdbuf[pos++] = 0x00; // Cylinder skew
304 				scsi_cmdbuf[pos++] = 0x00; // Sectoring type
305 				scsi_cmdbuf[pos++] = 0x00; // Reserved
306 				scsi_cmdbuf[pos++] = 0x00; // Reserved
307 				scsi_cmdbuf[pos++] = 0x00; // Reserved
308 				break;
309 			}
310 
311 			case 0x04: { // Rigid drive geometry page
312 				scsi_cmdbuf[pos++] = 0x84; // PS, page id
313 				scsi_cmdbuf[pos++] = 0x16; // Page length
314 				scsi_cmdbuf[pos++] = info->cylinders >> 16; // Cylinders
315 				scsi_cmdbuf[pos++] = info->cylinders >> 8;  // Cylinders
316 				scsi_cmdbuf[pos++] = info->cylinders;       // Cylinders
317 				scsi_cmdbuf[pos++] = info->heads;           // Heads
318 				scsi_cmdbuf[pos++] = 0x00;                  // Starting cylinder - write precomp
319 				scsi_cmdbuf[pos++] = 0x00;                  // Starting cylinder - write precomp
320 				scsi_cmdbuf[pos++] = 0x00;                  // Starting cylinder - write precomp
321 				scsi_cmdbuf[pos++] = 0x00;                  // Starting cylinder - reduced write current
322 				scsi_cmdbuf[pos++] = 0x00;                  // Starting cylinder - reduced write current
323 				scsi_cmdbuf[pos++] = 0x00;                  // Starting cylinder - reduced write current
324 				scsi_cmdbuf[pos++] = 0x00;                  // Drive step rate
325 				scsi_cmdbuf[pos++] = 0x00;                  // Drive step rate
326 				scsi_cmdbuf[pos++] = 0x00;                  // Landing zone cylinder
327 				scsi_cmdbuf[pos++] = 0x00;                  // Landing zone cylinder
328 				scsi_cmdbuf[pos++] = 0x00;                  // Landing zone cylinder
329 				scsi_cmdbuf[pos++] = 0x00;                  // RPL
330 				scsi_cmdbuf[pos++] = 0x00;                  // Rotational offset
331 				scsi_cmdbuf[pos++] = 0x00;                  // Reserved
332 				scsi_cmdbuf[pos++] = uint8_t(10000 >> 8);     // Medium rotation rate
333 				scsi_cmdbuf[pos++] = uint8_t(10000);          // Medium rotation rate
334 				scsi_cmdbuf[pos++] = 0x00;                  // Reserved
335 				scsi_cmdbuf[pos++] = 0x00;                  // Reserved
336 				break;
337 			}
338 
339 			case 0x08: // caching page
340 				scsi_cmdbuf[pos++] = 0x08; // !PS, page id
341 				scsi_cmdbuf[pos++] = 0x0a; // page length
342 				scsi_cmdbuf[pos++] = 0;
343 				scsi_cmdbuf[pos++] = 0;
344 				scsi_cmdbuf[pos++] = 0;
345 				scsi_cmdbuf[pos++] = 0;
346 				scsi_cmdbuf[pos++] = 0;
347 				scsi_cmdbuf[pos++] = 0;
348 				scsi_cmdbuf[pos++] = 0;
349 				scsi_cmdbuf[pos++] = 0;
350 				scsi_cmdbuf[pos++] = 0;
351 				scsi_cmdbuf[pos++] = 0;
352 				break;
353 
354 			case 0x30: { // Apple firmware ID page
355 				scsi_cmdbuf[pos++] = 0xb0; // cPS, page id
356 				scsi_cmdbuf[pos++] = 0x16; // Page length
357 				scsi_cmdbuf[pos++] = 'A';
358 				scsi_cmdbuf[pos++] = 'P';
359 				scsi_cmdbuf[pos++] = 'P';
360 				scsi_cmdbuf[pos++] = 'L';
361 				scsi_cmdbuf[pos++] = 'E';
362 				scsi_cmdbuf[pos++] = ' ';
363 				scsi_cmdbuf[pos++] = 'C';
364 				scsi_cmdbuf[pos++] = 'O';
365 				scsi_cmdbuf[pos++] = 'M';
366 				scsi_cmdbuf[pos++] = 'P';
367 				scsi_cmdbuf[pos++] = 'U';
368 				scsi_cmdbuf[pos++] = 'T';
369 				scsi_cmdbuf[pos++] = 'E';
370 				scsi_cmdbuf[pos++] = 'R';
371 				scsi_cmdbuf[pos++] = ',';
372 				scsi_cmdbuf[pos++] = ' ';
373 				scsi_cmdbuf[pos++] = 'I';
374 				scsi_cmdbuf[pos++] = 'N';
375 				scsi_cmdbuf[pos++] = 'C';
376 				scsi_cmdbuf[pos++] = ' ';
377 				scsi_cmdbuf[pos++] = ' ';
378 				scsi_cmdbuf[pos++] = ' ';
379 				break;
380 			}
381 
382 			default:
383 				if (page != 0x3f) {
384 					LOG("mode sense page %02x unhandled\n", page);
385 					fail = true;
386 				}
387 				break;
388 			}
389 		}
390 
391 		if (!fail) {
392 			scsi_cmdbuf[0] = pos;
393 			if (pos > size)
394 				pos = size;
395 
396 			scsi_data_in(0, pos);
397 			scsi_status_complete(SS_GOOD);
398 		} else {
399 			scsi_status_complete(SS_CHECK_CONDITION);
400 			sense(false, SK_ILLEGAL_REQUEST, SK_ASC_INVALID_FIELD_IN_CDB);
401 		}
402 		break;
403 	}
404 
405 	case SC_START_STOP_UNIT:
406 		LOG("command %s UNIT\n", (scsi_cmdbuf[4] & 0x1) ? "START" : "STOP");
407 		scsi_status_complete(SS_GOOD);
408 		break;
409 
410 	case SC_RECIEVE_DIAG_RES: {
411 		LOG("command RECIEVE DIAGNOSTICS RESULTS\n");
412 		int size = (scsi_cmdbuf[3] << 8) | scsi_cmdbuf[4];
413 		int pos = 0;
414 		scsi_cmdbuf[pos++] = 0;
415 		scsi_cmdbuf[pos++] = 6;
416 		scsi_cmdbuf[pos++] = 0; // ROM is OK
417 		scsi_cmdbuf[pos++] = 0; // RAM is OK
418 		scsi_cmdbuf[pos++] = 0; // Data buffer is OK
419 		scsi_cmdbuf[pos++] = 0; // Interface is OK
420 		scsi_cmdbuf[pos++] = 0;
421 		if(size > pos)
422 			size = pos;
423 		scsi_data_in(0, size);
424 		scsi_status_complete(SS_GOOD);
425 		break;
426 	}
427 
428 	case SC_SEND_DIAGNOSTICS: {
429 		LOG("command SEND DIAGNOSTICS\n");
430 		int size = (scsi_cmdbuf[3] << 8) | scsi_cmdbuf[4];
431 		if(scsi_cmdbuf[1] & 4) {
432 			// Self-test
433 			scsi_status_complete(SS_GOOD);
434 			break;
435 		}
436 		int pos = 0;
437 		scsi_cmdbuf[pos++] = 0;
438 		scsi_cmdbuf[pos++] = 6;
439 		scsi_cmdbuf[pos++] = 0; // ROM is OK
440 		scsi_cmdbuf[pos++] = 0; // RAM is OK
441 		scsi_cmdbuf[pos++] = 0; // Data buffer is OK
442 		scsi_cmdbuf[pos++] = 0; // Interface is OK
443 		scsi_cmdbuf[pos++] = 0;
444 		scsi_cmdbuf[pos++] = 0;
445 		if(size > pos)
446 			size = pos;
447 		scsi_data_in(0, size);
448 		scsi_status_complete(SS_GOOD);
449 		break;
450 	}
451 
452 	case SC_READ_CAPACITY: {
453 		LOG("command READ CAPACITY\n");
454 
455 		hard_disk_info *info = hard_disk_get_info(harddisk);
456 		uint32_t size = info->cylinders * info->heads * info->sectors - 1;
457 
458 		scsi_cmdbuf[0] = (size>>24) & 0xff;
459 		scsi_cmdbuf[1] = (size>>16) & 0xff;
460 		scsi_cmdbuf[2] = (size>>8) & 0xff;
461 		scsi_cmdbuf[3] = (size & 0xff);
462 		scsi_cmdbuf[4] = (info->sectorbytes>>24)&0xff;
463 		scsi_cmdbuf[5] = (info->sectorbytes>>16)&0xff;
464 		scsi_cmdbuf[6] = (info->sectorbytes>>8)&0xff;
465 		scsi_cmdbuf[7] = (info->sectorbytes & 0xff);
466 
467 		scsi_data_in(0, 8);
468 		scsi_status_complete(SS_GOOD);
469 		break;
470 	}
471 
472 	case SC_READ_10:
473 		lba = (scsi_cmdbuf[2]<<24) | (scsi_cmdbuf[3]<<16) | (scsi_cmdbuf[4]<<8) | scsi_cmdbuf[5];
474 		blocks = (scsi_cmdbuf[7] << 8) | scsi_cmdbuf[8];
475 
476 		LOG("command READ EXTENDED start=%08x blocks=%04x\n",lba, blocks);
477 
478 		if(hard_disk_read(harddisk, lba, block)) {
479 			scsi_data_in(2, blocks*bytes_per_sector);
480 			scsi_status_complete(SS_GOOD);
481 		}
482 		else
483 		{
484 			scsi_status_complete(SS_CHECK_CONDITION);
485 			sense(false, SK_ILLEGAL_REQUEST, SK_ASC_INVALID_FIELD_IN_CDB);
486 		}
487 		break;
488 
489 	case SC_WRITE_10:
490 		lba = (scsi_cmdbuf[2]<<24) | (scsi_cmdbuf[3]<<16) | (scsi_cmdbuf[4]<<8) | scsi_cmdbuf[5];
491 		blocks = (scsi_cmdbuf[7] << 8) | scsi_cmdbuf[8];
492 
493 		LOG("command WRITE EXTENDED start=%08x blocks=%04x\n", lba, blocks);
494 
495 		scsi_data_out(2, blocks*bytes_per_sector);
496 		scsi_status_complete(SS_GOOD);
497 		break;
498 
499 	case SC_FORMAT_UNIT:
500 		LOG("command FORMAT UNIT:%s%s%s%s%s\n",
501 				(scsi_cmdbuf[1] & 0x80) ? " FMT-PINFO" : "",
502 				(scsi_cmdbuf[1] & 0x40) ? " RTO_REQ" : "",
503 				(scsi_cmdbuf[1] & 0x20) ? " LONG-LIST" : "",
504 				(scsi_cmdbuf[1] & 0x10) ? " FMTDATA" : "",
505 				(scsi_cmdbuf[1] & 0x08) ? " CMPLIST" : "");
506 		{
507 			hard_disk_info *info = hard_disk_get_info(harddisk);
508 			auto block = std::make_unique<uint8_t[]>(info->sectorbytes);
509 			for(int cyl = 0; cyl < info->cylinders; cyl++) {
510 				for(int head = 0; head < info->heads; head++) {
511 					for(int sector = 0; sector < info->sectors; sector++) {
512 						hard_disk_write(harddisk, cyl * head * sector, block.get());
513 					}
514 				}
515 			}
516 		}
517 		scsi_status_complete(SS_GOOD);
518 		break;
519 
520 	case SC_MODE_SELECT_6:
521 		LOG("command MODE SELECT\n");
522 		scsi_status_complete(SS_GOOD);
523 		break;
524 
525 	case SC_VERIFY:
526 		LOG("command VERIFY BytChk %d\n", !!(scsi_cmdbuf[1] & 0x02));
527 		if (!(scsi_cmdbuf[1] & 0x02))
528 			scsi_status_complete(SS_GOOD);
529 		else
530 			scsi_unknown_command();
531 		break;
532 
533 	default:
534 		LOG("command %02x ***UNKNOWN***\n", scsi_cmdbuf[0]);
535 		nscsi_full_device::scsi_command();
536 		break;
537 	}
538 }
539