/[gxemul]/upstream/0.4.4/src/devices/dev_wdc.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.4.4/src/devices/dev_wdc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 35 - (show annotations)
Mon Oct 8 16:21:26 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 25247 byte(s)
0.4.4
1 /*
2 * Copyright (C) 2004-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_wdc.c,v 1.74 2007/02/16 19:57:56 debug Exp $
29 *
30 * Standard "wdc" IDE controller.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cpu.h"
38 #include "device.h"
39 #include "diskimage.h"
40 #include "machine.h"
41 #include "memory.h"
42 #include "misc.h"
43
44 #include "wdcreg.h"
45
46 #define DEV_WDC_LENGTH 8
47 #define WDC_TICK_SHIFT 14
48 #define WDC_MAX_SECTORS 512
49 #define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1))
50
51 extern int quiet_mode;
52
53 /* #define debug fatal */
54
55 struct wdc_data {
56 struct interrupt irq;
57 int addr_mult;
58 int base_drive;
59 int data_debug;
60 int io_enabled;
61
62 /* Cached values: */
63 int cyls[2];
64 int heads[2];
65 int sectors_per_track[2];
66
67 unsigned char *inbuf;
68 int inbuf_head;
69 int inbuf_tail;
70
71 int int_assert;
72
73 int write_in_progress;
74 int write_count;
75 int64_t write_offset;
76
77 int error;
78 int precomp;
79 int seccnt;
80 int sector;
81 int cyl_lo;
82 int cyl_hi;
83 int sectorsize;
84 int lba;
85 int drive;
86 int head;
87 int cur_command;
88
89 int atapi_cmd_in_progress;
90 int atapi_phase;
91 struct scsi_transfer *atapi_st;
92 int atapi_len;
93 size_t atapi_received;
94
95 unsigned char identify_struct[512];
96 };
97
98
99 #define COMMAND_RESET 0x100
100
101
102 DEVICE_TICK(wdc)
103 {
104 struct wdc_data *d = extra;
105
106 if (d->int_assert)
107 INTERRUPT_ASSERT(d->irq);
108 }
109
110
111 /*
112 * wdc_set_io_enabled():
113 *
114 * Set io_enabled to zero to disable the I/O registers temporarily (e.g.
115 * used by PCI code in NetBSD to detect whether multiple controllers collide
116 * in I/O space).
117 *
118 * Return value is old contents of the io_enabled variable.
119 */
120 int wdc_set_io_enabled(struct wdc_data *d, int io_enabled)
121 {
122 int old = d->io_enabled;
123 d->io_enabled = io_enabled;
124 return old;
125 }
126
127
128 /*
129 * wdc_addtoinbuf():
130 *
131 * Write to the inbuf at its head, read at its tail.
132 */
133 static void wdc_addtoinbuf(struct wdc_data *d, int c)
134 {
135 d->inbuf[d->inbuf_head] = c;
136
137 d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE;
138 if (d->inbuf_head == d->inbuf_tail)
139 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
140 " Increase WDC_MAX_SECTORS. ]\n");
141 }
142
143
144 /*
145 * wdc_get_inbuf():
146 *
147 * Read from the tail of inbuf.
148 */
149 static uint64_t wdc_get_inbuf(struct wdc_data *d)
150 {
151 int c = d->inbuf[d->inbuf_tail];
152
153 if (d->inbuf_head == d->inbuf_tail) {
154 fatal("[ wdc: WARNING! someone is reading too much from the "
155 "wdc inbuf! ]\n");
156 return -1;
157 }
158
159 d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
160 return c;
161 }
162
163
164 /*
165 * wdc_initialize_identify_struct():
166 */
167 static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
168 {
169 uint64_t total_size;
170 int flags, cdrom = 0;
171 char namebuf[40];
172
173 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
174 DISKIMAGE_IDE);
175 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
176 DISKIMAGE_IDE))
177 cdrom = 1;
178
179 memset(d->identify_struct, 0, sizeof(d->identify_struct));
180
181 /* Offsets are in 16-bit WORDS! High byte, then low. */
182
183 /* 0: general flags */
184 flags = 1 << 6; /* Fixed */
185 if (cdrom)
186 flags = 0x8580; /* ATAPI, CDROM, removable */
187 d->identify_struct[2 * 0 + 0] = flags >> 8;
188 d->identify_struct[2 * 0 + 1] = flags;
189
190 /* 1: nr of cylinders */
191 d->identify_struct[2 * 1 + 0] = d->cyls[d->drive] >> 8;
192 d->identify_struct[2 * 1 + 1] = d->cyls[d->drive];
193
194 /* 3: nr of heads */
195 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
196 d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
197
198 /* 6: sectors per track */
199 d->identify_struct[2 * 6 + 0] = d->sectors_per_track[d->drive] >> 8;
200 d->identify_struct[2 * 6 + 1] = d->sectors_per_track[d->drive];
201
202 /* 10-19: Serial number */
203 memcpy(&d->identify_struct[2 * 10], "#0 ", 20);
204
205 /* 23-26: Firmware version */
206 memcpy(&d->identify_struct[2 * 23], "1.0 ", 8);
207
208 /* 27-46: Model number */
209 if (diskimage_getname(cpu->machine, d->drive + d->base_drive,
210 DISKIMAGE_IDE, namebuf, sizeof(namebuf))) {
211 size_t i;
212 for (i=0; i<sizeof(namebuf); i++)
213 if (namebuf[i] == 0) {
214 for (; i<sizeof(namebuf); i++)
215 namebuf[i] = ' ';
216 break;
217 }
218 memcpy(&d->identify_struct[2 * 27], namebuf, 40);
219 } else
220 memcpy(&d->identify_struct[2 * 27],
221 "Fake GXemul IDE disk ", 40);
222
223 /* 47: max sectors per multitransfer */
224 d->identify_struct[2 * 47 + 0] = 0x80;
225 d->identify_struct[2 * 47 + 1] = 128;
226
227 /* 49: capabilities: */
228 /* (0x200 = LBA, 0x100 = DMA support.) */
229 d->identify_struct[2 * 49 + 0] = 0;
230 d->identify_struct[2 * 49 + 1] = 0;
231
232 /* 51: PIO timing mode. */
233 d->identify_struct[2 * 51 + 0] = 0x00; /* ? */
234 d->identify_struct[2 * 51 + 1] = 0x00;
235
236 /* 53: 0x02 = fields 64-70 valid, 0x01 = fields 54-58 valid */
237 d->identify_struct[2 * 53 + 0] = 0x00;
238 d->identify_struct[2 * 53 + 1] = 0x02;
239
240 /* 57-58: current capacity in sectors */
241 d->identify_struct[2 * 57 + 0] = ((total_size / 512) >> 24) % 255;
242 d->identify_struct[2 * 57 + 1] = ((total_size / 512) >> 16) % 255;
243 d->identify_struct[2 * 58 + 0] = ((total_size / 512) >> 8) % 255;
244 d->identify_struct[2 * 58 + 1] = (total_size / 512) & 255;
245
246 /* 60-61: total nr of addressable sectors */
247 d->identify_struct[2 * 60 + 0] = ((total_size / 512) >> 24) % 255;
248 d->identify_struct[2 * 60 + 1] = ((total_size / 512) >> 16) % 255;
249 d->identify_struct[2 * 61 + 0] = ((total_size / 512) >> 8) % 255;
250 d->identify_struct[2 * 61 + 1] = (total_size / 512) & 255;
251
252 /* 64: Advanced PIO mode support. 0x02 = mode4, 0x01 = mode3 */
253 d->identify_struct[2 * 64 + 0] = 0x00;
254 d->identify_struct[2 * 64 + 1] = 0x03;
255
256 /* 67, 68: PIO timing */
257 d->identify_struct[2 * 67 + 0] = 0;
258 d->identify_struct[2 * 67 + 1] = 120;
259 d->identify_struct[2 * 68 + 0] = 0;
260 d->identify_struct[2 * 68 + 1] = 120;
261 }
262
263
264 /*
265 * wdc__read():
266 */
267 void wdc__read(struct cpu *cpu, struct wdc_data *d)
268 {
269 #define MAX_SECTORS_PER_CHUNK 64
270 const int max_sectors_per_chunk = MAX_SECTORS_PER_CHUNK;
271 unsigned char buf[512 * MAX_SECTORS_PER_CHUNK];
272 int i, cyl = d->cyl_hi * 256+ d->cyl_lo;
273 int count = d->seccnt? d->seccnt : 256;
274 uint64_t offset = 512 * (d->sector - 1
275 + (int64_t)d->head * d->sectors_per_track[d->drive] +
276 (int64_t)d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
277
278 #if 0
279 /* LBA: */
280 if (d->lba)
281 offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8)
282 + d->sector);
283 printf("WDC read from offset %lli\n", (long long)offset);
284 #endif
285
286 while (count > 0) {
287 int to_read = count > max_sectors_per_chunk?
288 max_sectors_per_chunk : count;
289
290 /* TODO: result code from the read? */
291
292 if (d->inbuf_head + 512 * to_read <= WDC_INBUF_SIZE) {
293 diskimage_access(cpu->machine, d->drive + d->base_drive,
294 DISKIMAGE_IDE, 0, offset,
295 d->inbuf + d->inbuf_head, 512 * to_read);
296 d->inbuf_head += 512 * to_read;
297 if (d->inbuf_head == WDC_INBUF_SIZE)
298 d->inbuf_head = 0;
299 } else {
300 diskimage_access(cpu->machine, d->drive + d->base_drive,
301 DISKIMAGE_IDE, 0, offset, buf, 512 * to_read);
302 for (i=0; i<512 * to_read; i++)
303 wdc_addtoinbuf(d, buf[i]);
304 }
305
306 offset += 512 * to_read;
307 count -= to_read;
308 }
309
310 d->int_assert = 1;
311 }
312
313
314 /*
315 * wdc__write():
316 */
317 void wdc__write(struct cpu *cpu, struct wdc_data *d)
318 {
319 int cyl = d->cyl_hi * 256+ d->cyl_lo;
320 int count = d->seccnt? d->seccnt : 256;
321 uint64_t offset = 512 * (d->sector - 1
322 + (int64_t)d->head * d->sectors_per_track[d->drive] +
323 (int64_t)d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
324 #if 0
325 /* LBA: */
326 if (d->lba)
327 offset = 512 * (((d->head & 0xf) << 24) +
328 (cyl << 8) + d->sector);
329 printf("WDC write to offset %lli\n", (long long)offset);
330 #endif
331
332 d->write_in_progress = d->cur_command;
333 d->write_count = count;
334 d->write_offset = offset;
335
336 /* TODO: result code? */
337 }
338
339
340 /*
341 * status_byte():
342 *
343 * Return a reasonable status byte corresponding to the controller's current
344 * state.
345 */
346 static int status_byte(struct wdc_data *d, struct cpu *cpu)
347 {
348 int odata = 0;
349 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
350 DISKIMAGE_IDE))
351 odata |= WDCS_DRDY | WDCS_DSC;
352 if (d->inbuf_head != d->inbuf_tail)
353 odata |= WDCS_DRQ;
354 if (d->write_in_progress)
355 odata |= WDCS_DRQ;
356 if (d->error)
357 odata |= WDCS_ERR;
358 if (d->atapi_cmd_in_progress && (d->atapi_phase & WDCS_DRQ)) {
359 odata |= WDCS_DRQ;
360 }
361 return odata;
362 }
363
364
365 DEVICE_ACCESS(wdc_altstatus)
366 {
367 struct wdc_data *d = extra;
368 uint64_t idata = 0, odata = 0;
369
370 idata = data[0];
371
372 /* Same as the normal status byte: */
373 odata = status_byte(d, cpu);
374
375 if (writeflag==MEM_READ)
376 debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
377 (int)odata);
378 else {
379 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
380 (int)idata);
381 if (idata & WDCTL_4BIT)
382 d->cur_command = COMMAND_RESET;
383 }
384
385 if (writeflag == MEM_READ)
386 data[0] = odata;
387
388 return 1;
389 }
390
391
392 /*
393 * wdc_command():
394 */
395 void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata)
396 {
397 size_t i;
398
399 d->cur_command = idata;
400 d->atapi_cmd_in_progress = 0;
401 d->error = 0;
402
403 /*
404 * Disk images that do not exist return an ABORT error. This also
405 * happens with CDROM images with the WDCC_IDENTIFY command; CDROM
406 * images must be detected with ATAPI_IDENTIFY_DEVICE instead.
407 *
408 * TODO: Is this correct/good behaviour?
409 */
410 if (!diskimage_exist(cpu->machine, d->drive + d->base_drive,
411 DISKIMAGE_IDE)) {
412 debug("[ wdc: command 0x%02x drive %i, but no disk image ]\n",
413 d->cur_command, d->drive + d->base_drive);
414 d->error |= WDCE_ABRT;
415 d->int_assert = 1;
416 return;
417 }
418 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
419 DISKIMAGE_IDE) && d->cur_command == WDCC_IDENTIFY) {
420 debug("[ wdc: IDENTIFY drive %i, but it is an ATAPI "
421 "drive ]\n", d->drive + d->base_drive);
422 d->error |= WDCE_ABRT;
423 d->int_assert = 1;
424 return;
425 }
426
427 /* Handle the command: */
428 switch (d->cur_command) {
429
430 case WDCC_READ:
431 case WDCC_READMULTI:
432 if (!quiet_mode)
433 debug("[ wdc: READ from drive %i, head %i, cyl %i, "
434 "sector %i, nsecs %i ]\n", d->drive, d->head,
435 d->cyl_hi*256+d->cyl_lo, d->sector, d->seccnt);
436 wdc__read(cpu, d);
437 break;
438
439 case WDCC_WRITE:
440 case WDCC_WRITEMULTI:
441 if (!quiet_mode)
442 debug("[ wdc: WRITE to drive %i, head %i, cyl %i, "
443 "sector %i, nsecs %i ]\n", d->drive, d->head,
444 d->cyl_hi*256+d->cyl_lo, d->sector, d->seccnt);
445 wdc__write(cpu, d);
446 break;
447
448 case WDCC_IDP: /* Initialize drive parameters */
449 debug("[ wdc: IDP drive %i (TODO) ]\n", d->drive);
450 /* TODO */
451 d->int_assert = 1;
452 break;
453
454 case SET_FEATURES:
455 debug("[ wdc: SET_FEATURES drive %i (TODO), feature 0x%02x ]\n",
456 d->drive, d->precomp);
457 /* TODO */
458 switch (d->precomp) {
459 case WDSF_SET_MODE:
460 debug("[ wdc: WDSF_SET_MODE drive %i, pio/dma flags "
461 "0x%02x ]\n", d->drive, d->seccnt);
462 break;
463 default:d->error |= WDCE_ABRT;
464 }
465 /* TODO: always interrupt? */
466 d->int_assert = 1;
467 break;
468
469 case WDCC_RECAL:
470 debug("[ wdc: RECAL drive %i ]\n", d->drive);
471 d->int_assert = 1;
472 break;
473
474 case WDCC_IDENTIFY:
475 case ATAPI_IDENTIFY_DEVICE:
476 debug("[ wdc: %sIDENTIFY drive %i ]\n", d->cur_command ==
477 ATAPI_IDENTIFY_DEVICE? "ATAPI " : "", d->drive);
478 wdc_initialize_identify_struct(cpu, d);
479 /* The IDENTIFY data is sent out in low/high byte order: */
480 for (i=0; i<sizeof(d->identify_struct); i+=2) {
481 wdc_addtoinbuf(d, d->identify_struct[i+1]);
482 wdc_addtoinbuf(d, d->identify_struct[i+0]);
483 }
484 d->int_assert = 1;
485 break;
486
487 case WDCC_IDLE_IMMED:
488 debug("[ wdc: IDLE_IMMED drive %i ]\n", d->drive);
489 /* TODO: interrupt here? */
490 d->int_assert = 1;
491 break;
492
493 case WDCC_SETMULTI:
494 debug("[ wdc: SETMULTI drive %i ]\n", d->drive);
495 /* TODO: interrupt here? */
496 d->int_assert = 1;
497 break;
498
499 case ATAPI_SOFT_RESET:
500 debug("[ wdc: ATAPI_SOFT_RESET drive %i ]\n", d->drive);
501 /* TODO: interrupt here? */
502 d->int_assert = 1;
503 break;
504
505 case ATAPI_PKT_CMD:
506 debug("[ wdc: ATAPI_PKT_CMD drive %i ]\n", d->drive);
507 /* TODO: interrupt here? */
508 /* d->int_assert = 1; */
509 d->atapi_cmd_in_progress = 1;
510 d->atapi_phase = PHASE_CMDOUT;
511 break;
512
513 case WDCC_DIAGNOSE:
514 debug("[ wdc: WDCC_DIAGNOSE drive %i: TODO ]\n", d->drive);
515 /* TODO: interrupt here? */
516 d->int_assert = 1;
517 d->error = 1; /* No error? */
518 break;
519
520 /* Unsupported commands, without warning: */
521 case WDCC_SEC_SET_PASSWORD:
522 case WDCC_SEC_UNLOCK:
523 case WDCC_SEC_ERASE_PREPARE:
524 case WDCC_SEC_ERASE_UNIT:
525 case WDCC_SEC_FREEZE_LOCK:
526 case WDCC_SEC_DISABLE_PASSWORD:
527 d->error |= WDCE_ABRT;
528 break;
529
530 default:/* TODO */
531 d->error |= WDCE_ABRT;
532 fatal("[ wdc: WARNING! Unimplemented command 0x%02x (drive %i,"
533 " head %i, cyl %i, sector %i, nsecs %i) ]\n",
534 d->cur_command, d->drive, d->head, d->cyl_hi*256+d->cyl_lo,
535 d->sector, d->seccnt);
536 }
537 }
538
539
540 DEVICE_ACCESS(wdc)
541 {
542 struct wdc_data *d = extra;
543 uint64_t idata = 0, odata = 0;
544 int i;
545
546 relative_addr /= d->addr_mult;
547
548 if (!d->io_enabled)
549 goto ret;
550
551 if (writeflag == MEM_WRITE) {
552 if (relative_addr == wd_data)
553 idata = memory_readmax64(cpu, data, len);
554 else {
555 if (len != 1)
556 fatal("[ wdc: WARNING! non-8-bit access! ]\n");
557 idata = data[0];
558 }
559 }
560
561 switch (relative_addr) {
562
563 case wd_data: /* 0: data */
564 if (writeflag == MEM_READ) {
565 odata = wdc_get_inbuf(d);
566
567 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
568 if (len >= 2)
569 odata += (wdc_get_inbuf(d) << 8);
570 if (len == 4) {
571 odata += (wdc_get_inbuf(d) << 16);
572 odata += (wdc_get_inbuf(d) << 24);
573 }
574 } else {
575 if (len >= 2)
576 odata = (odata << 8) + wdc_get_inbuf(d);
577 if (len == 4) {
578 odata = (odata << 8) + wdc_get_inbuf(d);
579 odata = (odata << 8) + wdc_get_inbuf(d);
580 }
581 }
582
583 if (d->data_debug) {
584 char *s = "0x%04"PRIx64" ]\n";
585 if (len == 1)
586 s = "0x%02"PRIx64" ]\n";
587 if (len == 4)
588 s = "0x%08"PRIx64" ]\n";
589 if (len == 8)
590 s = "0x%016"PRIx64" ]\n";
591 debug("[ wdc: read from DATA: ");
592 debug(s, (uint64_t) odata);
593 }
594
595 if (d->atapi_cmd_in_progress) {
596 d->atapi_len -= len;
597 d->atapi_received += len;
598 if (d->atapi_len == 0) {
599 if (d->atapi_received < d->atapi_st->
600 data_in_len) {
601 d->atapi_phase = PHASE_DATAIN;
602 d->atapi_len = d->atapi_st->
603 data_in_len -
604 d->atapi_received;
605 if (d->atapi_len > 32768)
606 d->atapi_len = 0;
607 } else
608 d->atapi_phase =
609 PHASE_COMPLETED;
610 d->int_assert = 1;
611 }
612 } else {
613 #if 0
614 if (d->inbuf_tail != d->inbuf_head)
615 #else
616 if (d->inbuf_tail != d->inbuf_head &&
617 ((d->inbuf_tail - d->inbuf_head) % 512)
618 == 0)
619 #endif
620 d->int_assert = 1;
621 }
622 } else {
623 int inbuf_len;
624 if (d->data_debug) {
625 char *s = "0x%04"PRIx64" ]\n";
626 if (len == 1)
627 s = "0x%02"PRIx64" ]\n";
628 if (len == 4)
629 s = "0x%08"PRIx64" ]\n";
630 if (len == 8)
631 s = "0x%016"PRIx64" ]\n";
632 debug("[ wdc: write to DATA: ");
633 debug(s, (uint64_t) idata);
634 }
635 if (!d->write_in_progress &&
636 !d->atapi_cmd_in_progress) {
637 fatal("[ wdc: write to DATA, but not "
638 "expecting any? (len=%i): 0x%08lx ]\n",
639 (int)len, (long)idata);
640 }
641
642 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
643 switch (len) {
644 case 4: wdc_addtoinbuf(d, idata & 0xff);
645 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
646 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
647 wdc_addtoinbuf(d, (idata >> 24) & 0xff);
648 break;
649 case 2: wdc_addtoinbuf(d, idata & 0xff);
650 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
651 break;
652 case 1: wdc_addtoinbuf(d, idata); break;
653 default:fatal("wdc: unimplemented write "
654 "len %i\n", len);
655 exit(1);
656 }
657 } else {
658 switch (len) {
659 case 4: wdc_addtoinbuf(d, (idata >> 24) & 0xff);
660 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
661 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
662 wdc_addtoinbuf(d, idata & 0xff);
663 break;
664 case 2: wdc_addtoinbuf(d, (idata >> 8) & 0xff);
665 wdc_addtoinbuf(d, idata & 0xff);
666 break;
667 case 1: wdc_addtoinbuf(d, idata); break;
668 default:fatal("wdc: unimplemented write "
669 "len %i\n", len);
670 exit(1);
671 }
672 }
673
674 inbuf_len = d->inbuf_head - d->inbuf_tail;
675 while (inbuf_len < 0)
676 inbuf_len += WDC_INBUF_SIZE;
677
678 if (d->atapi_cmd_in_progress && inbuf_len == 12) {
679 unsigned char *scsi_cmd = malloc(12);
680 int x = 0, res;
681
682 if (d->atapi_st != NULL)
683 scsi_transfer_free(d->atapi_st);
684 d->atapi_st = scsi_transfer_alloc();
685
686 debug("[ wdc: ATAPI command ]\n");
687
688 while (inbuf_len > 0) {
689 scsi_cmd[x++] = wdc_get_inbuf(d);
690 inbuf_len --;
691 }
692
693 d->atapi_st->cmd = scsi_cmd;
694 d->atapi_st->cmd_len = 12;
695
696 if (scsi_cmd[0] == SCSIBLOCKCMD_READ_CAPACITY
697 || scsi_cmd[0] == SCSICMD_READ_10
698 || scsi_cmd[0] == SCSICMD_MODE_SENSE10)
699 d->atapi_st->cmd_len = 10;
700
701 res = diskimage_scsicommand(cpu,
702 d->drive + d->base_drive, DISKIMAGE_IDE,
703 d->atapi_st);
704
705 if (res == 0) {
706 fatal("WDC: ATAPI scsi error?\n");
707 exit(1);
708 }
709
710 d->atapi_len = 0;
711 d->atapi_received = 0;
712
713 if (res == 1) {
714 if (d->atapi_st->data_in != NULL) {
715 int i;
716 d->atapi_phase = PHASE_DATAIN;
717 d->atapi_len = d->atapi_st->
718 data_in_len;
719 for (i=0; i<d->atapi_len; i++)
720 wdc_addtoinbuf(d,
721 d->atapi_st->
722 data_in[i]);
723 if (d->atapi_len > 32768)
724 d->atapi_len = 32768;
725 } else {
726 d->atapi_phase =
727 PHASE_COMPLETED;
728 }
729 } else {
730 fatal("wdc atapi Dataout? TODO\n");
731 d->atapi_phase = PHASE_DATAOUT;
732 exit(1);
733 }
734
735 d->int_assert = 1;
736 }
737
738 if (( d->write_in_progress == WDCC_WRITEMULTI &&
739 inbuf_len % (512 * d->write_count) == 0)
740 ||
741 ( d->write_in_progress == WDCC_WRITE &&
742 inbuf_len % 512 == 0) ) {
743 int count = (d->write_in_progress ==
744 WDCC_WRITEMULTI)? d->write_count : 1;
745 unsigned char *buf = malloc(512 * count);
746 unsigned char *b = buf;
747
748 if (buf == NULL) {
749 fprintf(stderr, "out of memory\n");
750 exit(1);
751 }
752
753 if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) {
754 b = d->inbuf + d->inbuf_tail;
755 d->inbuf_tail = (d->inbuf_tail + 512
756 * count) % WDC_INBUF_SIZE;
757 } else {
758 for (i=0; i<512 * count; i++)
759 buf[i] = wdc_get_inbuf(d);
760 }
761
762 diskimage_access(cpu->machine,
763 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
764 d->write_offset, b, 512 * count);
765
766 d->write_count -= count;
767 d->write_offset += 512 * count;
768
769 d->int_assert = 1;
770
771 if (d->write_count == 0)
772 d->write_in_progress = 0;
773
774 free(buf);
775 }
776 }
777 break;
778
779 case wd_error: /* 1: error (r), precomp (w) */
780 if (writeflag == MEM_READ) {
781 odata = d->error;
782 debug("[ wdc: read from ERROR: 0x%02x ]\n", (int)odata);
783 /* TODO: is the error value cleared on read? */
784 d->error = 0;
785 } else {
786 d->precomp = idata;
787 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
788 }
789 break;
790
791 case wd_seccnt: /* 2: sector count (or "ireason" for ATAPI) */
792 if (writeflag == MEM_READ) {
793 odata = d->seccnt;
794 if (d->atapi_cmd_in_progress) {
795 odata = d->atapi_phase & (WDCI_CMD | WDCI_IN);
796 }
797 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
798 } else {
799 d->seccnt = idata;
800 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
801 }
802 break;
803
804 case wd_sector: /* 3: first sector */
805 if (writeflag == MEM_READ) {
806 odata = d->sector;
807 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
808 } else {
809 d->sector = idata;
810 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
811 }
812 break;
813
814 case wd_cyl_lo: /* 4: cylinder low */
815 if (writeflag == MEM_READ) {
816 odata = d->cyl_lo;
817 if (d->cur_command == COMMAND_RESET &&
818 diskimage_is_a_cdrom(cpu->machine,
819 d->drive + d->base_drive, DISKIMAGE_IDE))
820 odata = 0x14;
821 if (d->atapi_cmd_in_progress) {
822 int x = d->atapi_len;
823 if (x > 32768)
824 x = 32768;
825 odata = x & 255;
826 }
827 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
828 } else {
829 d->cyl_lo = idata;
830 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
831 }
832 break;
833
834 case wd_cyl_hi: /* 5: cylinder high */
835 if (writeflag == MEM_READ) {
836 odata = d->cyl_hi;
837 if (d->cur_command == COMMAND_RESET &&
838 diskimage_is_a_cdrom(cpu->machine,
839 d->drive + d->base_drive, DISKIMAGE_IDE))
840 odata = 0xeb;
841 if (d->atapi_cmd_in_progress) {
842 int x = d->atapi_len;
843 if (x > 32768)
844 x = 32768;
845 odata = (x >> 8) & 255;
846 }
847 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
848 } else {
849 d->cyl_hi = idata;
850 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
851 }
852 break;
853
854 case wd_sdh: /* 6: sectorsize/drive/head */
855 if (writeflag==MEM_READ) {
856 odata = (d->sectorsize << 6) + (d->lba << 5) +
857 (d->drive << 4) + (d->head);
858 debug("[ wdc: read from SDH: 0x%02x (sectorsize %i,"
859 " lba=%i, drive %i, head %i) ]\n", (int)odata,
860 d->sectorsize, d->lba, d->drive, d->head);
861 } else {
862 d->sectorsize = (idata >> 6) & 3;
863 d->lba = (idata >> 5) & 1;
864 d->drive = (idata >> 4) & 1;
865 d->head = idata & 0xf;
866 debug("[ wdc: write to SDH: 0x%02x (sectorsize %i,"
867 " lba=%i, drive %i, head %i) ]\n", (int)idata,
868 d->sectorsize, d->lba, d->drive, d->head);
869 }
870 break;
871
872 case wd_command: /* 7: command or status */
873 if (writeflag==MEM_READ) {
874 odata = status_byte(d, cpu);
875 if (!quiet_mode)
876 debug("[ wdc: read from STATUS: 0x%02x ]\n",
877 (int)odata);
878 INTERRUPT_DEASSERT(d->irq);
879 d->int_assert = 0;
880 } else {
881 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
882 wdc_command(cpu, d, idata);
883 }
884 break;
885
886 default:
887 if (writeflag==MEM_READ)
888 debug("[ wdc: read from 0x%02x ]\n",
889 (int)relative_addr);
890 else
891 debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
892 (int)relative_addr, (int)idata);
893 }
894
895 /* Assert interrupt, if necessary: */
896 dev_wdc_tick(cpu, extra);
897
898 ret:
899 if (writeflag == MEM_READ) {
900 if (relative_addr == wd_data)
901 memory_writemax64(cpu, data, len, odata);
902 else
903 data[0] = odata;
904 }
905
906 return 1;
907 }
908
909
910 DEVINIT(wdc)
911 {
912 struct wdc_data *d;
913 uint64_t alt_status_addr;
914 int i, tick_shift = WDC_TICK_SHIFT;
915
916 d = malloc(sizeof(struct wdc_data));
917 if (d == NULL) {
918 fprintf(stderr, "out of memory\n");
919 exit(1);
920 }
921 memset(d, 0, sizeof(struct wdc_data));
922
923 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
924 d->addr_mult = devinit->addr_mult;
925 d->data_debug = 1;
926 d->io_enabled = 1;
927
928 d->inbuf = zeroed_alloc(WDC_INBUF_SIZE);
929
930 /* base_drive = 0 for the primary controller, 2 for the secondary. */
931 d->base_drive = 0;
932 if ((devinit->addr & 0xfff) == 0x170)
933 d->base_drive = 2;
934
935 alt_status_addr = devinit->addr + 0x206;
936
937 /* Special hacks for individual machines: */
938 switch (devinit->machine->machine_type) {
939 case MACHINE_MACPPC:
940 alt_status_addr = devinit->addr + 0x160;
941 break;
942 case MACHINE_HPCMIPS:
943 /* TODO: Fix */
944 if (devinit->addr == 0x14000180)
945 alt_status_addr = 0x14000386;
946 break;
947 case MACHINE_IQ80321:
948 alt_status_addr = devinit->addr + 0x402;
949 break;
950 }
951
952 /* Get disk geometries: */
953 for (i=0; i<2; i++)
954 if (diskimage_exist(devinit->machine, d->base_drive +i,
955 DISKIMAGE_IDE))
956 diskimage_getchs(devinit->machine, d->base_drive + i,
957 DISKIMAGE_IDE, &d->cyls[i], &d->heads[i],
958 &d->sectors_per_track[i]);
959
960 memory_device_register(devinit->machine->memory, "wdc_altstatus",
961 alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL);
962 memory_device_register(devinit->machine->memory, devinit->name,
963 devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access,
964 d, DM_DEFAULT, NULL);
965
966 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
967 d, tick_shift, 0.0);
968
969 devinit->return_ptr = d;
970
971 return 1;
972 }
973

  ViewVC Help
Powered by ViewVC 1.1.26