/[gxemul]/upstream/0.3.8/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.3.8/src/devices/dev_wdc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 23 - (show annotations)
Mon Oct 8 16:19:43 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 25781 byte(s)
0.3.8
1 /*
2 * Copyright (C) 2004-2006 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.63 2006/02/18 13:15:21 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 /*
52 * INT_DELAY: This is an old hack which only exists because (some versions of)
53 * NetBSD for hpcmips have interrupt problems. These problems are probably not
54 * specific to GXemul, but are also triggered on real hardware.
55 *
56 * See the following URL for more info:
57 * http://mail-index.netbsd.org/port-hpcmips/2004/12/30/0003.html
58 *
59 * NetBSD/malta also bugs out if wdc interrupts come too quickly. Hm.
60 */
61 #define INT_DELAY 1
62
63 extern int quiet_mode;
64
65 /* #define debug fatal */
66
67 struct wdc_data {
68 int irq_nr;
69 int addr_mult;
70 int base_drive;
71 int data_debug;
72
73 /* Cached values: */
74 int cyls[2];
75 int heads[2];
76 int sectors_per_track[2];
77
78 unsigned char *inbuf;
79 int inbuf_head;
80 int inbuf_tail;
81
82 int delayed_interrupt;
83 int int_asserted;
84
85 int write_in_progress;
86 int write_count;
87 int64_t write_offset;
88
89 int error;
90 int precomp;
91 int seccnt;
92 int sector;
93 int cyl_lo;
94 int cyl_hi;
95 int sectorsize;
96 int lba;
97 int drive;
98 int head;
99 int cur_command;
100
101 int atapi_cmd_in_progress;
102 int atapi_phase;
103 struct scsi_transfer *atapi_st;
104 int atapi_len;
105 size_t atapi_received;
106
107 unsigned char identify_struct[512];
108 };
109
110
111 #define COMMAND_RESET 0x100
112
113
114 /*
115 * dev_wdc_tick():
116 */
117 void dev_wdc_tick(struct cpu *cpu, void *extra)
118 {
119 struct wdc_data *d = extra;
120 int old_di = d->delayed_interrupt;
121
122 if (d->delayed_interrupt)
123 d->delayed_interrupt --;
124
125 if (old_di == 1 || d->int_asserted) {
126 cpu_interrupt(cpu, d->irq_nr);
127 d->int_asserted = 1;
128 }
129 }
130
131
132 /*
133 * wdc_addtoinbuf():
134 *
135 * Write to the inbuf at its head, read at its tail.
136 */
137 static void wdc_addtoinbuf(struct wdc_data *d, int c)
138 {
139 d->inbuf[d->inbuf_head] = c;
140
141 d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE;
142 if (d->inbuf_head == d->inbuf_tail)
143 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
144 " Increase WDC_MAX_SECTORS. ]\n");
145 }
146
147
148 /*
149 * wdc_get_inbuf():
150 *
151 * Read from the tail of inbuf.
152 */
153 static uint64_t wdc_get_inbuf(struct wdc_data *d)
154 {
155 int c = d->inbuf[d->inbuf_tail];
156
157 if (d->inbuf_head == d->inbuf_tail) {
158 fatal("[ wdc: WARNING! someone is reading too much from the "
159 "wdc inbuf! ]\n");
160 return -1;
161 }
162
163 d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
164 return c;
165 }
166
167
168 /*
169 * wdc_initialize_identify_struct():
170 */
171 static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
172 {
173 uint64_t total_size;
174 int flags, cdrom = 0;
175 char namebuf[40];
176
177 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
178 DISKIMAGE_IDE);
179 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
180 DISKIMAGE_IDE))
181 cdrom = 1;
182
183 memset(d->identify_struct, 0, sizeof(d->identify_struct));
184
185 /* Offsets are in 16-bit WORDS! High byte, then low. */
186
187 /* 0: general flags */
188 flags = 1 << 6; /* Fixed */
189 if (cdrom)
190 flags = 0x8580; /* ATAPI, CDROM, removable */
191 d->identify_struct[2 * 0 + 0] = flags >> 8;
192 d->identify_struct[2 * 0 + 1] = flags;
193
194 /* 1: nr of cylinders */
195 d->identify_struct[2 * 1 + 0] = d->cyls[d->drive] >> 8;
196 d->identify_struct[2 * 1 + 1] = d->cyls[d->drive];
197
198 /* 3: nr of heads */
199 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
200 d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
201
202 /* 6: sectors per track */
203 d->identify_struct[2 * 6 + 0] = d->sectors_per_track[d->drive] >> 8;
204 d->identify_struct[2 * 6 + 1] = d->sectors_per_track[d->drive];
205
206 /* 10-19: Serial number */
207 memcpy(&d->identify_struct[2 * 10], "#0 ", 20);
208
209 /* 23-26: Firmware version */
210 memcpy(&d->identify_struct[2 * 23], "1.0 ", 8);
211
212 /* 27-46: Model number */
213 if (diskimage_getname(cpu->machine, d->drive + d->base_drive,
214 DISKIMAGE_IDE, namebuf, sizeof(namebuf))) {
215 size_t i;
216 for (i=0; i<sizeof(namebuf); i++)
217 if (namebuf[i] == 0) {
218 for (; i<sizeof(namebuf); i++)
219 namebuf[i] = ' ';
220 break;
221 }
222 memcpy(&d->identify_struct[2 * 27], namebuf, 40);
223 } else
224 memcpy(&d->identify_struct[2 * 27],
225 "Fake GXemul IDE disk ", 40);
226
227 /* 47: max sectors per multitransfer */
228 d->identify_struct[2 * 47 + 0] = 0x80;
229 d->identify_struct[2 * 47 + 1] = 128;
230
231 /* 49: capabilities: */
232 /* (0x200 = LBA, 0x100 = DMA support.) */
233 d->identify_struct[2 * 49 + 0] = 0;
234 d->identify_struct[2 * 49 + 1] = 0;
235
236 /* 51: PIO timing mode. */
237 d->identify_struct[2 * 51 + 0] = 0x00; /* ? */
238 d->identify_struct[2 * 51 + 1] = 0x00;
239
240 /* 53: 0x02 = fields 64-70 valid, 0x01 = fields 54-58 valid */
241 d->identify_struct[2 * 53 + 0] = 0x00;
242 d->identify_struct[2 * 53 + 1] = 0x02;
243
244 /* 57-58: current capacity in sectors */
245 d->identify_struct[2 * 57 + 0] = ((total_size / 512) >> 24) % 255;
246 d->identify_struct[2 * 57 + 1] = ((total_size / 512) >> 16) % 255;
247 d->identify_struct[2 * 58 + 0] = ((total_size / 512) >> 8) % 255;
248 d->identify_struct[2 * 58 + 1] = (total_size / 512) & 255;
249
250 /* 60-61: total nr of addressable sectors */
251 d->identify_struct[2 * 60 + 0] = ((total_size / 512) >> 24) % 255;
252 d->identify_struct[2 * 60 + 1] = ((total_size / 512) >> 16) % 255;
253 d->identify_struct[2 * 61 + 0] = ((total_size / 512) >> 8) % 255;
254 d->identify_struct[2 * 61 + 1] = (total_size / 512) & 255;
255
256 /* 64: Advanced PIO mode support. 0x02 = mode4, 0x01 = mode3 */
257 d->identify_struct[2 * 64 + 0] = 0x00;
258 d->identify_struct[2 * 64 + 1] = 0x03;
259
260 /* 67, 68: PIO timing */
261 d->identify_struct[2 * 67 + 0] = 0;
262 d->identify_struct[2 * 67 + 1] = 120;
263 d->identify_struct[2 * 68 + 0] = 0;
264 d->identify_struct[2 * 68 + 1] = 120;
265 }
266
267
268 /*
269 * wdc__read():
270 */
271 void wdc__read(struct cpu *cpu, struct wdc_data *d)
272 {
273 #define MAX_SECTORS_PER_CHUNK 64
274 const int max_sectors_per_chunk = MAX_SECTORS_PER_CHUNK;
275 unsigned char buf[512 * MAX_SECTORS_PER_CHUNK];
276 int i, cyl = d->cyl_hi * 256+ d->cyl_lo;
277 int count = d->seccnt? d->seccnt : 256;
278 uint64_t offset = 512 * (d->sector - 1
279 + d->head * d->sectors_per_track[d->drive] +
280 d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
281
282 #if 0
283 /* LBA: */
284 if (d->lba)
285 offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8)
286 + d->sector);
287 printf("WDC read from offset %lli\n", (long long)offset);
288 #endif
289
290 while (count > 0) {
291 int to_read = count > max_sectors_per_chunk?
292 max_sectors_per_chunk : count;
293
294 /* TODO: result code from the read? */
295
296 if (d->inbuf_head + 512 * to_read <= WDC_INBUF_SIZE) {
297 diskimage_access(cpu->machine, d->drive + d->base_drive,
298 DISKIMAGE_IDE, 0, offset,
299 d->inbuf + d->inbuf_head, 512 * to_read);
300 d->inbuf_head += 512 * to_read;
301 if (d->inbuf_head == WDC_INBUF_SIZE)
302 d->inbuf_head = 0;
303 } else {
304 diskimage_access(cpu->machine, d->drive + d->base_drive,
305 DISKIMAGE_IDE, 0, offset, buf, 512 * to_read);
306 for (i=0; i<512 * to_read; i++)
307 wdc_addtoinbuf(d, buf[i]);
308 }
309
310 offset += 512 * to_read;
311 count -= to_read;
312 }
313
314 d->delayed_interrupt = INT_DELAY;
315 }
316
317
318 /*
319 * wdc__write():
320 */
321 void wdc__write(struct cpu *cpu, struct wdc_data *d)
322 {
323 int cyl = d->cyl_hi * 256+ d->cyl_lo;
324 int count = d->seccnt? d->seccnt : 256;
325 uint64_t offset = 512 * (d->sector - 1
326 + d->head * d->sectors_per_track[d->drive] +
327 d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
328 #if 0
329 /* LBA: */
330 if (d->lba)
331 offset = 512 * (((d->head & 0xf) << 24) +
332 (cyl << 8) + d->sector);
333 printf("WDC write to offset %lli\n", (long long)offset);
334 #endif
335
336 d->write_in_progress = d->cur_command;
337 d->write_count = count;
338 d->write_offset = offset;
339
340 /* TODO: result code? */
341 }
342
343
344 /*
345 * status_byte():
346 *
347 * Return a reasonable status byte corresponding to the controller's current
348 * state.
349 */
350 static int status_byte(struct wdc_data *d, struct cpu *cpu)
351 {
352 int odata = 0;
353 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
354 DISKIMAGE_IDE))
355 odata |= WDCS_DRDY | WDCS_DSC;
356 if (d->inbuf_head != d->inbuf_tail)
357 odata |= WDCS_DRQ;
358 if (d->write_in_progress)
359 odata |= WDCS_DRQ;
360 if (d->error)
361 odata |= WDCS_ERR;
362 if (d->atapi_cmd_in_progress && (d->atapi_phase & WDCS_DRQ)) {
363 odata |= WDCS_DRQ;
364 }
365 return odata;
366 }
367
368
369 /*
370 * dev_wdc_altstatus_access():
371 */
372 DEVICE_ACCESS(wdc_altstatus)
373 {
374 struct wdc_data *d = extra;
375 uint64_t idata = 0, odata = 0;
376
377 idata = data[0];
378
379 /* Same as the normal status byte: */
380 odata = status_byte(d, cpu);
381
382 if (writeflag==MEM_READ)
383 debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
384 (int)odata);
385 else {
386 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
387 (int)idata);
388 if (idata & WDCTL_4BIT)
389 d->cur_command = COMMAND_RESET;
390 }
391
392 if (writeflag == MEM_READ)
393 data[0] = odata;
394
395 return 1;
396 }
397
398
399 /*
400 * wdc_command():
401 */
402 void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata)
403 {
404 size_t i;
405
406 d->cur_command = idata;
407 d->atapi_cmd_in_progress = 0;
408 d->error = 0;
409
410 /*
411 * Disk images that do not exist return an ABORT error. This also
412 * happens with CDROM images with the WDCC_IDENTIFY command; CDROM
413 * images must be detected with ATAPI_IDENTIFY_DEVICE instead.
414 *
415 * TODO: Is this correct/good behaviour?
416 */
417 if (!diskimage_exist(cpu->machine, d->drive + d->base_drive,
418 DISKIMAGE_IDE)) {
419 debug("[ wdc: command 0x%02x drive %i, but no disk image ]\n",
420 d->cur_command, d->drive + d->base_drive);
421 d->error |= WDCE_ABRT;
422 d->delayed_interrupt = INT_DELAY;
423 return;
424 }
425 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
426 DISKIMAGE_IDE) && d->cur_command == WDCC_IDENTIFY) {
427 debug("[ wdc: IDENTIFY drive %i, but it is an ATAPI "
428 "drive ]\n", d->drive + d->base_drive);
429 d->error |= WDCE_ABRT;
430 d->delayed_interrupt = INT_DELAY;
431 return;
432 }
433
434 /* Handle the command: */
435 switch (d->cur_command) {
436
437 case WDCC_READ:
438 case WDCC_READMULTI:
439 if (!quiet_mode)
440 debug("[ wdc: READ from drive %i, head %i, cyl %i, "
441 "sector %i, nsecs %i ]\n", d->drive, d->head,
442 d->cyl_hi*256+d->cyl_lo, d->sector, d->seccnt);
443 wdc__read(cpu, d);
444 break;
445
446 case WDCC_WRITE:
447 case WDCC_WRITEMULTI:
448 if (!quiet_mode)
449 debug("[ wdc: WRITE to drive %i, head %i, cyl %i, "
450 "sector %i, nsecs %i ]\n", d->drive, d->head,
451 d->cyl_hi*256+d->cyl_lo, d->sector, d->seccnt);
452 wdc__write(cpu, d);
453 break;
454
455 case WDCC_IDP: /* Initialize drive parameters */
456 debug("[ wdc: IDP drive %i (TODO) ]\n", d->drive);
457 /* TODO */
458 d->delayed_interrupt = INT_DELAY;
459 break;
460
461 case SET_FEATURES:
462 debug("[ wdc: SET_FEATURES drive %i (TODO), feature 0x%02x ]\n",
463 d->drive, d->precomp);
464 /* TODO */
465 switch (d->precomp) {
466 case WDSF_SET_MODE:
467 debug("[ wdc: WDSF_SET_MODE drive %i, pio/dma flags "
468 "0x%02x ]\n", d->drive, d->seccnt);
469 break;
470 default:d->error |= WDCE_ABRT;
471 }
472 /* TODO: always interrupt? */
473 d->delayed_interrupt = INT_DELAY;
474 break;
475
476 case WDCC_RECAL:
477 debug("[ wdc: RECAL drive %i ]\n", d->drive);
478 d->delayed_interrupt = INT_DELAY;
479 break;
480
481 case WDCC_IDENTIFY:
482 case ATAPI_IDENTIFY_DEVICE:
483 debug("[ wdc: %sIDENTIFY drive %i ]\n", d->cur_command ==
484 ATAPI_IDENTIFY_DEVICE? "ATAPI " : "", d->drive);
485 wdc_initialize_identify_struct(cpu, d);
486 /* The IDENTIFY data is sent out in low/high byte order: */
487 for (i=0; i<sizeof(d->identify_struct); i+=2) {
488 wdc_addtoinbuf(d, d->identify_struct[i+1]);
489 wdc_addtoinbuf(d, d->identify_struct[i+0]);
490 }
491 d->delayed_interrupt = INT_DELAY;
492 break;
493
494 case WDCC_IDLE_IMMED:
495 debug("[ wdc: IDLE_IMMED drive %i ]\n", d->drive);
496 /* TODO: interrupt here? */
497 d->delayed_interrupt = INT_DELAY;
498 break;
499
500 case WDCC_SETMULTI:
501 debug("[ wdc: SETMULTI drive %i ]\n", d->drive);
502 /* TODO: interrupt here? */
503 d->delayed_interrupt = INT_DELAY;
504 break;
505
506 case ATAPI_SOFT_RESET:
507 debug("[ wdc: ATAPI_SOFT_RESET drive %i ]\n", d->drive);
508 /* TODO: interrupt here? */
509 d->delayed_interrupt = INT_DELAY;
510 break;
511
512 case ATAPI_PKT_CMD:
513 debug("[ wdc: ATAPI_PKT_CMD drive %i ]\n", d->drive);
514 /* TODO: interrupt here? */
515 /* d->delayed_interrupt = INT_DELAY; */
516 d->atapi_cmd_in_progress = 1;
517 d->atapi_phase = PHASE_CMDOUT;
518 break;
519
520 case WDCC_DIAGNOSE:
521 debug("[ wdc: WDCC_DIAGNOSE drive %i: TODO ]\n", d->drive);
522 /* TODO: interrupt here? */
523 d->delayed_interrupt = INT_DELAY;
524 d->error = 1; /* No error? */
525 break;
526
527 /* Unsupported commands, without warning: */
528 case WDCC_SEC_SET_PASSWORD:
529 case WDCC_SEC_UNLOCK:
530 case WDCC_SEC_ERASE_PREPARE:
531 case WDCC_SEC_ERASE_UNIT:
532 case WDCC_SEC_FREEZE_LOCK:
533 case WDCC_SEC_DISABLE_PASSWORD:
534 d->error |= WDCE_ABRT;
535 break;
536
537 default:/* TODO */
538 d->error |= WDCE_ABRT;
539 fatal("[ wdc: WARNING! Unimplemented command 0x%02x (drive %i,"
540 " head %i, cyl %i, sector %i, nsecs %i) ]\n",
541 d->cur_command, d->drive, d->head, d->cyl_hi*256+d->cyl_lo,
542 d->sector, d->seccnt);
543 }
544 }
545
546
547 /*
548 * dev_wdc_access():
549 */
550 DEVICE_ACCESS(wdc)
551 {
552 struct wdc_data *d = extra;
553 uint64_t idata = 0, odata = 0;
554 int i;
555
556 relative_addr /= d->addr_mult;
557
558 if (writeflag == MEM_WRITE) {
559 if (relative_addr == wd_data)
560 idata = memory_readmax64(cpu, data, len);
561 else {
562 if (len != 1)
563 fatal("[ wdc: WARNING! non-8-bit access! ]\n");
564 idata = data[0];
565 }
566 }
567
568 switch (relative_addr) {
569
570 case wd_data: /* 0: data */
571 if (writeflag == MEM_READ) {
572 odata = wdc_get_inbuf(d);
573
574 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
575 if (len >= 2)
576 odata += (wdc_get_inbuf(d) << 8);
577 if (len == 4) {
578 odata += (wdc_get_inbuf(d) << 16);
579 odata += (wdc_get_inbuf(d) << 24);
580 }
581 } else {
582 if (len >= 2)
583 odata = (odata << 8) + wdc_get_inbuf(d);
584 if (len == 4) {
585 odata = (odata << 8) + wdc_get_inbuf(d);
586 odata = (odata << 8) + wdc_get_inbuf(d);
587 }
588 }
589
590 if (d->data_debug) {
591 char *s = "0x%04llx ]\n";
592 if (len == 1)
593 s = "0x%02llx ]\n";
594 if (len == 4)
595 s = "0x%08llx ]\n";
596 if (len == 8)
597 s = "0x%016llx ]\n";
598 debug("[ wdc: read from DATA: ");
599 debug(s, (long long)odata);
600 }
601
602 if (d->atapi_cmd_in_progress) {
603 d->atapi_len -= len;
604 d->atapi_received += len;
605 if (d->atapi_len == 0) {
606 if (d->atapi_received < d->atapi_st->
607 data_in_len) {
608 d->atapi_phase = PHASE_DATAIN;
609 d->atapi_len = d->atapi_st->
610 data_in_len -
611 d->atapi_received;
612 if (d->atapi_len > 32768)
613 d->atapi_len = 0;
614 } else
615 d->atapi_phase =
616 PHASE_COMPLETED;
617 d->delayed_interrupt = INT_DELAY;
618 }
619 } else {
620 #if 0
621 if (d->inbuf_tail != d->inbuf_head)
622 #else
623 if (d->inbuf_tail != d->inbuf_head &&
624 ((d->inbuf_tail - d->inbuf_head) % 512)
625 == 0)
626 #endif
627 d->delayed_interrupt = INT_DELAY;
628 }
629 } else {
630 int inbuf_len;
631 if (d->data_debug) {
632 char *s = "0x%04llx ]\n";
633 if (len == 1)
634 s = "0x%02llx ]\n";
635 if (len == 4)
636 s = "0x%08llx ]\n";
637 if (len == 8)
638 s = "0x%016llx ]\n";
639 debug("[ wdc: write to DATA: ");
640 debug(s, (long long)idata);
641 }
642 if (!d->write_in_progress &&
643 !d->atapi_cmd_in_progress) {
644 fatal("[ wdc: write to DATA, but not "
645 "expecting any? (len=%i): 0x%08lx ]\n",
646 (int)len, (long)idata);
647 }
648
649 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
650 switch (len) {
651 case 4: wdc_addtoinbuf(d, idata & 0xff);
652 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
653 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
654 wdc_addtoinbuf(d, (idata >> 24) & 0xff);
655 break;
656 case 2: wdc_addtoinbuf(d, idata & 0xff);
657 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
658 break;
659 case 1: wdc_addtoinbuf(d, idata); break;
660 default:fatal("wdc: unimplemented write "
661 "len %i\n", len);
662 exit(1);
663 }
664 } else {
665 switch (len) {
666 case 4: wdc_addtoinbuf(d, (idata >> 24) & 0xff);
667 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
668 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
669 wdc_addtoinbuf(d, idata & 0xff);
670 break;
671 case 2: wdc_addtoinbuf(d, (idata >> 8) & 0xff);
672 wdc_addtoinbuf(d, idata & 0xff);
673 break;
674 case 1: wdc_addtoinbuf(d, idata); break;
675 default:fatal("wdc: unimplemented write "
676 "len %i\n", len);
677 exit(1);
678 }
679 }
680
681 inbuf_len = d->inbuf_head - d->inbuf_tail;
682 while (inbuf_len < 0)
683 inbuf_len += WDC_INBUF_SIZE;
684
685 if (d->atapi_cmd_in_progress && inbuf_len == 12) {
686 unsigned char *scsi_cmd = malloc(12);
687 int x = 0, res;
688
689 if (d->atapi_st != NULL)
690 scsi_transfer_free(d->atapi_st);
691 d->atapi_st = scsi_transfer_alloc();
692
693 debug("[ wdc: ATAPI command ]\n");
694
695 while (inbuf_len > 0) {
696 scsi_cmd[x++] = wdc_get_inbuf(d);
697 inbuf_len --;
698 }
699
700 d->atapi_st->cmd = scsi_cmd;
701 d->atapi_st->cmd_len = 12;
702
703 if (scsi_cmd[0] == SCSIBLOCKCMD_READ_CAPACITY
704 || scsi_cmd[0] == SCSICMD_READ_10
705 || scsi_cmd[0] == SCSICMD_MODE_SENSE10)
706 d->atapi_st->cmd_len = 10;
707
708 res = diskimage_scsicommand(cpu,
709 d->drive + d->base_drive, DISKIMAGE_IDE,
710 d->atapi_st);
711
712 if (res == 0) {
713 fatal("WDC: ATAPI scsi error?\n");
714 exit(1);
715 }
716
717 d->atapi_len = 0;
718 d->atapi_received = 0;
719
720 if (res == 1) {
721 if (d->atapi_st->data_in != NULL) {
722 int i;
723 d->atapi_phase = PHASE_DATAIN;
724 d->atapi_len = d->atapi_st->
725 data_in_len;
726 for (i=0; i<d->atapi_len; i++)
727 wdc_addtoinbuf(d,
728 d->atapi_st->
729 data_in[i]);
730 if (d->atapi_len > 32768)
731 d->atapi_len = 32768;
732 } else {
733 d->atapi_phase =
734 PHASE_COMPLETED;
735 }
736 } else {
737 fatal("wdc atapi Dataout? TODO\n");
738 d->atapi_phase = PHASE_DATAOUT;
739 exit(1);
740 }
741
742 d->delayed_interrupt = INT_DELAY;
743 }
744
745 if (( d->write_in_progress == WDCC_WRITEMULTI &&
746 inbuf_len % (512 * d->write_count) == 0)
747 ||
748 ( d->write_in_progress == WDCC_WRITE &&
749 inbuf_len % 512 == 0) ) {
750 int count = (d->write_in_progress ==
751 WDCC_WRITEMULTI)? d->write_count : 1;
752 unsigned char buf[512 * count];
753 unsigned char *b = buf;
754
755 if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) {
756 b = d->inbuf + d->inbuf_tail;
757 d->inbuf_tail = (d->inbuf_tail + 512
758 * count) % WDC_INBUF_SIZE;
759 } else {
760 for (i=0; i<512 * count; i++)
761 buf[i] = wdc_get_inbuf(d);
762 }
763
764 diskimage_access(cpu->machine,
765 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
766 d->write_offset, b, 512 * count);
767
768 d->write_count -= count;
769 d->write_offset += 512 * count;
770
771 d->delayed_interrupt = INT_DELAY;
772
773 if (d->write_count == 0)
774 d->write_in_progress = 0;
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 cpu_interrupt_ack(cpu, d->irq_nr);
879 d->int_asserted = 0;
880 d->delayed_interrupt = 0;
881 } else {
882 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
883 wdc_command(cpu, d, idata);
884 }
885 break;
886
887 default:
888 if (writeflag==MEM_READ)
889 debug("[ wdc: read from 0x%02x ]\n",
890 (int)relative_addr);
891 else
892 debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
893 (int)relative_addr, (int)idata);
894 }
895
896 if (cpu->machine->machine_type != MACHINE_HPCMIPS &&
897 cpu->machine->machine_type != MACHINE_EVBMIPS &&
898 cpu->machine->machine_type != MACHINE_BEBOX)
899 dev_wdc_tick(cpu, extra);
900
901 if (writeflag == MEM_READ) {
902 if (relative_addr == wd_data)
903 memory_writemax64(cpu, data, len, odata);
904 else
905 data[0] = odata;
906 }
907
908 return 1;
909 }
910
911
912 DEVINIT(wdc)
913 {
914 struct wdc_data *d;
915 uint64_t alt_status_addr;
916 int i, tick_shift = WDC_TICK_SHIFT;
917
918 d = malloc(sizeof(struct wdc_data));
919 if (d == NULL) {
920 fprintf(stderr, "out of memory\n");
921 exit(1);
922 }
923 memset(d, 0, sizeof(struct wdc_data));
924 d->irq_nr = devinit->irq_nr;
925 d->addr_mult = devinit->addr_mult;
926 d->data_debug = 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 if (devinit->machine->machine_type != MACHINE_HPCMIPS &&
967 devinit->machine->machine_type != MACHINE_EVBMIPS)
968 tick_shift += 1;
969
970 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
971 d, tick_shift);
972
973 return 1;
974 }
975

  ViewVC Help
Powered by ViewVC 1.1.26