/[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 24 - (hide annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 25937 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1256 2006/06/23 20:43:44 debug Exp $
20060219	Various minor updates. Removing the old MIPS16 skeleton code,
		because it will need to be rewritten for dyntrans anyway.
20060220-22	Removing the non-working dyntrans backend support.
		Continuing on the 64-bit dyntrans virtual memory generalization.
20060223	More work on the 64-bit vm generalization.
20060225	Beginning on MIPS dyntrans load/store instructions.
		Minor PPC updates (64-bit load/store, etc).
		Fixes for the variable-instruction-length framework, some
		minor AVR updates (a simple Hello World program works!).
		Beginning on a skeleton for automatically generating documen-
		tation (for devices etc.).
20060226	PPC updates (adding some more 64-bit instructions, etc).
		AVR updates (more instructions).
		FINALLY found and fixed the zs bug, making NetBSD/macppc
		accept the serial console.
20060301	Adding more AVR instructions.
20060304	Continuing on AVR-related stuff. Beginning on a framework for
		cycle-accurate device emulation. Adding an experimental "PAL
		TV" device (just a dummy so far).
20060305	Adding more AVR instructions.
		Adding a dummy epcom serial controller (for TS7200 emulation).
20060310	Removing the emul() command from configuration files, so only
		net() and machine() are supported.
		Minor progress on the MIPS dyntrans rewrite.
20060311	Continuing on the MIPS dyntrans rewrite (adding more
		instructions, etc).
20060315	Adding more instructions (sllv, srav, srlv, bgtz[l], blez[l],
		beql, bnel, slti[u], various loads and stores).
20060316	Removing the ALWAYS_SIGNEXTEND_32 option, since it was rarely
		used.
		Adding more MIPS dyntrans instructions, and fixing bugs.
20060318	Implementing fast loads/stores for MIPS dyntrans (big/little
		endian, 32-bit and 64-bit modes).
20060320	Making MIPS dyntrans the default configure option; use
		"--enable-oldmips" to use the old bintrans system.
		Adding MIPS dyntrans dmult[u]; minor updates.
20060322	Continuing... adding some more instructions.
		Adding a simple skeleton for demangling C++ "_ZN" symbols.
20060323	Moving src/debugger.c into a new directory (src/debugger/).
20060324	Fixing the hack used to load PPC ELFs (useful for relocated
		Linux/ppc kernels), and adding a dummy G3 machine mode.
20060325-26	Beginning to experiment with GDB remote serial protocol
		connections; adding a -G command line option for selecting
		which TCP port to listen to.
20060330	Beginning a major cleanup to replace things like "0x%016llx"
		with more correct "0x%016"PRIx64, etc.
		Continuing on the GDB remote serial protocol support.
20060331	More cleanup, and some minor GDB remote progress.
20060402	Adding a hack to the configure script, to allow compilation
		on systems that lack PRIx64 etc.
20060406	Removing the temporary FreeBSD/arm hack in dev_ns16550.c and
		replacing it with a better fix from Olivier Houchard.
20060407	A remote debugger (gdb or ddd) can now start and stop the
		emulator using the GDB remote serial protocol, and registers
		and memory can be read. MIPS only for now.
20060408	More GDB progress: single-stepping also works, and also adding
		support for ARM, PowerPC, and Alpha targets.
		Continuing on the delay-slot-across-page-boundary issue.
20060412	Minor update: beginning to add support for the SPARC target
		to the remote GDB functionality.
20060414	Various MIPS updates: adding more instructions for dyntrans
		(eret, add), and making some exceptions work. Fixing a bug
		in dmult[u].
		Implementing the first SPARC instructions (sethi, or).
20060415	Adding "magic trap" instructions so that PROM calls can be
		software emulated in MIPS dyntrans.
		Adding more MIPS dyntrans instructions (ddiv, dadd) and
		fixing another bug in dmult.
20060416	More MIPS dyntrans progress: adding [d]addi, movn, movz, dsllv,
		rfi, an ugly hack for supporting R2000/R3000 style faked caches,
		preliminary interrupt support, and various other updates and
		bugfixes.
20060417	Adding more SPARC instructions (add, sub, sll[x], sra[x],
		srl[x]), and useful SPARC header definitions.
		Adding the first (trivial) x86/AMD64 dyntrans instructions (nop,
		cli/sti, stc/clc, std/cld, simple mov, inc ax). Various other
		x86 updates related to variable instruction length stuff.
		Adding unaligned loads/stores to the MIPS dyntrans mode (but
		still using the pre-dyntrans (slow) imlementation).
20060419	Fixing a MIPS dyntrans exception-in-delay-slot bug.
		Removing the old "show opcode statistics" functionality, since
		it wasn't really useful and isn't implemented for dyntrans.
		Single-stepping (or running with instruction trace) now looks
		ok with dyntrans with delay-slot architectures.
20060420	Minor hacks (removing the -B command line option when compiled
		for non-bintrans, and some other very minor updates).
		Adding (slow) MIPS dyntrans load-linked/store-conditional.
20060422	Applying fixes for bugs discovered by Nils Weller's nwcc
		(static DEC memmap => now per machine, and adding an extern
		keyword in cpu_arm_instr.c).
		Finally found one of the MIPS dyntrans bugs that I've been
		looking for (copy/paste spelling error BIG vs LITTLE endian in
		cpu_mips_instr_loadstore.c for 16-bit fast stores).
		FINALLY found the major MIPS dyntrans bug: slti vs sltiu
		signed/unsigned code in cpu_mips_instr.c. :-)
		Adding more MIPS dyntrans instructions (lwc1, swc1, bgezal[l],
		ctc1, tlt[u], tge[u], tne, beginning on rdhwr).
		NetBSD/hpcmips can now reach userland when using dyntrans :-)
		Adding some more x86 dyntrans instructions.
		Finally removed the old Alpha-specific virtual memory code,
		and replaced it with the generic 64-bit version.
		Beginning to add disassembly support for SPECIAL3 MIPS opcodes.
20060423	Continuing on the delay-slot-across-page-boundary issue;
		adding an end_of_page2 ic slot (like I had planned before, but
		had removed for some reason).
		Adding a quick-and-dirty fallback to legacy coprocessor 1
		code (i.e. skipping dyntrans implementation for now).
		NetBSD/hpcmips and NetBSD/pmax (when running on an emulated
		R4400) can now be installed and run. :-)  (Many bugs left
		to fix, though.)
		Adding more MIPS dyntrans instructions: madd[u], msub[u].
		Cleaning up the SPECIAL2 vs R5900/TX79/C790 "MMI" opcode
		maps somewhat (disassembly and dyntrans instruction decoding).
20060424	Adding an isa_revision field to mips_cpu_types.h, and making
		sure that SPECIAL3 opcodes cause Reserved Instruction
		exceptions on MIPS32/64 revisions lower than 2.
		Adding the SPARC 'ba', 'call', 'jmpl/retl', 'and', and 'xor'
		instructions.
20060425	Removing the -m command line option ("run at most x 
		instructions") and -T ("single_step_on_bad_addr"), because
		they never worked correctly with dyntrans anyway.
		Freshening up the man page.
20060428	Adding more MIPS dyntrans instructions: bltzal[l], idle.
		Enabling MIPS dyntrans compare interrupts.
20060429	FINALLY found the weird dyntrans bug, causing NetBSD etc. to
		behave strangely: some floating point code (conditional
		coprocessor branches) could not be reused from the old
		non-dyntrans code. The "quick-and-dirty fallback" only appeared
		to work. Fixing by implementing bc1* for MIPS dyntrans.
		More MIPS instructions: [d]sub, sdc1, ldc1, dmtc1, dmfc1, cfc0.
		Freshening up MIPS floating point disassembly appearance.
20060430	Continuing on C790/R5900/TX79 disassembly; implementing 128-bit
		"por" and "pextlw".
20060504	Disabling -u (userland emulation) unless compiled as unstable
		development version.
		Beginning on freshening up the testmachine include files,
		to make it easier to reuse those files (placing them in
		src/include/testmachine/), and beginning on a set of "demos"
		or "tutorials" for the testmachine functionality.
		Minor updates to the MIPS GDB remote protocol stub.
		Refreshing doc/experiments.html and gdb_remote.html.
		Enabling Alpha emulation in the stable release configuration,
		even though no guest OSes for Alpha can run yet.
20060505	Adding a generic 'settings' object, which will contain
		references to settable variables (which will later be possible
		to access using the debugger).
20060506	Updating dev_disk and corresponding demo/documentation (and
		switching from SCSI to IDE disk types, so it actually works
		with current test machines :-).
20060510	Adding a -D_LARGEFILE_SOURCE hack for 64-bit Linux hosts,
		so that fseeko() doesn't give a warning.
		Updating the section about how dyntrans works (the "runnable
		IR") in doc/intro.html.
		Instruction updates (some x64=1 checks, some more R5900
		dyntrans stuff: better mul/mult separation from MIPS32/64,
		adding ei and di).
		Updating MIPS cpuregs.h to a newer one (from NetBSD).
		Adding more MIPS dyntrans instructions: deret, ehb.
20060514	Adding disassembly and beginning implementation of SPARC wr
		and wrpr instructions.
20060515	Adding a SUN SPARC machine mode, with dummy SS20 and Ultra1
		machines. Adding the 32-bit "rd psr" instruction.
20060517	Disassembly support for the general SPARC rd instruction.
		Partial implementation of the cmp (subcc) instruction.
		Some other minor updates (making sure that R5900 processors
		start up with the EIE bit enabled, otherwise Linux/playstation2
		receives no interrupts).
20060519	Minor MIPS updates/cleanups.
20060521	Moving the MeshCube machine into evbmips; this seems to work
		reasonably well with a snapshot of a NetBSD MeshCube kernel.
		Cleanup/fix of MIPS config0 register initialization.
20060529	Minor MIPS fixes, including a sign-extension fix to the
		unaligned load/store code, which makes NetBSD/pmax on R3000
		work better with dyntrans. (Ultrix and Linux/DECstation still
		don't work, though.)
20060530	Minor updates to the Alpha machine mode: adding an AlphaBook
		mode, an LCA bus (forwarding accesses to an ISA bus), etc.
20060531	Applying a bugfix for the MIPS dyntrans sc[d] instruction from
		Ondrej Palkovsky. (Many thanks.)
20060601	Minifix to allow ARM immediate msr instruction to not give
		an error for some valid values.
		More Alpha updates.
20060602	Some minor Alpha updates.
20060603	Adding the Alpha cmpbge instruction. NetBSD/alpha prints its
		first boot messages :-) on an emulated Alphabook 1.
20060612	Minor updates; adding a dev_ether.h include file for the
		testmachine ether device. Continuing the hunt for the dyntrans
		bug which makes Linux and Ultrix on DECstation behave
		strangely... FINALLY found it! It seems to be related to
		invalidation of the translation cache, on tlbw{r,i}. There
		also seems to be some remaining interrupt-related problems.
20060614	Correcting the implementation of ldc1/sdc1 for MIPS dyntrans
		(so that it uses 16 32-bit registers if the FR bit in the
		status register is not set).
20060616	REMOVING BINTRANS COMPLETELY!
		Removing the old MIPS interpretation mode.
		Removing the MFHILO_DELAY and instruction delay stuff, because
		they wouldn't work with dyntrans anyway.
20060617	Some documentation updates (adding "NetBSD-archive" to some
		URLs, and new Debian/DECstation installation screenshots).
		Removing the "tracenull" and "enable-caches" configure options.
		Improving MIPS dyntrans performance somewhat (only invalidate
		translations if necessary, on writes to the entryhi register,
		instead of doing it for all cop0 writes).
20060618	More cleanup after the removal of the old MIPS emulation.
		Trying to fix the MIPS dyntrans performance bugs/bottlenecks;
		only semi-successful so far (for R3000).
20060620	Minor update to allow clean compilation again on Tru64/Alpha.
20060622	MIPS cleanup and fixes (removing the pc_last stuff, which
		doesn't make sense with dyntrans anyway, and fixing a cross-
		page-delay-slot-with-exception case in end_of_page).
		Removing the old max_random_cycles_per_chunk stuff, and the
		concept of cycles vs instructions for MIPS emulation.
		FINALLY found and fixed the bug which caused NetBSD/pmax
		clocks to behave strangely (it was a load to the zero register,
		which was treated as a NOP; now it is treated as a load to a
		dummy scratch register).
20060623	Increasing the dyntrans chunk size back to
		N_SAFE_DYNTRANS_LIMIT, instead of N_SAFE_DYNTRANS_LIMIT/2.
		Preparing for a quick release, even though there are known
		bugs, and performance for non-R3000 MIPS emulation is very
		poor. :-/
		Reverting to half the dyntrans chunk size again, because
		NetBSD/cats seemed less stable with full size chunks. :(
		NetBSD/sgimips 3.0 can now run :-)  (With release 0.3.8, only
		NetBSD/sgimips 2.1 worked, not 3.0.)

==============  RELEASE 0.4.0  ==============


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 24 * $Id: dev_wdc.c,v 1.66 2006/04/20 16:59: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 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 dpavlin 24 char *s = "0x%04"PRIx64" ]\n";
592 dpavlin 20 if (len == 1)
593 dpavlin 24 s = "0x%02"PRIx64" ]\n";
594 dpavlin 20 if (len == 4)
595 dpavlin 24 s = "0x%08"PRIx64" ]\n";
596 dpavlin 20 if (len == 8)
597 dpavlin 24 s = "0x%016"PRIx64" ]\n";
598 dpavlin 20 debug("[ wdc: read from DATA: ");
599 dpavlin 24 debug(s, (uint64_t) odata);
600 dpavlin 20 }
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 dpavlin 24 char *s = "0x%04"PRIx64" ]\n";
633 dpavlin 20 if (len == 1)
634 dpavlin 24 s = "0x%02"PRIx64" ]\n";
635 dpavlin 20 if (len == 4)
636 dpavlin 24 s = "0x%08"PRIx64" ]\n";
637 dpavlin 20 if (len == 8)
638 dpavlin 24 s = "0x%016"PRIx64" ]\n";
639 dpavlin 20 debug("[ wdc: write to DATA: ");
640 dpavlin 24 debug(s, (uint64_t) idata);
641 dpavlin 20 }
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 24 unsigned char *buf = malloc(512 * count);
753 dpavlin 18 unsigned char *b = buf;
754    
755 dpavlin 24 if (buf == NULL) {
756     fprintf(stderr, "out of memory\n");
757     exit(1);
758     }
759    
760 dpavlin 18 if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) {
761     b = d->inbuf + d->inbuf_tail;
762     d->inbuf_tail = (d->inbuf_tail + 512
763     * count) % WDC_INBUF_SIZE;
764     } else {
765     for (i=0; i<512 * count; i++)
766     buf[i] = wdc_get_inbuf(d);
767 dpavlin 4 }
768    
769     diskimage_access(cpu->machine,
770 dpavlin 6 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
771 dpavlin 18 d->write_offset, b, 512 * count);
772 dpavlin 4
773 dpavlin 20 d->write_count -= count;
774     d->write_offset += 512 * count;
775 dpavlin 4
776     d->delayed_interrupt = INT_DELAY;
777    
778     if (d->write_count == 0)
779     d->write_in_progress = 0;
780 dpavlin 24
781     free(buf);
782 dpavlin 4 }
783     }
784     break;
785    
786     case wd_error: /* 1: error (r), precomp (w) */
787 dpavlin 20 if (writeflag == MEM_READ) {
788 dpavlin 4 odata = d->error;
789 dpavlin 22 debug("[ wdc: read from ERROR: 0x%02x ]\n", (int)odata);
790 dpavlin 4 /* TODO: is the error value cleared on read? */
791     d->error = 0;
792     } else {
793     d->precomp = idata;
794 dpavlin 14 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
795 dpavlin 4 }
796     break;
797    
798 dpavlin 20 case wd_seccnt: /* 2: sector count (or "ireason" for ATAPI) */
799     if (writeflag == MEM_READ) {
800 dpavlin 4 odata = d->seccnt;
801 dpavlin 20 if (d->atapi_cmd_in_progress) {
802     odata = d->atapi_phase & (WDCI_CMD | WDCI_IN);
803     }
804 dpavlin 14 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
805 dpavlin 4 } else {
806     d->seccnt = idata;
807 dpavlin 14 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
808 dpavlin 4 }
809     break;
810    
811     case wd_sector: /* 3: first sector */
812 dpavlin 20 if (writeflag == MEM_READ) {
813 dpavlin 4 odata = d->sector;
814 dpavlin 14 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
815 dpavlin 4 } else {
816     d->sector = idata;
817 dpavlin 14 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
818 dpavlin 4 }
819     break;
820    
821     case wd_cyl_lo: /* 4: cylinder low */
822 dpavlin 20 if (writeflag == MEM_READ) {
823 dpavlin 4 odata = d->cyl_lo;
824 dpavlin 20 if (d->cur_command == COMMAND_RESET &&
825     diskimage_is_a_cdrom(cpu->machine,
826     d->drive + d->base_drive, DISKIMAGE_IDE))
827     odata = 0x14;
828     if (d->atapi_cmd_in_progress) {
829     int x = d->atapi_len;
830     if (x > 32768)
831     x = 32768;
832     odata = x & 255;
833     }
834 dpavlin 14 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
835 dpavlin 4 } else {
836     d->cyl_lo = idata;
837 dpavlin 14 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
838 dpavlin 4 }
839     break;
840    
841 dpavlin 20 case wd_cyl_hi: /* 5: cylinder high */
842     if (writeflag == MEM_READ) {
843 dpavlin 4 odata = d->cyl_hi;
844 dpavlin 20 if (d->cur_command == COMMAND_RESET &&
845     diskimage_is_a_cdrom(cpu->machine,
846     d->drive + d->base_drive, DISKIMAGE_IDE))
847     odata = 0xeb;
848     if (d->atapi_cmd_in_progress) {
849     int x = d->atapi_len;
850     if (x > 32768)
851     x = 32768;
852     odata = (x >> 8) & 255;
853     }
854 dpavlin 14 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
855 dpavlin 4 } else {
856     d->cyl_hi = idata;
857 dpavlin 14 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
858 dpavlin 4 }
859     break;
860    
861     case wd_sdh: /* 6: sectorsize/drive/head */
862     if (writeflag==MEM_READ) {
863     odata = (d->sectorsize << 6) + (d->lba << 5) +
864     (d->drive << 4) + (d->head);
865     debug("[ wdc: read from SDH: 0x%02x (sectorsize %i,"
866     " lba=%i, drive %i, head %i) ]\n", (int)odata,
867     d->sectorsize, d->lba, d->drive, d->head);
868     } else {
869     d->sectorsize = (idata >> 6) & 3;
870     d->lba = (idata >> 5) & 1;
871     d->drive = (idata >> 4) & 1;
872     d->head = idata & 0xf;
873     debug("[ wdc: write to SDH: 0x%02x (sectorsize %i,"
874     " lba=%i, drive %i, head %i) ]\n", (int)idata,
875     d->sectorsize, d->lba, d->drive, d->head);
876     }
877     break;
878    
879     case wd_command: /* 7: command or status */
880     if (writeflag==MEM_READ) {
881     odata = status_byte(d, cpu);
882 dpavlin 6 if (!quiet_mode)
883     debug("[ wdc: read from STATUS: 0x%02x ]\n",
884 dpavlin 14 (int)odata);
885 dpavlin 4 cpu_interrupt_ack(cpu, d->irq_nr);
886 dpavlin 20 d->int_asserted = 0;
887 dpavlin 4 d->delayed_interrupt = 0;
888     } else {
889 dpavlin 14 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
890 dpavlin 20 wdc_command(cpu, d, idata);
891 dpavlin 4 }
892     break;
893    
894     default:
895     if (writeflag==MEM_READ)
896     debug("[ wdc: read from 0x%02x ]\n",
897     (int)relative_addr);
898     else
899     debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
900     (int)relative_addr, (int)idata);
901     }
902    
903 dpavlin 20 if (cpu->machine->machine_type != MACHINE_HPCMIPS &&
904     cpu->machine->machine_type != MACHINE_EVBMIPS &&
905     cpu->machine->machine_type != MACHINE_BEBOX)
906 dpavlin 18 dev_wdc_tick(cpu, extra);
907 dpavlin 4
908 dpavlin 18 if (writeflag == MEM_READ) {
909     if (relative_addr == wd_data)
910     memory_writemax64(cpu, data, len, odata);
911     else
912     data[0] = odata;
913     }
914    
915 dpavlin 4 return 1;
916     }
917    
918    
919 dpavlin 22 DEVINIT(wdc)
920 dpavlin 4 {
921     struct wdc_data *d;
922     uint64_t alt_status_addr;
923 dpavlin 18 int i, tick_shift = WDC_TICK_SHIFT;
924 dpavlin 4
925     d = malloc(sizeof(struct wdc_data));
926     if (d == NULL) {
927     fprintf(stderr, "out of memory\n");
928     exit(1);
929     }
930     memset(d, 0, sizeof(struct wdc_data));
931 dpavlin 22 d->irq_nr = devinit->irq_nr;
932     d->addr_mult = devinit->addr_mult;
933 dpavlin 20 d->data_debug = 1;
934    
935     d->inbuf = zeroed_alloc(WDC_INBUF_SIZE);
936    
937 dpavlin 14 /* base_drive = 0 for the primary controller, 2 for the secondary. */
938     d->base_drive = 0;
939     if ((devinit->addr & 0xfff) == 0x170)
940     d->base_drive = 2;
941 dpavlin 4
942 dpavlin 14 alt_status_addr = devinit->addr + 0x206;
943    
944 dpavlin 22 /* Special hacks for individual machines: */
945     switch (devinit->machine->machine_type) {
946     case MACHINE_MACPPC:
947     alt_status_addr = devinit->addr + 0x160;
948     break;
949     case MACHINE_HPCMIPS:
950     /* TODO: Fix */
951     if (devinit->addr == 0x14000180)
952     alt_status_addr = 0x14000386;
953     break;
954     case MACHINE_IQ80321:
955     alt_status_addr = devinit->addr + 0x402;
956     break;
957     }
958 dpavlin 4
959 dpavlin 14 /* Get disk geometries: */
960     for (i=0; i<2; i++)
961     if (diskimage_exist(devinit->machine, d->base_drive +i,
962     DISKIMAGE_IDE))
963     diskimage_getchs(devinit->machine, d->base_drive + i,
964     DISKIMAGE_IDE, &d->cyls[i], &d->heads[i],
965     &d->sectors_per_track[i]);
966 dpavlin 4
967 dpavlin 14 memory_device_register(devinit->machine->memory, "wdc_altstatus",
968 dpavlin 20 alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL);
969 dpavlin 14 memory_device_register(devinit->machine->memory, devinit->name,
970 dpavlin 22 devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access,
971     d, DM_DEFAULT, NULL);
972 dpavlin 14
973 dpavlin 20 if (devinit->machine->machine_type != MACHINE_HPCMIPS &&
974     devinit->machine->machine_type != MACHINE_EVBMIPS)
975     tick_shift += 1;
976 dpavlin 18
977 dpavlin 14 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
978 dpavlin 24 d, tick_shift, 0.0);
979 dpavlin 14
980     return 1;
981 dpavlin 4 }
982    

  ViewVC Help
Powered by ViewVC 1.1.26