/[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

Annotation of /trunk/src/devices/dev_wdc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (hide 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 dpavlin 4 /*
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 dpavlin 14 *
28 dpavlin 18 * $Id: dev_wdc.c,v 1.44 2005/10/26 14:37:05 debug Exp $
29 dpavlin 14 *
30     * Standard "wdc" IDE controller.
31 dpavlin 4 */
32    
33     #include <stdio.h>
34     #include <stdlib.h>
35     #include <string.h>
36    
37     #include "cpu.h"
38 dpavlin 14 #include "device.h"
39 dpavlin 4 #include "diskimage.h"
40     #include "machine.h"
41     #include "memory.h"
42     #include "misc.h"
43    
44     #include "wdcreg.h"
45    
46 dpavlin 14 #define DEV_WDC_LENGTH 8
47 dpavlin 4 #define WDC_TICK_SHIFT 14
48 dpavlin 18 #define WDC_MAX_SECTORS 512
49     #define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1))
50 dpavlin 4
51 dpavlin 14 /*
52 dpavlin 18 * 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 dpavlin 14 *
56 dpavlin 18 * See the following URL for more info:
57     * http://mail-index.netbsd.org/port-hpcmips/2004/12/30/0003.html
58 dpavlin 14 */
59 dpavlin 4 #define INT_DELAY 1
60    
61 dpavlin 6 extern int quiet_mode;
62    
63 dpavlin 4 /* #define debug fatal */
64    
65     struct wdc_data {
66     int irq_nr;
67     int base_drive;
68 dpavlin 14 int data_debug;
69 dpavlin 4
70 dpavlin 14 /* Cached values: */
71     int cyls[2];
72     int heads[2];
73     int sectors_per_track[2];
74 dpavlin 4
75     unsigned char identify_struct[512];
76    
77     unsigned char inbuf[WDC_INBUF_SIZE];
78     int inbuf_head;
79     int inbuf_tail;
80    
81 dpavlin 14 int delayed_interrupt;
82 dpavlin 4 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 dpavlin 18 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
128     " Increase WDC_MAX_SECTORS. ]\n");
129 dpavlin 4 }
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 dpavlin 6 fatal("[ wdc: WARNING! someone is reading too much from the "
143     "wdc inbuf! ]\n");
144 dpavlin 4 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 dpavlin 6 uint64_t total_size;
158 dpavlin 14 char namebuf[40];
159 dpavlin 4
160 dpavlin 6 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
161     DISKIMAGE_IDE);
162 dpavlin 4
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 dpavlin 14 d->identify_struct[2 * 1 + 0] = (d->cyls[d->drive] >> 8);
173     d->identify_struct[2 * 1 + 1] = d->cyls[d->drive] & 255;
174 dpavlin 4
175     /* 3: nr of heads */
176 dpavlin 14 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
177     d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
178 dpavlin 4
179     /* 6: sectors per track */
180 dpavlin 14 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 dpavlin 4
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 dpavlin 14 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 dpavlin 4
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 dpavlin 14 * wdc__read():
225     */
226     void wdc__read(struct cpu *cpu, struct wdc_data *d)
227     {
228 dpavlin 18 const int max_sectors_per_chunk = 64;
229     unsigned char buf[512 * max_sectors_per_chunk];
230 dpavlin 14 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 dpavlin 18 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 dpavlin 14 }
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 dpavlin 4 * status_byte():
300 dpavlin 14 *
301     * Return a reasonable status byte corresponding to the controller's current
302     * state.
303 dpavlin 4 */
304     static int status_byte(struct wdc_data *d, struct cpu *cpu)
305     {
306     int odata = 0;
307    
308 dpavlin 14 /*
309     * Modern versions of OpenBSD wants WDCS_DSC. (Thanks to Alexander
310     * Yurchenko for noticing this.)
311     */
312 dpavlin 6 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
313     DISKIMAGE_IDE))
314 dpavlin 14 odata |= WDCS_DRDY | WDCS_DSC;
315 dpavlin 4 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 dpavlin 18 idata = data[0];
337 dpavlin 4
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 dpavlin 18 data[0] = odata;
350 dpavlin 4
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 dpavlin 14 int i;
365 dpavlin 4
366 dpavlin 18 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 dpavlin 4
373     switch (relative_addr) {
374    
375     case wd_data: /* 0: data */
376 dpavlin 18 if (writeflag == MEM_READ) {
377 dpavlin 4 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 dpavlin 14 if (d->data_debug)
390     debug("[ wdc: read from DATA: 0x%04x ]\n",
391     (int)odata);
392 dpavlin 18 #if 0
393 dpavlin 4 if (d->inbuf_tail != d->inbuf_head)
394 dpavlin 18 #else
395     if (d->inbuf_tail != d->inbuf_head &&
396     ((d->inbuf_tail - d->inbuf_head) % 512) == 0)
397     #endif
398 dpavlin 4 d->delayed_interrupt = INT_DELAY;
399     } else {
400     int inbuf_len;
401 dpavlin 14 if (d->data_debug)
402     debug("[ wdc: write to DATA (len=%i): "
403     "0x%08lx ]\n", (int)len, (long)idata);
404 dpavlin 4 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 dpavlin 14 #else
431     if ((inbuf_len % 512) == 0) {
432 dpavlin 4 #endif
433     int count = 1; /* d->write_count; */
434 dpavlin 18 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 dpavlin 4 }
445    
446     diskimage_access(cpu->machine,
447 dpavlin 6 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
448 dpavlin 18 d->write_offset, b, 512 * count);
449 dpavlin 4
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 dpavlin 14 debug("[ wdc: read from ERROR: 0x%02x ]\n",
465     (int)odata);
466 dpavlin 4 /* TODO: is the error value cleared on read? */
467     d->error = 0;
468     } else {
469     d->precomp = idata;
470 dpavlin 14 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
471 dpavlin 4 }
472     break;
473    
474     case wd_seccnt: /* 2: sector count */
475     if (writeflag==MEM_READ) {
476     odata = d->seccnt;
477 dpavlin 14 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
478 dpavlin 4 } else {
479     d->seccnt = idata;
480 dpavlin 14 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
481 dpavlin 4 }
482     break;
483    
484     case wd_sector: /* 3: first sector */
485     if (writeflag==MEM_READ) {
486     odata = d->sector;
487 dpavlin 14 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
488 dpavlin 4 } else {
489     d->sector = idata;
490 dpavlin 14 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
491 dpavlin 4 }
492     break;
493    
494     case wd_cyl_lo: /* 4: cylinder low */
495     if (writeflag==MEM_READ) {
496     odata = d->cyl_lo;
497 dpavlin 14 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
498 dpavlin 4 } else {
499     d->cyl_lo = idata;
500 dpavlin 14 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
501 dpavlin 4 }
502     break;
503    
504     case wd_cyl_hi: /* 5: cylinder low */
505     if (writeflag==MEM_READ) {
506     odata = d->cyl_hi;
507 dpavlin 14 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
508 dpavlin 4 } else {
509     d->cyl_hi = idata;
510 dpavlin 14 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
511 dpavlin 4 }
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 dpavlin 6 if (!quiet_mode)
536     debug("[ wdc: read from STATUS: 0x%02x ]\n",
537 dpavlin 14 (int)odata);
538 dpavlin 4 cpu_interrupt_ack(cpu, d->irq_nr);
539     d->delayed_interrupt = 0;
540     } else {
541 dpavlin 14 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
542 dpavlin 4 d->cur_command = idata;
543    
544     /* TODO: Is this correct behaviour? */
545     if (!diskimage_exist(cpu->machine,
546 dpavlin 6 d->drive + d->base_drive, DISKIMAGE_IDE)) {
547 dpavlin 4 d->error |= WDCE_ABRT;
548     d->delayed_interrupt = INT_DELAY;
549     break;
550     }
551    
552     /* Handle the command: */
553     switch (d->cur_command) {
554 dpavlin 14
555 dpavlin 4 case WDCC_READ:
556 dpavlin 14 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 dpavlin 6
565 dpavlin 4 case WDCC_WRITE:
566 dpavlin 14 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 dpavlin 4 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 dpavlin 14
582 dpavlin 4 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 dpavlin 14
599 dpavlin 4 case WDCC_RECAL:
600     debug("[ wdc: RECAL drive %i ]\n", d->drive);
601     d->delayed_interrupt = INT_DELAY;
602     break;
603 dpavlin 14
604 dpavlin 4 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 dpavlin 14
618 dpavlin 4 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 dpavlin 14
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 dpavlin 4 default:
637     /* TODO */
638     d->error |= WDCE_ABRT;
639    
640 dpavlin 14 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 dpavlin 4 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 dpavlin 18 if (cpu->machine->machine_type != MACHINE_HPCMIPS)
659     dev_wdc_tick(cpu, extra);
660 dpavlin 4
661 dpavlin 18 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 dpavlin 4 return 1;
669     }
670    
671    
672     /*
673 dpavlin 14 * devinit_wdc():
674 dpavlin 4 */
675 dpavlin 14 int devinit_wdc(struct devinit *devinit)
676 dpavlin 4 {
677     struct wdc_data *d;
678     uint64_t alt_status_addr;
679 dpavlin 18 int i, tick_shift = WDC_TICK_SHIFT;
680 dpavlin 4
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 dpavlin 14 d->irq_nr = devinit->irq_nr;
688 dpavlin 4
689 dpavlin 14 /* 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 dpavlin 4
694 dpavlin 14 alt_status_addr = devinit->addr + 0x206;
695    
696 dpavlin 4 /* Special hack for pcic/hpcmips: TODO: Fix */
697 dpavlin 14 if (devinit->addr == 0x14000180)
698 dpavlin 4 alt_status_addr = 0x14000386;
699    
700 dpavlin 14 /* 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 dpavlin 4
708 dpavlin 14 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 dpavlin 18 if (devinit->machine->machine_type != MACHINE_HPCMIPS)
715     tick_shift += 2;
716    
717 dpavlin 14 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
718 dpavlin 18 d, tick_shift);
719 dpavlin 14
720     return 1;
721 dpavlin 4 }
722    

  ViewVC Help
Powered by ViewVC 1.1.26