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