/[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 22 - (hide annotations)
Mon Oct 8 16:19:37 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 25781 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1121 2006/02/18 21:03:08 debug Exp $
20051126	Cobalt and PReP now work with the 21143 NIC.
		Continuing on Alpha dyntrans things.
		Fixing some more left-shift-by-24 to unsigned.
20051127	Working on OpenFirmware emulation; major cleanup/redesign.
		Progress on MacPPC emulation: NetBSD detects two CPUs (when
		running with -n 2), framebuffer output (for text) works.
		Adding quick-hack Bandit PCI controller and "gc" interrupt
		controller for MacPPC.
20051128	Changing from a Bandit to a Uni-North controller for macppc.
		Continuing on OpenFirmware and MacPPC emulation in general
		(obio controller, and wdc attached to the obio seems to work).
20051129	More work on MacPPC emulation (adding a dummy ADB controller).
		Continuing the PCI bus cleanup (endianness and tag composition)
		and rewriting all PCI controllers' access functions.
20051130	Various minor PPC dyntrans optimizations.
		Manually inlining some parts of the framebuffer redraw routine.
		Slowly beginning the conversion of the old MIPS emulation into
		dyntrans (but this will take quite some time to get right).
		Generalizing quick_pc_to_pointers.
20051201	Documentation update (David Muse has made available a kernel
		which simplifies Debian/DECstation installation).
		Continuing on the ADB bus controller.
20051202	Beginning a rewrite of the Zilog serial controller (dev_zs).
20051203	Continuing on the zs rewrite (now called dev_z8530); conversion
		to devinit style.
		Reworking some of the input-only vs output-only vs input-output
		details of src/console.c, better warning messages, and adding
		a debug dump.
		Removing the concept of "device state"; it wasn't really used.
		Changing some debug output (-vv should now be used to show all
		details about devices and busses; not shown during normal
		startup anymore).
		Beginning on some SPARC instruction disassembly support.
20051204	Minor PPC updates (WALNUT skeleton stuff).
		Continuing on the MIPS dyntrans rewrite.
		More progress on the ADB controller (a keyboard is "detected"
		by NetBSD and OpenBSD).
		Downgrading OpenBSD/arc as a guest OS from "working" to
		"almost working" in the documentation.
		Progress on Algor emulation ("v3" PCI controller).
20051205	Minor updates.
20051207	Sorting devices according to address; this reduces complexity
		of device lookups from O(n) to O(log n) in memory_rw (but no
		real performance increase (yet) in experiments).
20051210	Beginning the work on native dyntrans backends (by making a
		simple skeleton; so far only for Alpha hosts).
20051211	Some very minor SPARC updates.
20051215	Fixing a bug in the MIPS mul (note: not mult) instruction,
		so it also works with non-64-bit emulation. (Thanks to Alec
		Voropay for noticing the problem.)
20051216	More work on the fake/empty/simple/skeleton/whatever backend;
		performance doesn't increase, so this isn't really worth it,
		but it was probably worth it to prepare for a real backend
		later.
20051219	More instr call statistics gathering and analysis stuff.
20051220	Another fix for MIPS 'mul'. Also converting mul and {d,}cl{o,z}
		to dyntrans.
		memory_ppc.c syntax error fix (noticed by Peter Valchev).
		Beginning to move out machines from src/machine.c into
		individual files in src/machines (in a way similar to the
		autodev system for devices).
20051222	Updating the documentation regarding NetBSD/pmax 3.0.
20051223	- " - NetBSD/cats 3.0.
20051225	- " - NetBSD/hpcmips 3.0.
20051226	Continuing on the machine registry redesign.
		Adding support for ARM rrx (33-bit rotate).
		Fixing some signed/unsigned issues (exposed by gcc -W).
20051227	Fixing the bug which prevented a NetBSD/prep 3.0 install kernel
		from starting (triggered when an mtmsr was the last instruction
		on a page). Unfortunately not enough to get the kernel to run
		as well as the 2.1 kernels did.
20051230	Some dyntrans refactoring.
20051231	Continuing on the machine registry redesign.
20060101-10	Continuing... moving more machines. Moving MD interrupt stuff
		from machine.c into a new src/machines/interrupts.c.
20060114	Adding various mvmeppc machine skeletons.
20060115	Continuing on mvme* stuff. NetBSD/mvmeppc prints boot messages
		(for MVME1600) and reaches the root device prompt, but no
		specific hardware devices are emulated yet.
20060116	Minor updates to the mvme1600 emulation mode; the Eagle PCI bus
		seems to work without much modification, and a 21143 can be
		detected, interrupts might work (but untested so far).
		Adding a fake MK48Txx (mkclock) device, for NetBSD/mvmeppc.
20060121	Adding an aux control register for ARM. (A BIG thank you to
		Olivier Houchard for tracking down this bug.)
20060122	Adding more ARM instructions (smulXY), and dev_iq80321_7seg.
20060124	Adding disassembly of more ARM instructions (mia*, mra/mar),
		and some semi-bogus XScale and i80321 registers.
20060201-02	Various minor updates. Moving the last machines out of
		machine.c.
20060204	Adding a -c command line option, for running debugger commands
		before the simulation starts, but after all files have been
		loaded.
		Minor iq80321-related updates.
20060209	Minor hacks (DEVINIT macro, etc).
		Preparing for the generalization of the 64-bit dyntrans address
		translation subsystem.
20060216	Adding ARM ldrd (double-register load).
20060217	Continuing on various ARM-related stuff.
20060218	More progress on the ATA/wdc emulation for NetBSD/iq80321.
		NetBSD/evbarm can now be installed :-)  Updating the docs, etc.
		Continuing on Algor emulation.

==============  RELEASE 0.3.8  ==============


1 dpavlin 4 /*
2 dpavlin 22 * Copyright (C) 2004-2006 Anders Gavare. All rights reserved.
3 dpavlin 4 *
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 22 * $Id: dev_wdc.c,v 1.63 2006/02/18 13:15:21 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 20 *
59     * NetBSD/malta also bugs out if wdc interrupts come too quickly. Hm.
60 dpavlin 14 */
61 dpavlin 4 #define INT_DELAY 1
62    
63 dpavlin 6 extern int quiet_mode;
64    
65 dpavlin 4 /* #define debug fatal */
66    
67     struct wdc_data {
68     int irq_nr;
69 dpavlin 22 int addr_mult;
70 dpavlin 4 int base_drive;
71 dpavlin 14 int data_debug;
72 dpavlin 4
73 dpavlin 14 /* Cached values: */
74     int cyls[2];
75     int heads[2];
76     int sectors_per_track[2];
77 dpavlin 4
78 dpavlin 20 unsigned char *inbuf;
79 dpavlin 4 int inbuf_head;
80     int inbuf_tail;
81    
82 dpavlin 14 int delayed_interrupt;
83 dpavlin 20 int int_asserted;
84    
85 dpavlin 4 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 dpavlin 20
101     int atapi_cmd_in_progress;
102     int atapi_phase;
103     struct scsi_transfer *atapi_st;
104     int atapi_len;
105 dpavlin 22 size_t atapi_received;
106 dpavlin 20
107     unsigned char identify_struct[512];
108 dpavlin 4 };
109    
110    
111 dpavlin 20 #define COMMAND_RESET 0x100
112    
113    
114 dpavlin 4 /*
115     * dev_wdc_tick():
116     */
117     void dev_wdc_tick(struct cpu *cpu, void *extra)
118     {
119     struct wdc_data *d = extra;
120 dpavlin 20 int old_di = d->delayed_interrupt;
121 dpavlin 4
122 dpavlin 20 if (d->delayed_interrupt)
123 dpavlin 4 d->delayed_interrupt --;
124    
125 dpavlin 20 if (old_di == 1 || d->int_asserted) {
126     cpu_interrupt(cpu, d->irq_nr);
127     d->int_asserted = 1;
128 dpavlin 4 }
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 dpavlin 18 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
144     " Increase WDC_MAX_SECTORS. ]\n");
145 dpavlin 4 }
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 dpavlin 6 fatal("[ wdc: WARNING! someone is reading too much from the "
159     "wdc inbuf! ]\n");
160 dpavlin 4 return -1;
161     }
162    
163     d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
164     return c;
165     }
166    
167    
168     /*
169 dpavlin 20 * wdc_initialize_identify_struct():
170 dpavlin 4 */
171     static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
172     {
173 dpavlin 6 uint64_t total_size;
174 dpavlin 20 int flags, cdrom = 0;
175 dpavlin 14 char namebuf[40];
176 dpavlin 4
177 dpavlin 6 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
178     DISKIMAGE_IDE);
179 dpavlin 20 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
180     DISKIMAGE_IDE))
181     cdrom = 1;
182 dpavlin 4
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 dpavlin 20 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 dpavlin 4
194     /* 1: nr of cylinders */
195 dpavlin 20 d->identify_struct[2 * 1 + 0] = d->cyls[d->drive] >> 8;
196     d->identify_struct[2 * 1 + 1] = d->cyls[d->drive];
197 dpavlin 4
198     /* 3: nr of heads */
199 dpavlin 14 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
200     d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
201 dpavlin 4
202     /* 6: sectors per track */
203 dpavlin 14 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 dpavlin 4
206     /* 10-19: Serial number */
207 dpavlin 20 memcpy(&d->identify_struct[2 * 10], "#0 ", 20);
208 dpavlin 4
209     /* 23-26: Firmware version */
210 dpavlin 20 memcpy(&d->identify_struct[2 * 23], "1.0 ", 8);
211 dpavlin 4
212     /* 27-46: Model number */
213 dpavlin 14 if (diskimage_getname(cpu->machine, d->drive + d->base_drive,
214     DISKIMAGE_IDE, namebuf, sizeof(namebuf))) {
215 dpavlin 22 size_t i;
216 dpavlin 14 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 dpavlin 4
227     /* 47: max sectors per multitransfer */
228     d->identify_struct[2 * 47 + 0] = 0x80;
229 dpavlin 20 d->identify_struct[2 * 47 + 1] = 128;
230 dpavlin 4
231 dpavlin 20 /* 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 dpavlin 4 /* 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 dpavlin 20 /* 60-61: total nr of addressable sectors */
251 dpavlin 4 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 dpavlin 20 /* 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 dpavlin 4 }
266    
267    
268     /*
269 dpavlin 14 * wdc__read():
270     */
271     void wdc__read(struct cpu *cpu, struct wdc_data *d)
272     {
273 dpavlin 20 #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 dpavlin 14 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 dpavlin 18 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 dpavlin 14 }
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 dpavlin 20 d->write_in_progress = d->cur_command;
337 dpavlin 14 d->write_count = count;
338     d->write_offset = offset;
339    
340     /* TODO: result code? */
341     }
342    
343    
344     /*
345 dpavlin 4 * status_byte():
346 dpavlin 14 *
347     * Return a reasonable status byte corresponding to the controller's current
348     * state.
349 dpavlin 4 */
350     static int status_byte(struct wdc_data *d, struct cpu *cpu)
351     {
352     int odata = 0;
353 dpavlin 6 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
354     DISKIMAGE_IDE))
355 dpavlin 14 odata |= WDCS_DRDY | WDCS_DSC;
356 dpavlin 4 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 dpavlin 20 if (d->atapi_cmd_in_progress && (d->atapi_phase & WDCS_DRQ)) {
363     odata |= WDCS_DRQ;
364     }
365 dpavlin 4 return odata;
366     }
367    
368    
369     /*
370     * dev_wdc_altstatus_access():
371     */
372 dpavlin 22 DEVICE_ACCESS(wdc_altstatus)
373 dpavlin 4 {
374     struct wdc_data *d = extra;
375     uint64_t idata = 0, odata = 0;
376    
377 dpavlin 18 idata = data[0];
378 dpavlin 4
379 dpavlin 20 /* Same as the normal status byte: */
380 dpavlin 4 odata = status_byte(d, cpu);
381    
382     if (writeflag==MEM_READ)
383     debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
384     (int)odata);
385 dpavlin 20 else {
386 dpavlin 4 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
387     (int)idata);
388 dpavlin 20 if (idata & WDCTL_4BIT)
389     d->cur_command = COMMAND_RESET;
390     }
391 dpavlin 4
392     if (writeflag == MEM_READ)
393 dpavlin 18 data[0] = odata;
394 dpavlin 4
395     return 1;
396     }
397    
398    
399     /*
400 dpavlin 20 * wdc_command():
401     */
402     void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata)
403     {
404 dpavlin 22 size_t i;
405 dpavlin 20
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 dpavlin 22 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 dpavlin 20 /* 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 dpavlin 4 * dev_wdc_access():
549     */
550 dpavlin 22 DEVICE_ACCESS(wdc)
551 dpavlin 4 {
552     struct wdc_data *d = extra;
553     uint64_t idata = 0, odata = 0;
554 dpavlin 14 int i;
555 dpavlin 4
556 dpavlin 22 relative_addr /= d->addr_mult;
557    
558 dpavlin 18 if (writeflag == MEM_WRITE) {
559     if (relative_addr == wd_data)
560     idata = memory_readmax64(cpu, data, len);
561 dpavlin 20 else {
562     if (len != 1)
563     fatal("[ wdc: WARNING! non-8-bit access! ]\n");
564 dpavlin 18 idata = data[0];
565 dpavlin 20 }
566 dpavlin 18 }
567 dpavlin 4
568     switch (relative_addr) {
569    
570     case wd_data: /* 0: data */
571 dpavlin 18 if (writeflag == MEM_READ) {
572 dpavlin 22 odata = wdc_get_inbuf(d);
573 dpavlin 4
574 dpavlin 20 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 dpavlin 4 }
589    
590 dpavlin 20 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 dpavlin 18 #if 0
621 dpavlin 20 if (d->inbuf_tail != d->inbuf_head)
622 dpavlin 18 #else
623 dpavlin 20 if (d->inbuf_tail != d->inbuf_head &&
624     ((d->inbuf_tail - d->inbuf_head) % 512)
625     == 0)
626 dpavlin 18 #endif
627 dpavlin 20 d->delayed_interrupt = INT_DELAY;
628     }
629 dpavlin 4 } else {
630     int inbuf_len;
631 dpavlin 20 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 dpavlin 4 fatal("[ wdc: write to DATA, but not "
645     "expecting any? (len=%i): 0x%08lx ]\n",
646     (int)len, (long)idata);
647     }
648    
649 dpavlin 20 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 dpavlin 4 }
680    
681     inbuf_len = d->inbuf_head - d->inbuf_tail;
682     while (inbuf_len < 0)
683     inbuf_len += WDC_INBUF_SIZE;
684    
685 dpavlin 20 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 dpavlin 18 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 dpavlin 4 }
763    
764     diskimage_access(cpu->machine,
765 dpavlin 6 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
766 dpavlin 18 d->write_offset, b, 512 * count);
767 dpavlin 4
768 dpavlin 20 d->write_count -= count;
769     d->write_offset += 512 * count;
770 dpavlin 4
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 dpavlin 20 if (writeflag == MEM_READ) {
781 dpavlin 4 odata = d->error;
782 dpavlin 22 debug("[ wdc: read from ERROR: 0x%02x ]\n", (int)odata);
783 dpavlin 4 /* TODO: is the error value cleared on read? */
784     d->error = 0;
785     } else {
786     d->precomp = idata;
787 dpavlin 14 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
788 dpavlin 4 }
789     break;
790    
791 dpavlin 20 case wd_seccnt: /* 2: sector count (or "ireason" for ATAPI) */
792     if (writeflag == MEM_READ) {
793 dpavlin 4 odata = d->seccnt;
794 dpavlin 20 if (d->atapi_cmd_in_progress) {
795     odata = d->atapi_phase & (WDCI_CMD | WDCI_IN);
796     }
797 dpavlin 14 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
798 dpavlin 4 } else {
799     d->seccnt = idata;
800 dpavlin 14 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
801 dpavlin 4 }
802     break;
803    
804     case wd_sector: /* 3: first sector */
805 dpavlin 20 if (writeflag == MEM_READ) {
806 dpavlin 4 odata = d->sector;
807 dpavlin 14 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
808 dpavlin 4 } else {
809     d->sector = idata;
810 dpavlin 14 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
811 dpavlin 4 }
812     break;
813    
814     case wd_cyl_lo: /* 4: cylinder low */
815 dpavlin 20 if (writeflag == MEM_READ) {
816 dpavlin 4 odata = d->cyl_lo;
817 dpavlin 20 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 dpavlin 14 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
828 dpavlin 4 } else {
829     d->cyl_lo = idata;
830 dpavlin 14 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
831 dpavlin 4 }
832     break;
833    
834 dpavlin 20 case wd_cyl_hi: /* 5: cylinder high */
835     if (writeflag == MEM_READ) {
836 dpavlin 4 odata = d->cyl_hi;
837 dpavlin 20 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 dpavlin 14 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
848 dpavlin 4 } else {
849     d->cyl_hi = idata;
850 dpavlin 14 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
851 dpavlin 4 }
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 dpavlin 6 if (!quiet_mode)
876     debug("[ wdc: read from STATUS: 0x%02x ]\n",
877 dpavlin 14 (int)odata);
878 dpavlin 4 cpu_interrupt_ack(cpu, d->irq_nr);
879 dpavlin 20 d->int_asserted = 0;
880 dpavlin 4 d->delayed_interrupt = 0;
881     } else {
882 dpavlin 14 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
883 dpavlin 20 wdc_command(cpu, d, idata);
884 dpavlin 4 }
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 dpavlin 20 if (cpu->machine->machine_type != MACHINE_HPCMIPS &&
897     cpu->machine->machine_type != MACHINE_EVBMIPS &&
898     cpu->machine->machine_type != MACHINE_BEBOX)
899 dpavlin 18 dev_wdc_tick(cpu, extra);
900 dpavlin 4
901 dpavlin 18 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 dpavlin 4 return 1;
909     }
910    
911    
912 dpavlin 22 DEVINIT(wdc)
913 dpavlin 4 {
914     struct wdc_data *d;
915     uint64_t alt_status_addr;
916 dpavlin 18 int i, tick_shift = WDC_TICK_SHIFT;
917 dpavlin 4
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 dpavlin 22 d->irq_nr = devinit->irq_nr;
925     d->addr_mult = devinit->addr_mult;
926 dpavlin 20 d->data_debug = 1;
927    
928     d->inbuf = zeroed_alloc(WDC_INBUF_SIZE);
929    
930 dpavlin 14 /* 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 dpavlin 4
935 dpavlin 14 alt_status_addr = devinit->addr + 0x206;
936    
937 dpavlin 22 /* 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 dpavlin 4
952 dpavlin 14 /* 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 dpavlin 4
960 dpavlin 14 memory_device_register(devinit->machine->memory, "wdc_altstatus",
961 dpavlin 20 alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL);
962 dpavlin 14 memory_device_register(devinit->machine->memory, devinit->name,
963 dpavlin 22 devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access,
964     d, DM_DEFAULT, NULL);
965 dpavlin 14
966 dpavlin 20 if (devinit->machine->machine_type != MACHINE_HPCMIPS &&
967     devinit->machine->machine_type != MACHINE_EVBMIPS)
968     tick_shift += 1;
969 dpavlin 18
970 dpavlin 14 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
971 dpavlin 18 d, tick_shift);
972 dpavlin 14
973     return 1;
974 dpavlin 4 }
975    

  ViewVC Help
Powered by ViewVC 1.1.26