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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (show annotations)
Mon Oct 8 16:19:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 18873 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1004 2005/10/27 14:01:10 debug Exp $
20051011        Passing -A as the default boot arg for CATS (works fine with
                OpenBSD/cats).
20051012	Fixing the VGA cursor offset bug, and speeding up framebuffer
		redraws if character cells contain the same thing as during
		the last redraw.
20051013	Adding a slow strd ARM instruction hack.
20051017	Minor updates: Adding a dummy i80321 Verde controller (for
		XScale emulation), fixing the disassembly of the ARM "ldrd"
		instruction, adding "support" for less-than-4KB pages for ARM
		(by not adding them to translation tables).
20051020	Continuing on some HPCarm stuff. A NetBSD/hpcarm kernel prints
		some boot messages on an emulated Jornada 720.
		Making dev_ram work better with dyntrans (speeds up some things
		quite a bit).
20051021	Automatically generating some of the most common ARM load/store
		multiple instructions.
20051022	Better statistics gathering for the ARM load/store multiple.
		Various other dyntrans and device updates.
20051023	Various minor updates.
20051024	Continuing; minor device and dyntrans fine-tuning. Adding the
		first "reasonable" instruction combination hacks for ARM (the
		cores of NetBSD/cats' memset and memcpy).
20051025	Fixing a dyntrans-related bug in dev_vga. Also changing the
		dyntrans low/high access notification to only be updated on
		writes, not reads. Hopefully it will be enough. (dev_vga in
		charcell mode now seems to work correctly with both reads and
		writes.)
		Experimenting with gathering dyntrans statistics (which parts
		of emulated RAM that are actually executed), and adding
		instruction combination hacks for cache cleaning and a part of
		NetBSD's scanc() function.
20051026	Adding a bitmap for ARM emulation which indicates if a page is
		(specifically) user accessible; loads and stores with the t-
		flag set can now use the translation arrays, which results in
		a measurable speedup.
20051027	Dyntrans updates; adding an extra bitmap array for 32-bit
		emulation modes, speeding up the check whether a physical page
		has any code translations or not (O(n) -> O(1)). Doing a
		similar reduction of O(n) to O(1) by avoiding the scan through
		the translation entries on a translation update (32-bit mode
		only).
		Various other minor hacks.
20051029	Quick release, without any testing at all.

==============  RELEASE 0.3.6.2  ==============


1 /*
2 * Copyright (C) 2004-2005 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.44 2005/10/26 14:37:05 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 #define INT_DELAY 1
60
61 extern int quiet_mode;
62
63 /* #define debug fatal */
64
65 struct wdc_data {
66 int irq_nr;
67 int base_drive;
68 int data_debug;
69
70 /* Cached values: */
71 int cyls[2];
72 int heads[2];
73 int sectors_per_track[2];
74
75 unsigned char identify_struct[512];
76
77 unsigned char inbuf[WDC_INBUF_SIZE];
78 int inbuf_head;
79 int inbuf_tail;
80
81 int delayed_interrupt;
82 int write_in_progress;
83 int write_count;
84 int64_t write_offset;
85
86 int error;
87 int precomp;
88 int seccnt;
89 int sector;
90 int cyl_lo;
91 int cyl_hi;
92 int sectorsize;
93 int lba;
94 int drive;
95 int head;
96 int cur_command;
97 };
98
99
100 /*
101 * dev_wdc_tick():
102 */
103 void dev_wdc_tick(struct cpu *cpu, void *extra)
104 {
105 struct wdc_data *d = extra;
106
107 if (d->delayed_interrupt) {
108 d->delayed_interrupt --;
109
110 if (d->delayed_interrupt == 0)
111 cpu_interrupt(cpu, d->irq_nr);
112 }
113 }
114
115
116 /*
117 * wdc_addtoinbuf():
118 *
119 * Write to the inbuf at its head, read at its tail.
120 */
121 static void wdc_addtoinbuf(struct wdc_data *d, int c)
122 {
123 d->inbuf[d->inbuf_head] = c;
124
125 d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE;
126 if (d->inbuf_head == d->inbuf_tail)
127 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
128 " Increase WDC_MAX_SECTORS. ]\n");
129 }
130
131
132 /*
133 * wdc_get_inbuf():
134 *
135 * Read from the tail of inbuf.
136 */
137 static uint64_t wdc_get_inbuf(struct wdc_data *d)
138 {
139 int c = d->inbuf[d->inbuf_tail];
140
141 if (d->inbuf_head == d->inbuf_tail) {
142 fatal("[ wdc: WARNING! someone is reading too much from the "
143 "wdc inbuf! ]\n");
144 return -1;
145 }
146
147 d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
148 return c;
149 }
150
151
152 /*
153 * wdc_initialize_identify_struct(d):
154 */
155 static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
156 {
157 uint64_t total_size;
158 char namebuf[40];
159
160 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
161 DISKIMAGE_IDE);
162
163 memset(d->identify_struct, 0, sizeof(d->identify_struct));
164
165 /* Offsets are in 16-bit WORDS! High byte, then low. */
166
167 /* 0: general flags */
168 d->identify_struct[2 * 0 + 0] = 0;
169 d->identify_struct[2 * 0 + 1] = 1 << 6;
170
171 /* 1: nr of cylinders */
172 d->identify_struct[2 * 1 + 0] = (d->cyls[d->drive] >> 8);
173 d->identify_struct[2 * 1 + 1] = d->cyls[d->drive] & 255;
174
175 /* 3: nr of heads */
176 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
177 d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
178
179 /* 6: sectors per track */
180 d->identify_struct[2 * 6 + 0] = d->sectors_per_track[d->drive] >> 8;
181 d->identify_struct[2 * 6 + 1] = d->sectors_per_track[d->drive];
182
183 /* 10-19: Serial number */
184 memcpy(&d->identify_struct[2 * 10], "S/N 1234-5678 ", 20);
185
186 /* 23-26: Firmware version */
187 memcpy(&d->identify_struct[2 * 23], "VER 1.0 ", 8);
188
189 /* 27-46: Model number */
190 if (diskimage_getname(cpu->machine, d->drive + d->base_drive,
191 DISKIMAGE_IDE, namebuf, sizeof(namebuf))) {
192 int i;
193 for (i=0; i<sizeof(namebuf); i++)
194 if (namebuf[i] == 0) {
195 for (; i<sizeof(namebuf); i++)
196 namebuf[i] = ' ';
197 break;
198 }
199 memcpy(&d->identify_struct[2 * 27], namebuf, 40);
200 } else
201 memcpy(&d->identify_struct[2 * 27],
202 "Fake GXemul IDE disk ", 40);
203
204 /* 47: max sectors per multitransfer */
205 d->identify_struct[2 * 47 + 0] = 0x80;
206 d->identify_struct[2 * 47 + 1] = 1; /* 1 or 16? */
207
208 /* 57-58: current capacity in sectors */
209 d->identify_struct[2 * 57 + 0] = ((total_size / 512) >> 24) % 255;
210 d->identify_struct[2 * 57 + 1] = ((total_size / 512) >> 16) % 255;
211 d->identify_struct[2 * 58 + 0] = ((total_size / 512) >> 8) % 255;
212 d->identify_struct[2 * 58 + 1] = (total_size / 512) & 255;
213
214 /* 60-61: total nr of addresable sectors */
215 d->identify_struct[2 * 60 + 0] = ((total_size / 512) >> 24) % 255;
216 d->identify_struct[2 * 60 + 1] = ((total_size / 512) >> 16) % 255;
217 d->identify_struct[2 * 61 + 0] = ((total_size / 512) >> 8) % 255;
218 d->identify_struct[2 * 61 + 1] = (total_size / 512) & 255;
219
220 }
221
222
223 /*
224 * wdc__read():
225 */
226 void wdc__read(struct cpu *cpu, struct wdc_data *d)
227 {
228 const int max_sectors_per_chunk = 64;
229 unsigned char buf[512 * max_sectors_per_chunk];
230 int i, cyl = d->cyl_hi * 256+ d->cyl_lo;
231 int count = d->seccnt? d->seccnt : 256;
232 uint64_t offset = 512 * (d->sector - 1
233 + d->head * d->sectors_per_track[d->drive] +
234 d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
235
236 #if 0
237 /* LBA: */
238 if (d->lba)
239 offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8)
240 + d->sector);
241 printf("WDC read from offset %lli\n", (long long)offset);
242 #endif
243
244 while (count > 0) {
245 int to_read = count > max_sectors_per_chunk?
246 max_sectors_per_chunk : count;
247
248 /* TODO: result code from the read? */
249
250 if (d->inbuf_head + 512 * to_read <= WDC_INBUF_SIZE) {
251 diskimage_access(cpu->machine, d->drive + d->base_drive,
252 DISKIMAGE_IDE, 0, offset,
253 d->inbuf + d->inbuf_head, 512 * to_read);
254 d->inbuf_head += 512 * to_read;
255 if (d->inbuf_head == WDC_INBUF_SIZE)
256 d->inbuf_head = 0;
257 } else {
258 diskimage_access(cpu->machine, d->drive + d->base_drive,
259 DISKIMAGE_IDE, 0, offset, buf, 512 * to_read);
260 for (i=0; i<512 * to_read; i++)
261 wdc_addtoinbuf(d, buf[i]);
262 }
263
264 offset += 512 * to_read;
265 count -= to_read;
266 }
267
268 d->delayed_interrupt = INT_DELAY;
269 }
270
271
272 /*
273 * wdc__write():
274 */
275 void wdc__write(struct cpu *cpu, struct wdc_data *d)
276 {
277 int cyl = d->cyl_hi * 256+ d->cyl_lo;
278 int count = d->seccnt? d->seccnt : 256;
279 uint64_t offset = 512 * (d->sector - 1
280 + d->head * d->sectors_per_track[d->drive] +
281 d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
282 #if 0
283 /* LBA: */
284 if (d->lba)
285 offset = 512 * (((d->head & 0xf) << 24) +
286 (cyl << 8) + d->sector);
287 printf("WDC write to offset %lli\n", (long long)offset);
288 #endif
289
290 d->write_in_progress = 1;
291 d->write_count = count;
292 d->write_offset = offset;
293
294 /* TODO: result code? */
295 }
296
297
298 /*
299 * status_byte():
300 *
301 * Return a reasonable status byte corresponding to the controller's current
302 * state.
303 */
304 static int status_byte(struct wdc_data *d, struct cpu *cpu)
305 {
306 int odata = 0;
307
308 /*
309 * Modern versions of OpenBSD wants WDCS_DSC. (Thanks to Alexander
310 * Yurchenko for noticing this.)
311 */
312 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
313 DISKIMAGE_IDE))
314 odata |= WDCS_DRDY | WDCS_DSC;
315 if (d->inbuf_head != d->inbuf_tail)
316 odata |= WDCS_DRQ;
317 if (d->write_in_progress)
318 odata |= WDCS_DRQ;
319 if (d->error)
320 odata |= WDCS_ERR;
321
322 return odata;
323 }
324
325
326 /*
327 * dev_wdc_altstatus_access():
328 */
329 int dev_wdc_altstatus_access(struct cpu *cpu, struct memory *mem,
330 uint64_t relative_addr, unsigned char *data, size_t len,
331 int writeflag, void *extra)
332 {
333 struct wdc_data *d = extra;
334 uint64_t idata = 0, odata = 0;
335
336 idata = data[0];
337
338 /* Same as the normal status byte? */
339 odata = status_byte(d, cpu);
340
341 if (writeflag==MEM_READ)
342 debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
343 (int)odata);
344 else
345 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
346 (int)idata);
347
348 if (writeflag == MEM_READ)
349 data[0] = odata;
350
351 return 1;
352 }
353
354
355 /*
356 * dev_wdc_access():
357 */
358 int dev_wdc_access(struct cpu *cpu, struct memory *mem,
359 uint64_t relative_addr, unsigned char *data, size_t len,
360 int writeflag, void *extra)
361 {
362 struct wdc_data *d = extra;
363 uint64_t idata = 0, odata = 0;
364 int i;
365
366 if (writeflag == MEM_WRITE) {
367 if (relative_addr == wd_data)
368 idata = memory_readmax64(cpu, data, len);
369 else
370 idata = data[0];
371 }
372
373 switch (relative_addr) {
374
375 case wd_data: /* 0: data */
376 if (writeflag == MEM_READ) {
377 odata = 0;
378
379 /* TODO: This is hardcoded for little-endian? */
380
381 odata += wdc_get_inbuf(d);
382 if (len >= 2)
383 odata += (wdc_get_inbuf(d) << 8);
384 if (len == 4) {
385 odata += (wdc_get_inbuf(d) << 16);
386 odata += (wdc_get_inbuf(d) << 24);
387 }
388
389 if (d->data_debug)
390 debug("[ wdc: read from DATA: 0x%04x ]\n",
391 (int)odata);
392 #if 0
393 if (d->inbuf_tail != d->inbuf_head)
394 #else
395 if (d->inbuf_tail != d->inbuf_head &&
396 ((d->inbuf_tail - d->inbuf_head) % 512) == 0)
397 #endif
398 d->delayed_interrupt = INT_DELAY;
399 } else {
400 int inbuf_len;
401 if (d->data_debug)
402 debug("[ wdc: write to DATA (len=%i): "
403 "0x%08lx ]\n", (int)len, (long)idata);
404 if (!d->write_in_progress) {
405 fatal("[ wdc: write to DATA, but not "
406 "expecting any? (len=%i): 0x%08lx ]\n",
407 (int)len, (long)idata);
408 }
409
410 switch (len) {
411 case 4: wdc_addtoinbuf(d, idata & 0xff);
412 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
413 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
414 wdc_addtoinbuf(d, (idata >> 24) & 0xff);
415 break;
416 case 2: wdc_addtoinbuf(d, idata & 0xff);
417 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
418 break;
419 case 1: wdc_addtoinbuf(d, idata); break;
420 default:fatal("wdc: unimplemented write len %i\n", len);
421 exit(1);
422 }
423
424 inbuf_len = d->inbuf_head - d->inbuf_tail;
425 while (inbuf_len < 0)
426 inbuf_len += WDC_INBUF_SIZE;
427
428 #if 0
429 if ((inbuf_len % (512 * d->write_count)) == 0) {
430 #else
431 if ((inbuf_len % 512) == 0) {
432 #endif
433 int count = 1; /* d->write_count; */
434 unsigned char buf[512 * count];
435 unsigned char *b = buf;
436
437 if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) {
438 b = d->inbuf + d->inbuf_tail;
439 d->inbuf_tail = (d->inbuf_tail + 512
440 * count) % WDC_INBUF_SIZE;
441 } else {
442 for (i=0; i<512 * count; i++)
443 buf[i] = wdc_get_inbuf(d);
444 }
445
446 diskimage_access(cpu->machine,
447 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
448 d->write_offset, b, 512 * count);
449
450 d->write_count --;
451 d->write_offset += 512;
452
453 d->delayed_interrupt = INT_DELAY;
454
455 if (d->write_count == 0)
456 d->write_in_progress = 0;
457 }
458 }
459 break;
460
461 case wd_error: /* 1: error (r), precomp (w) */
462 if (writeflag==MEM_READ) {
463 odata = d->error;
464 debug("[ wdc: read from ERROR: 0x%02x ]\n",
465 (int)odata);
466 /* TODO: is the error value cleared on read? */
467 d->error = 0;
468 } else {
469 d->precomp = idata;
470 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
471 }
472 break;
473
474 case wd_seccnt: /* 2: sector count */
475 if (writeflag==MEM_READ) {
476 odata = d->seccnt;
477 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
478 } else {
479 d->seccnt = idata;
480 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
481 }
482 break;
483
484 case wd_sector: /* 3: first sector */
485 if (writeflag==MEM_READ) {
486 odata = d->sector;
487 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
488 } else {
489 d->sector = idata;
490 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
491 }
492 break;
493
494 case wd_cyl_lo: /* 4: cylinder low */
495 if (writeflag==MEM_READ) {
496 odata = d->cyl_lo;
497 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
498 } else {
499 d->cyl_lo = idata;
500 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
501 }
502 break;
503
504 case wd_cyl_hi: /* 5: cylinder low */
505 if (writeflag==MEM_READ) {
506 odata = d->cyl_hi;
507 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
508 } else {
509 d->cyl_hi = idata;
510 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
511 }
512 break;
513
514 case wd_sdh: /* 6: sectorsize/drive/head */
515 if (writeflag==MEM_READ) {
516 odata = (d->sectorsize << 6) + (d->lba << 5) +
517 (d->drive << 4) + (d->head);
518 debug("[ wdc: read from SDH: 0x%02x (sectorsize %i,"
519 " lba=%i, drive %i, head %i) ]\n", (int)odata,
520 d->sectorsize, d->lba, d->drive, d->head);
521 } else {
522 d->sectorsize = (idata >> 6) & 3;
523 d->lba = (idata >> 5) & 1;
524 d->drive = (idata >> 4) & 1;
525 d->head = idata & 0xf;
526 debug("[ wdc: write to SDH: 0x%02x (sectorsize %i,"
527 " lba=%i, drive %i, head %i) ]\n", (int)idata,
528 d->sectorsize, d->lba, d->drive, d->head);
529 }
530 break;
531
532 case wd_command: /* 7: command or status */
533 if (writeflag==MEM_READ) {
534 odata = status_byte(d, cpu);
535 if (!quiet_mode)
536 debug("[ wdc: read from STATUS: 0x%02x ]\n",
537 (int)odata);
538 cpu_interrupt_ack(cpu, d->irq_nr);
539 d->delayed_interrupt = 0;
540 } else {
541 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
542 d->cur_command = idata;
543
544 /* TODO: Is this correct behaviour? */
545 if (!diskimage_exist(cpu->machine,
546 d->drive + d->base_drive, DISKIMAGE_IDE)) {
547 d->error |= WDCE_ABRT;
548 d->delayed_interrupt = INT_DELAY;
549 break;
550 }
551
552 /* Handle the command: */
553 switch (d->cur_command) {
554
555 case WDCC_READ:
556 if (!quiet_mode)
557 debug("[ wdc: READ from drive %i, head"
558 " %i, cyl %i, sector %i, nsecs %i "
559 "]\n", d->drive, d->head,
560 d->cyl_hi*256+d->cyl_lo, d->sector,
561 d->seccnt);
562 wdc__read(cpu, d);
563 break;
564
565 case WDCC_WRITE:
566 if (!quiet_mode)
567 debug("[ wdc: WRITE to drive %i, head"
568 " %i, cyl %i, sector %i, nsecs %i"
569 " ]\n", d->drive, d->head,
570 d->cyl_hi*256+d->cyl_lo, d->sector,
571 d->seccnt);
572 wdc__write(cpu, d);
573 break;
574
575 case WDCC_IDP: /* Initialize drive parameters */
576 debug("[ wdc: IDP drive %i (TODO) ]\n",
577 d->drive);
578 /* TODO */
579 d->delayed_interrupt = INT_DELAY;
580 break;
581
582 case SET_FEATURES:
583 fatal("[ wdc: SET_FEATURES drive %i (TODO), "
584 "feature 0x%02x ]\n", d->drive, d->precomp);
585 /* TODO */
586 switch (d->precomp) {
587 case WDSF_SET_MODE:
588 fatal("[ wdc: WDSF_SET_MODE drive %i, "
589 "pio/dma flags 0x%02x ]\n",
590 d->drive, d->seccnt);
591 break;
592 default:
593 d->error |= WDCE_ABRT;
594 }
595 /* TODO: always interrupt? */
596 d->delayed_interrupt = INT_DELAY;
597 break;
598
599 case WDCC_RECAL:
600 debug("[ wdc: RECAL drive %i ]\n", d->drive);
601 d->delayed_interrupt = INT_DELAY;
602 break;
603
604 case WDCC_IDENTIFY:
605 debug("[ wdc: IDENTIFY drive %i ]\n", d->drive);
606 wdc_initialize_identify_struct(cpu, d);
607 /* The IDENTIFY data block is sent out
608 in low/high byte order: */
609 for (i=0; i<sizeof(d->identify_struct); i+=2) {
610 wdc_addtoinbuf(d, d->identify_struct
611 [i+1]);
612 wdc_addtoinbuf(d, d->identify_struct
613 [i+0]);
614 }
615 d->delayed_interrupt = INT_DELAY;
616 break;
617
618 case WDCC_IDLE_IMMED:
619 debug("[ wdc: IDLE_IMMED drive %i ]\n",
620 d->drive);
621 /* TODO: interrupt here? */
622 d->delayed_interrupt = INT_DELAY;
623 break;
624
625 /* Unsupported commands, without warning: */
626 case ATAPI_IDENTIFY_DEVICE:
627 case WDCC_SEC_SET_PASSWORD:
628 case WDCC_SEC_UNLOCK:
629 case WDCC_SEC_ERASE_PREPARE:
630 case WDCC_SEC_ERASE_UNIT:
631 case WDCC_SEC_FREEZE_LOCK:
632 case WDCC_SEC_DISABLE_PASSWORD:
633 d->error |= WDCE_ABRT;
634 break;
635
636 default:
637 /* TODO */
638 d->error |= WDCE_ABRT;
639
640 fatal("[ wdc: WARNING! Unimplemented command "
641 "0x%02x (drive %i, head %i, cyl %i, sector"
642 " %i, nsecs %i) ]\n", d->cur_command,
643 d->drive, d->head, d->cyl_hi*256+d->cyl_lo,
644 d->sector, d->seccnt);
645 }
646 }
647 break;
648
649 default:
650 if (writeflag==MEM_READ)
651 debug("[ wdc: read from 0x%02x ]\n",
652 (int)relative_addr);
653 else
654 debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
655 (int)relative_addr, (int)idata);
656 }
657
658 if (cpu->machine->machine_type != MACHINE_HPCMIPS)
659 dev_wdc_tick(cpu, extra);
660
661 if (writeflag == MEM_READ) {
662 if (relative_addr == wd_data)
663 memory_writemax64(cpu, data, len, odata);
664 else
665 data[0] = odata;
666 }
667
668 return 1;
669 }
670
671
672 /*
673 * devinit_wdc():
674 */
675 int devinit_wdc(struct devinit *devinit)
676 {
677 struct wdc_data *d;
678 uint64_t alt_status_addr;
679 int i, tick_shift = WDC_TICK_SHIFT;
680
681 d = malloc(sizeof(struct wdc_data));
682 if (d == NULL) {
683 fprintf(stderr, "out of memory\n");
684 exit(1);
685 }
686 memset(d, 0, sizeof(struct wdc_data));
687 d->irq_nr = devinit->irq_nr;
688
689 /* base_drive = 0 for the primary controller, 2 for the secondary. */
690 d->base_drive = 0;
691 if ((devinit->addr & 0xfff) == 0x170)
692 d->base_drive = 2;
693
694 alt_status_addr = devinit->addr + 0x206;
695
696 /* Special hack for pcic/hpcmips: TODO: Fix */
697 if (devinit->addr == 0x14000180)
698 alt_status_addr = 0x14000386;
699
700 /* Get disk geometries: */
701 for (i=0; i<2; i++)
702 if (diskimage_exist(devinit->machine, d->base_drive +i,
703 DISKIMAGE_IDE))
704 diskimage_getchs(devinit->machine, d->base_drive + i,
705 DISKIMAGE_IDE, &d->cyls[i], &d->heads[i],
706 &d->sectors_per_track[i]);
707
708 memory_device_register(devinit->machine->memory, "wdc_altstatus",
709 alt_status_addr, 2, dev_wdc_altstatus_access, d, MEM_DEFAULT, NULL);
710 memory_device_register(devinit->machine->memory, devinit->name,
711 devinit->addr, DEV_WDC_LENGTH, dev_wdc_access, d, MEM_DEFAULT,
712 NULL);
713
714 if (devinit->machine->machine_type != MACHINE_HPCMIPS)
715 tick_shift += 2;
716
717 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
718 d, tick_shift);
719
720 return 1;
721 }
722

  ViewVC Help
Powered by ViewVC 1.1.26