/[gxemul]/trunk/src/devices/dev_wdc.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 34 - (show annotations)
Mon Oct 8 16:21:17 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 25247 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1480 2007/02/19 01:34:42 debug Exp $
20061029	Changing usleep(1) calls in the debugger to usleep(10000)
20061107	Adding a new disk image option (-d o...) which sets the ISO9660
		filesystem base offset; also making some other hacks to allow
		NetBSD/dreamcast and homebrew demos/games to boot directly
		from a filesystem image.
		Moving Dreamcast-specific stuff in the documentation to its
		own page (dreamcast.html).
		Adding a border to the Dreamcast PVR framebuffer.
20061108	Adding a -T command line option (again?), for halting the
		emulator on unimplemented memory accesses.
20061109	Continuing on various SH4 and Dreamcast related things.
		The emulator should now halt on more unimplemented device
		accesses, instead of just printing a warning, forcing me to
		actually implement missing stuff :)
20061111	Continuing on SH4 and Dreamcast stuff.
		Adding a bogus Landisk (SH4) machine mode.
20061112	Implementing some parts of the Dreamcast GDROM device. With
		some ugly hacks, NetBSD can (barely) mount an ISO image.
20061113	NetBSD/dreamcast now starts booting from the Live CD image,
		but crashes randomly quite early on in the boot process.
20061122	Beginning on a skeleton interrupt.h and interrupt.c for the
		new interrupt subsystem.
20061124	Continuing on the new interrupt system; taking the first steps
		to attempt to connect CPUs (SuperH and MIPS) and devices
		(dev_cons and SH4 timer interrupts) to it. Many things will
		probably break from now on.
20061125	Converting dev_ns16550, dev_8253 to the new interrupt system.
		Attempting to begin to convert the ISA bus.
20061130	Incorporating a patch from Brian Foley for the configure
		script, which checks for X11 libs in /usr/X11R6/lib64 (which
		is used on some Linux systems).
20061227	Adding a note in the man page about booting from Dreamcast
		CDROM images (i.e. that no external kernel is needed).
20061229	Continuing on the interrupt system rewrite: beginning to
		convert more devices, adding abort() calls for legacy interrupt
		system calls so that everything now _has_ to be rewritten!
		Almost all machine modes are now completely broken.
20061230	More progress on removing old interrupt code, mostly related
		to the ISA bus + devices, the LCA bus (on AlphaBook1), and
		the Footbridge bus (for CATS). And some minor PCI stuff.
		Connecting the ARM cpu to the new interrupt system.
		The CATS, NetWinder, and QEMU_MIPS machine modes now work with
		the new interrupt system :)
20061231	Connecting PowerPC CPUs to the new interrupt system.
		Making PReP machines (IBM 6050) work again.
		Beginning to convert the GT PCI controller (for e.g. Malta
		and Cobalt emulation). Some things work, but not everything.
		Updating Copyright notices for 2007.
20070101	Converting dev_kn02 from legacy style to devinit; the 3max
		machine mode now works with the new interrupt system :-]
20070105	Beginning to convert the SGI O2 machine to the new interrupt
		system; finally converting O2 (IP32) devices to devinit, etc.
20070106	Continuing on the interrupt system redesign/rewrite; KN01
		(PMAX), KN230, and Dreamcast ASIC interrupts should work again,
		moving out stuff from machine.h and devices.h into the
		corresponding devices, beginning the rewrite of i80321
		interrupts, etc.
20070107	Beginning on the rewrite of Eagle interrupt stuff (PReP, etc).
20070117	Beginning the rewrite of Algor (V3) interrupts (finally
		changing dev_v3 into devinit style).
20070118	Removing the "bus" registry concept from machine.h, because
		it was practically meaningless.
		Continuing on the rewrite of Algor V3 ISA interrupts.
20070121	More work on Algor interrupts; they are now working again,
		well enough to run NetBSD/algor. :-)
20070122	Converting VR41xx (HPCmips) interrupts. NetBSD/hpcmips
		can be installed using the new interrupt system :-)
20070123	Making the testmips mode work with the new interrupt system.
20070127	Beginning to convert DEC5800 devices to devinit, and to the
		new interrupt system.
		Converting Playstation 2 devices to devinit, and converting
		the interrupt system. Also fixing a severe bug: the interrupt
		mask register on Playstation 2 is bitwise _toggled_ on writes.
20070128	Removing the dummy NetGear machine mode and the 8250 device
		(which was only used by the NetGear machine).
		Beginning to convert the MacPPC GC (Grand Central) interrupt
		controller to the new interrupt system.
		Converting Jazz interrupts (PICA61 etc.) to the new interrupt
		system. NetBSD/arc can be installed again :-)
		Fixing the JAZZ timer (hardcoding it at 100 Hz, works with
		NetBSD and it is better than a completely dummy timer as it
		was before).
		Converting dev_mp to the new interrupt system, although I
		haven't had time to actually test it yet.
		Completely removing src/machines/interrupts.c, cpu_interrupt
		and cpu_interrupt_ack in src/cpu.c, and
		src/include/machine_interrupts.h! Adding fatal error messages
		+ abort() in the few places that are left to fix.
		Converting dev_z8530 to the new interrupt system.
		FINALLY removing the md_int struct completely from the
		machine struct.
		SH4 fixes (adding a PADDR invalidation in the ITLB replacement
		code in memory_sh.c); the NetBSD/dreamcast LiveCD now runs
		all the way to the login prompt, and can be interacted with :-)
		Converting the CPC700 controller (PCI and interrupt controller
		for PM/PPC) to the new interrupt system.
20070129	Fixing MACE ISA interrupts (SGI IP32 emulation). Both NetBSD/
		sgimips' and OpenBSD/sgi's ramdisk kernels can now be
		interacted with again.
20070130	Moving out the MIPS multi_lw and _sw instruction combinations
		so that they are auto-generated at compile time instead.
20070131	Adding detection of amd64/x86_64 hosts in the configure script,
		for doing initial experiments (again :-) with native code
		generation.
		Adding a -k command line option to set the size of the dyntrans
		cache, and a -B command line option to disable native code
		generation, even if GXemul was compiled with support for
		native code generation for the specific host CPU architecture.
20070201	Experimenting with a skeleton for native code generation.
		Changing the default behaviour, so that native code generation
		is now disabled by default, and has to be enabled by using
		-b on the command line.
20070202	Continuing the native code generation experiments.
		Making PCI interrupts work for Footbridge again.
20070203	More native code generation experiments.
		Removing most of the native code generation experimental code,
		it does not make sense to include any quick hacks like this.
		Minor cleanup/removal of some more legacy MIPS interrupt code.
20070204	Making i80321 interrupts work again (for NetBSD/evbarm etc.),
		and fixing the timer at 100 Hz.
20070206	Experimenting with removing the wdc interrupt slowness hack.
20070207	Lowering the number of dyntrans TLB entries for MIPS from
		192 to 128, resulting in a minor speed improvement.
		Minor optimization to the code invalidation routine in
		cpu_dyntrans.c.
20070208	Increasing (experimentally) the nr of dyntrans instructions per
		loop from 60 to 120.
20070210	Commenting out (experimentally) the dyntrans_device_danger
		detection in memory_rw.c.
		Changing the testmips and baremips machines to use a revision 2
		MIPS64 CPU by default, instead of revision 1.
		Removing the dummy i960, IA64, x86, AVR32, and HP PA-RISC
		files, the PC bios emulation, and the Olivetti M700 (ARC) and
		db64360 emulation modes.
20070211	Adding an "mp" demo to the demos directory, which tests the
		SMP functionality of the testmips machine.
		Fixing PReP interrupts some more. NetBSD/prep now boots again.
20070216	Adding a "nop workaround" for booting Mach/PMAX to the
		documentation; thanks to Artur Bujdoso for the values.
		Converting more of the MacPPC interrupt stuff to the new
		system.
		Beginning to convert BeBox interrupts to the new system.
		PPC603e should NOT have the PPC_NO_DEC flag! Removing it.
		Correcting BeBox clock speed (it was set to 100 in the NetBSD
		bootinfo block, but should be 33000000/4), allowing NetBSD
		to start without using the (incorrect) PPC_NO_DEC hack.
20070217	Implementing (slow) AltiVec vector loads and stores, allowing
		NetBSD/macppc to finally boot using the GENERIC kernel :-)
		Updating the documentation with install instructions for
		NetBSD/macppc.
20070218-19	Regression testing for the release.

==============  RELEASE 0.4.4  ==============


1 /*
2 * Copyright (C) 2004-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_wdc.c,v 1.74 2007/02/16 19:57:56 debug Exp $
29 *
30 * Standard "wdc" IDE controller.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cpu.h"
38 #include "device.h"
39 #include "diskimage.h"
40 #include "machine.h"
41 #include "memory.h"
42 #include "misc.h"
43
44 #include "wdcreg.h"
45
46 #define DEV_WDC_LENGTH 8
47 #define WDC_TICK_SHIFT 14
48 #define WDC_MAX_SECTORS 512
49 #define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1))
50
51 extern int quiet_mode;
52
53 /* #define debug fatal */
54
55 struct wdc_data {
56 struct interrupt irq;
57 int addr_mult;
58 int base_drive;
59 int data_debug;
60 int io_enabled;
61
62 /* Cached values: */
63 int cyls[2];
64 int heads[2];
65 int sectors_per_track[2];
66
67 unsigned char *inbuf;
68 int inbuf_head;
69 int inbuf_tail;
70
71 int int_assert;
72
73 int write_in_progress;
74 int write_count;
75 int64_t write_offset;
76
77 int error;
78 int precomp;
79 int seccnt;
80 int sector;
81 int cyl_lo;
82 int cyl_hi;
83 int sectorsize;
84 int lba;
85 int drive;
86 int head;
87 int cur_command;
88
89 int atapi_cmd_in_progress;
90 int atapi_phase;
91 struct scsi_transfer *atapi_st;
92 int atapi_len;
93 size_t atapi_received;
94
95 unsigned char identify_struct[512];
96 };
97
98
99 #define COMMAND_RESET 0x100
100
101
102 DEVICE_TICK(wdc)
103 {
104 struct wdc_data *d = extra;
105
106 if (d->int_assert)
107 INTERRUPT_ASSERT(d->irq);
108 }
109
110
111 /*
112 * wdc_set_io_enabled():
113 *
114 * Set io_enabled to zero to disable the I/O registers temporarily (e.g.
115 * used by PCI code in NetBSD to detect whether multiple controllers collide
116 * in I/O space).
117 *
118 * Return value is old contents of the io_enabled variable.
119 */
120 int wdc_set_io_enabled(struct wdc_data *d, int io_enabled)
121 {
122 int old = d->io_enabled;
123 d->io_enabled = io_enabled;
124 return old;
125 }
126
127
128 /*
129 * wdc_addtoinbuf():
130 *
131 * Write to the inbuf at its head, read at its tail.
132 */
133 static void wdc_addtoinbuf(struct wdc_data *d, int c)
134 {
135 d->inbuf[d->inbuf_head] = c;
136
137 d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE;
138 if (d->inbuf_head == d->inbuf_tail)
139 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
140 " Increase WDC_MAX_SECTORS. ]\n");
141 }
142
143
144 /*
145 * wdc_get_inbuf():
146 *
147 * Read from the tail of inbuf.
148 */
149 static uint64_t wdc_get_inbuf(struct wdc_data *d)
150 {
151 int c = d->inbuf[d->inbuf_tail];
152
153 if (d->inbuf_head == d->inbuf_tail) {
154 fatal("[ wdc: WARNING! someone is reading too much from the "
155 "wdc inbuf! ]\n");
156 return -1;
157 }
158
159 d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
160 return c;
161 }
162
163
164 /*
165 * wdc_initialize_identify_struct():
166 */
167 static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
168 {
169 uint64_t total_size;
170 int flags, cdrom = 0;
171 char namebuf[40];
172
173 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
174 DISKIMAGE_IDE);
175 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
176 DISKIMAGE_IDE))
177 cdrom = 1;
178
179 memset(d->identify_struct, 0, sizeof(d->identify_struct));
180
181 /* Offsets are in 16-bit WORDS! High byte, then low. */
182
183 /* 0: general flags */
184 flags = 1 << 6; /* Fixed */
185 if (cdrom)
186 flags = 0x8580; /* ATAPI, CDROM, removable */
187 d->identify_struct[2 * 0 + 0] = flags >> 8;
188 d->identify_struct[2 * 0 + 1] = flags;
189
190 /* 1: nr of cylinders */
191 d->identify_struct[2 * 1 + 0] = d->cyls[d->drive] >> 8;
192 d->identify_struct[2 * 1 + 1] = d->cyls[d->drive];
193
194 /* 3: nr of heads */
195 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
196 d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
197
198 /* 6: sectors per track */
199 d->identify_struct[2 * 6 + 0] = d->sectors_per_track[d->drive] >> 8;
200 d->identify_struct[2 * 6 + 1] = d->sectors_per_track[d->drive];
201
202 /* 10-19: Serial number */
203 memcpy(&d->identify_struct[2 * 10], "#0 ", 20);
204
205 /* 23-26: Firmware version */
206 memcpy(&d->identify_struct[2 * 23], "1.0 ", 8);
207
208 /* 27-46: Model number */
209 if (diskimage_getname(cpu->machine, d->drive + d->base_drive,
210 DISKIMAGE_IDE, namebuf, sizeof(namebuf))) {
211 size_t i;
212 for (i=0; i<sizeof(namebuf); i++)
213 if (namebuf[i] == 0) {
214 for (; i<sizeof(namebuf); i++)
215 namebuf[i] = ' ';
216 break;
217 }
218 memcpy(&d->identify_struct[2 * 27], namebuf, 40);
219 } else
220 memcpy(&d->identify_struct[2 * 27],
221 "Fake GXemul IDE disk ", 40);
222
223 /* 47: max sectors per multitransfer */
224 d->identify_struct[2 * 47 + 0] = 0x80;
225 d->identify_struct[2 * 47 + 1] = 128;
226
227 /* 49: capabilities: */
228 /* (0x200 = LBA, 0x100 = DMA support.) */
229 d->identify_struct[2 * 49 + 0] = 0;
230 d->identify_struct[2 * 49 + 1] = 0;
231
232 /* 51: PIO timing mode. */
233 d->identify_struct[2 * 51 + 0] = 0x00; /* ? */
234 d->identify_struct[2 * 51 + 1] = 0x00;
235
236 /* 53: 0x02 = fields 64-70 valid, 0x01 = fields 54-58 valid */
237 d->identify_struct[2 * 53 + 0] = 0x00;
238 d->identify_struct[2 * 53 + 1] = 0x02;
239
240 /* 57-58: current capacity in sectors */
241 d->identify_struct[2 * 57 + 0] = ((total_size / 512) >> 24) % 255;
242 d->identify_struct[2 * 57 + 1] = ((total_size / 512) >> 16) % 255;
243 d->identify_struct[2 * 58 + 0] = ((total_size / 512) >> 8) % 255;
244 d->identify_struct[2 * 58 + 1] = (total_size / 512) & 255;
245
246 /* 60-61: total nr of addressable sectors */
247 d->identify_struct[2 * 60 + 0] = ((total_size / 512) >> 24) % 255;
248 d->identify_struct[2 * 60 + 1] = ((total_size / 512) >> 16) % 255;
249 d->identify_struct[2 * 61 + 0] = ((total_size / 512) >> 8) % 255;
250 d->identify_struct[2 * 61 + 1] = (total_size / 512) & 255;
251
252 /* 64: Advanced PIO mode support. 0x02 = mode4, 0x01 = mode3 */
253 d->identify_struct[2 * 64 + 0] = 0x00;
254 d->identify_struct[2 * 64 + 1] = 0x03;
255
256 /* 67, 68: PIO timing */
257 d->identify_struct[2 * 67 + 0] = 0;
258 d->identify_struct[2 * 67 + 1] = 120;
259 d->identify_struct[2 * 68 + 0] = 0;
260 d->identify_struct[2 * 68 + 1] = 120;
261 }
262
263
264 /*
265 * wdc__read():
266 */
267 void wdc__read(struct cpu *cpu, struct wdc_data *d)
268 {
269 #define MAX_SECTORS_PER_CHUNK 64
270 const int max_sectors_per_chunk = MAX_SECTORS_PER_CHUNK;
271 unsigned char buf[512 * MAX_SECTORS_PER_CHUNK];
272 int i, cyl = d->cyl_hi * 256+ d->cyl_lo;
273 int count = d->seccnt? d->seccnt : 256;
274 uint64_t offset = 512 * (d->sector - 1
275 + (int64_t)d->head * d->sectors_per_track[d->drive] +
276 (int64_t)d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
277
278 #if 0
279 /* LBA: */
280 if (d->lba)
281 offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8)
282 + d->sector);
283 printf("WDC read from offset %lli\n", (long long)offset);
284 #endif
285
286 while (count > 0) {
287 int to_read = count > max_sectors_per_chunk?
288 max_sectors_per_chunk : count;
289
290 /* TODO: result code from the read? */
291
292 if (d->inbuf_head + 512 * to_read <= WDC_INBUF_SIZE) {
293 diskimage_access(cpu->machine, d->drive + d->base_drive,
294 DISKIMAGE_IDE, 0, offset,
295 d->inbuf + d->inbuf_head, 512 * to_read);
296 d->inbuf_head += 512 * to_read;
297 if (d->inbuf_head == WDC_INBUF_SIZE)
298 d->inbuf_head = 0;
299 } else {
300 diskimage_access(cpu->machine, d->drive + d->base_drive,
301 DISKIMAGE_IDE, 0, offset, buf, 512 * to_read);
302 for (i=0; i<512 * to_read; i++)
303 wdc_addtoinbuf(d, buf[i]);
304 }
305
306 offset += 512 * to_read;
307 count -= to_read;
308 }
309
310 d->int_assert = 1;
311 }
312
313
314 /*
315 * wdc__write():
316 */
317 void wdc__write(struct cpu *cpu, struct wdc_data *d)
318 {
319 int cyl = d->cyl_hi * 256+ d->cyl_lo;
320 int count = d->seccnt? d->seccnt : 256;
321 uint64_t offset = 512 * (d->sector - 1
322 + (int64_t)d->head * d->sectors_per_track[d->drive] +
323 (int64_t)d->heads[d->drive] * d->sectors_per_track[d->drive] * cyl);
324 #if 0
325 /* LBA: */
326 if (d->lba)
327 offset = 512 * (((d->head & 0xf) << 24) +
328 (cyl << 8) + d->sector);
329 printf("WDC write to offset %lli\n", (long long)offset);
330 #endif
331
332 d->write_in_progress = d->cur_command;
333 d->write_count = count;
334 d->write_offset = offset;
335
336 /* TODO: result code? */
337 }
338
339
340 /*
341 * status_byte():
342 *
343 * Return a reasonable status byte corresponding to the controller's current
344 * state.
345 */
346 static int status_byte(struct wdc_data *d, struct cpu *cpu)
347 {
348 int odata = 0;
349 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
350 DISKIMAGE_IDE))
351 odata |= WDCS_DRDY | WDCS_DSC;
352 if (d->inbuf_head != d->inbuf_tail)
353 odata |= WDCS_DRQ;
354 if (d->write_in_progress)
355 odata |= WDCS_DRQ;
356 if (d->error)
357 odata |= WDCS_ERR;
358 if (d->atapi_cmd_in_progress && (d->atapi_phase & WDCS_DRQ)) {
359 odata |= WDCS_DRQ;
360 }
361 return odata;
362 }
363
364
365 DEVICE_ACCESS(wdc_altstatus)
366 {
367 struct wdc_data *d = extra;
368 uint64_t idata = 0, odata = 0;
369
370 idata = data[0];
371
372 /* Same as the normal status byte: */
373 odata = status_byte(d, cpu);
374
375 if (writeflag==MEM_READ)
376 debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
377 (int)odata);
378 else {
379 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
380 (int)idata);
381 if (idata & WDCTL_4BIT)
382 d->cur_command = COMMAND_RESET;
383 }
384
385 if (writeflag == MEM_READ)
386 data[0] = odata;
387
388 return 1;
389 }
390
391
392 /*
393 * wdc_command():
394 */
395 void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata)
396 {
397 size_t i;
398
399 d->cur_command = idata;
400 d->atapi_cmd_in_progress = 0;
401 d->error = 0;
402
403 /*
404 * Disk images that do not exist return an ABORT error. This also
405 * happens with CDROM images with the WDCC_IDENTIFY command; CDROM
406 * images must be detected with ATAPI_IDENTIFY_DEVICE instead.
407 *
408 * TODO: Is this correct/good behaviour?
409 */
410 if (!diskimage_exist(cpu->machine, d->drive + d->base_drive,
411 DISKIMAGE_IDE)) {
412 debug("[ wdc: command 0x%02x drive %i, but no disk image ]\n",
413 d->cur_command, d->drive + d->base_drive);
414 d->error |= WDCE_ABRT;
415 d->int_assert = 1;
416 return;
417 }
418 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
419 DISKIMAGE_IDE) && d->cur_command == WDCC_IDENTIFY) {
420 debug("[ wdc: IDENTIFY drive %i, but it is an ATAPI "
421 "drive ]\n", d->drive + d->base_drive);
422 d->error |= WDCE_ABRT;
423 d->int_assert = 1;
424 return;
425 }
426
427 /* Handle the command: */
428 switch (d->cur_command) {
429
430 case WDCC_READ:
431 case WDCC_READMULTI:
432 if (!quiet_mode)
433 debug("[ wdc: READ from drive %i, head %i, cyl %i, "
434 "sector %i, nsecs %i ]\n", d->drive, d->head,
435 d->cyl_hi*256+d->cyl_lo, d->sector, d->seccnt);
436 wdc__read(cpu, d);
437 break;
438
439 case WDCC_WRITE:
440 case WDCC_WRITEMULTI:
441 if (!quiet_mode)
442 debug("[ wdc: WRITE to drive %i, head %i, cyl %i, "
443 "sector %i, nsecs %i ]\n", d->drive, d->head,
444 d->cyl_hi*256+d->cyl_lo, d->sector, d->seccnt);
445 wdc__write(cpu, d);
446 break;
447
448 case WDCC_IDP: /* Initialize drive parameters */
449 debug("[ wdc: IDP drive %i (TODO) ]\n", d->drive);
450 /* TODO */
451 d->int_assert = 1;
452 break;
453
454 case SET_FEATURES:
455 debug("[ wdc: SET_FEATURES drive %i (TODO), feature 0x%02x ]\n",
456 d->drive, d->precomp);
457 /* TODO */
458 switch (d->precomp) {
459 case WDSF_SET_MODE:
460 debug("[ wdc: WDSF_SET_MODE drive %i, pio/dma flags "
461 "0x%02x ]\n", d->drive, d->seccnt);
462 break;
463 default:d->error |= WDCE_ABRT;
464 }
465 /* TODO: always interrupt? */
466 d->int_assert = 1;
467 break;
468
469 case WDCC_RECAL:
470 debug("[ wdc: RECAL drive %i ]\n", d->drive);
471 d->int_assert = 1;
472 break;
473
474 case WDCC_IDENTIFY:
475 case ATAPI_IDENTIFY_DEVICE:
476 debug("[ wdc: %sIDENTIFY drive %i ]\n", d->cur_command ==
477 ATAPI_IDENTIFY_DEVICE? "ATAPI " : "", d->drive);
478 wdc_initialize_identify_struct(cpu, d);
479 /* The IDENTIFY data is sent out in low/high byte order: */
480 for (i=0; i<sizeof(d->identify_struct); i+=2) {
481 wdc_addtoinbuf(d, d->identify_struct[i+1]);
482 wdc_addtoinbuf(d, d->identify_struct[i+0]);
483 }
484 d->int_assert = 1;
485 break;
486
487 case WDCC_IDLE_IMMED:
488 debug("[ wdc: IDLE_IMMED drive %i ]\n", d->drive);
489 /* TODO: interrupt here? */
490 d->int_assert = 1;
491 break;
492
493 case WDCC_SETMULTI:
494 debug("[ wdc: SETMULTI drive %i ]\n", d->drive);
495 /* TODO: interrupt here? */
496 d->int_assert = 1;
497 break;
498
499 case ATAPI_SOFT_RESET:
500 debug("[ wdc: ATAPI_SOFT_RESET drive %i ]\n", d->drive);
501 /* TODO: interrupt here? */
502 d->int_assert = 1;
503 break;
504
505 case ATAPI_PKT_CMD:
506 debug("[ wdc: ATAPI_PKT_CMD drive %i ]\n", d->drive);
507 /* TODO: interrupt here? */
508 /* d->int_assert = 1; */
509 d->atapi_cmd_in_progress = 1;
510 d->atapi_phase = PHASE_CMDOUT;
511 break;
512
513 case WDCC_DIAGNOSE:
514 debug("[ wdc: WDCC_DIAGNOSE drive %i: TODO ]\n", d->drive);
515 /* TODO: interrupt here? */
516 d->int_assert = 1;
517 d->error = 1; /* No error? */
518 break;
519
520 /* Unsupported commands, without warning: */
521 case WDCC_SEC_SET_PASSWORD:
522 case WDCC_SEC_UNLOCK:
523 case WDCC_SEC_ERASE_PREPARE:
524 case WDCC_SEC_ERASE_UNIT:
525 case WDCC_SEC_FREEZE_LOCK:
526 case WDCC_SEC_DISABLE_PASSWORD:
527 d->error |= WDCE_ABRT;
528 break;
529
530 default:/* TODO */
531 d->error |= WDCE_ABRT;
532 fatal("[ wdc: WARNING! Unimplemented command 0x%02x (drive %i,"
533 " head %i, cyl %i, sector %i, nsecs %i) ]\n",
534 d->cur_command, d->drive, d->head, d->cyl_hi*256+d->cyl_lo,
535 d->sector, d->seccnt);
536 }
537 }
538
539
540 DEVICE_ACCESS(wdc)
541 {
542 struct wdc_data *d = extra;
543 uint64_t idata = 0, odata = 0;
544 int i;
545
546 relative_addr /= d->addr_mult;
547
548 if (!d->io_enabled)
549 goto ret;
550
551 if (writeflag == MEM_WRITE) {
552 if (relative_addr == wd_data)
553 idata = memory_readmax64(cpu, data, len);
554 else {
555 if (len != 1)
556 fatal("[ wdc: WARNING! non-8-bit access! ]\n");
557 idata = data[0];
558 }
559 }
560
561 switch (relative_addr) {
562
563 case wd_data: /* 0: data */
564 if (writeflag == MEM_READ) {
565 odata = wdc_get_inbuf(d);
566
567 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
568 if (len >= 2)
569 odata += (wdc_get_inbuf(d) << 8);
570 if (len == 4) {
571 odata += (wdc_get_inbuf(d) << 16);
572 odata += (wdc_get_inbuf(d) << 24);
573 }
574 } else {
575 if (len >= 2)
576 odata = (odata << 8) + wdc_get_inbuf(d);
577 if (len == 4) {
578 odata = (odata << 8) + wdc_get_inbuf(d);
579 odata = (odata << 8) + wdc_get_inbuf(d);
580 }
581 }
582
583 if (d->data_debug) {
584 char *s = "0x%04"PRIx64" ]\n";
585 if (len == 1)
586 s = "0x%02"PRIx64" ]\n";
587 if (len == 4)
588 s = "0x%08"PRIx64" ]\n";
589 if (len == 8)
590 s = "0x%016"PRIx64" ]\n";
591 debug("[ wdc: read from DATA: ");
592 debug(s, (uint64_t) odata);
593 }
594
595 if (d->atapi_cmd_in_progress) {
596 d->atapi_len -= len;
597 d->atapi_received += len;
598 if (d->atapi_len == 0) {
599 if (d->atapi_received < d->atapi_st->
600 data_in_len) {
601 d->atapi_phase = PHASE_DATAIN;
602 d->atapi_len = d->atapi_st->
603 data_in_len -
604 d->atapi_received;
605 if (d->atapi_len > 32768)
606 d->atapi_len = 0;
607 } else
608 d->atapi_phase =
609 PHASE_COMPLETED;
610 d->int_assert = 1;
611 }
612 } else {
613 #if 0
614 if (d->inbuf_tail != d->inbuf_head)
615 #else
616 if (d->inbuf_tail != d->inbuf_head &&
617 ((d->inbuf_tail - d->inbuf_head) % 512)
618 == 0)
619 #endif
620 d->int_assert = 1;
621 }
622 } else {
623 int inbuf_len;
624 if (d->data_debug) {
625 char *s = "0x%04"PRIx64" ]\n";
626 if (len == 1)
627 s = "0x%02"PRIx64" ]\n";
628 if (len == 4)
629 s = "0x%08"PRIx64" ]\n";
630 if (len == 8)
631 s = "0x%016"PRIx64" ]\n";
632 debug("[ wdc: write to DATA: ");
633 debug(s, (uint64_t) idata);
634 }
635 if (!d->write_in_progress &&
636 !d->atapi_cmd_in_progress) {
637 fatal("[ wdc: write to DATA, but not "
638 "expecting any? (len=%i): 0x%08lx ]\n",
639 (int)len, (long)idata);
640 }
641
642 if (cpu->byte_order == EMUL_LITTLE_ENDIAN) {
643 switch (len) {
644 case 4: wdc_addtoinbuf(d, idata & 0xff);
645 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
646 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
647 wdc_addtoinbuf(d, (idata >> 24) & 0xff);
648 break;
649 case 2: wdc_addtoinbuf(d, idata & 0xff);
650 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
651 break;
652 case 1: wdc_addtoinbuf(d, idata); break;
653 default:fatal("wdc: unimplemented write "
654 "len %i\n", len);
655 exit(1);
656 }
657 } else {
658 switch (len) {
659 case 4: wdc_addtoinbuf(d, (idata >> 24) & 0xff);
660 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
661 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
662 wdc_addtoinbuf(d, idata & 0xff);
663 break;
664 case 2: wdc_addtoinbuf(d, (idata >> 8) & 0xff);
665 wdc_addtoinbuf(d, idata & 0xff);
666 break;
667 case 1: wdc_addtoinbuf(d, idata); break;
668 default:fatal("wdc: unimplemented write "
669 "len %i\n", len);
670 exit(1);
671 }
672 }
673
674 inbuf_len = d->inbuf_head - d->inbuf_tail;
675 while (inbuf_len < 0)
676 inbuf_len += WDC_INBUF_SIZE;
677
678 if (d->atapi_cmd_in_progress && inbuf_len == 12) {
679 unsigned char *scsi_cmd = malloc(12);
680 int x = 0, res;
681
682 if (d->atapi_st != NULL)
683 scsi_transfer_free(d->atapi_st);
684 d->atapi_st = scsi_transfer_alloc();
685
686 debug("[ wdc: ATAPI command ]\n");
687
688 while (inbuf_len > 0) {
689 scsi_cmd[x++] = wdc_get_inbuf(d);
690 inbuf_len --;
691 }
692
693 d->atapi_st->cmd = scsi_cmd;
694 d->atapi_st->cmd_len = 12;
695
696 if (scsi_cmd[0] == SCSIBLOCKCMD_READ_CAPACITY
697 || scsi_cmd[0] == SCSICMD_READ_10
698 || scsi_cmd[0] == SCSICMD_MODE_SENSE10)
699 d->atapi_st->cmd_len = 10;
700
701 res = diskimage_scsicommand(cpu,
702 d->drive + d->base_drive, DISKIMAGE_IDE,
703 d->atapi_st);
704
705 if (res == 0) {
706 fatal("WDC: ATAPI scsi error?\n");
707 exit(1);
708 }
709
710 d->atapi_len = 0;
711 d->atapi_received = 0;
712
713 if (res == 1) {
714 if (d->atapi_st->data_in != NULL) {
715 int i;
716 d->atapi_phase = PHASE_DATAIN;
717 d->atapi_len = d->atapi_st->
718 data_in_len;
719 for (i=0; i<d->atapi_len; i++)
720 wdc_addtoinbuf(d,
721 d->atapi_st->
722 data_in[i]);
723 if (d->atapi_len > 32768)
724 d->atapi_len = 32768;
725 } else {
726 d->atapi_phase =
727 PHASE_COMPLETED;
728 }
729 } else {
730 fatal("wdc atapi Dataout? TODO\n");
731 d->atapi_phase = PHASE_DATAOUT;
732 exit(1);
733 }
734
735 d->int_assert = 1;
736 }
737
738 if (( d->write_in_progress == WDCC_WRITEMULTI &&
739 inbuf_len % (512 * d->write_count) == 0)
740 ||
741 ( d->write_in_progress == WDCC_WRITE &&
742 inbuf_len % 512 == 0) ) {
743 int count = (d->write_in_progress ==
744 WDCC_WRITEMULTI)? d->write_count : 1;
745 unsigned char *buf = malloc(512 * count);
746 unsigned char *b = buf;
747
748 if (buf == NULL) {
749 fprintf(stderr, "out of memory\n");
750 exit(1);
751 }
752
753 if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) {
754 b = d->inbuf + d->inbuf_tail;
755 d->inbuf_tail = (d->inbuf_tail + 512
756 * count) % WDC_INBUF_SIZE;
757 } else {
758 for (i=0; i<512 * count; i++)
759 buf[i] = wdc_get_inbuf(d);
760 }
761
762 diskimage_access(cpu->machine,
763 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
764 d->write_offset, b, 512 * count);
765
766 d->write_count -= count;
767 d->write_offset += 512 * count;
768
769 d->int_assert = 1;
770
771 if (d->write_count == 0)
772 d->write_in_progress = 0;
773
774 free(buf);
775 }
776 }
777 break;
778
779 case wd_error: /* 1: error (r), precomp (w) */
780 if (writeflag == MEM_READ) {
781 odata = d->error;
782 debug("[ wdc: read from ERROR: 0x%02x ]\n", (int)odata);
783 /* TODO: is the error value cleared on read? */
784 d->error = 0;
785 } else {
786 d->precomp = idata;
787 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
788 }
789 break;
790
791 case wd_seccnt: /* 2: sector count (or "ireason" for ATAPI) */
792 if (writeflag == MEM_READ) {
793 odata = d->seccnt;
794 if (d->atapi_cmd_in_progress) {
795 odata = d->atapi_phase & (WDCI_CMD | WDCI_IN);
796 }
797 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
798 } else {
799 d->seccnt = idata;
800 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
801 }
802 break;
803
804 case wd_sector: /* 3: first sector */
805 if (writeflag == MEM_READ) {
806 odata = d->sector;
807 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
808 } else {
809 d->sector = idata;
810 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
811 }
812 break;
813
814 case wd_cyl_lo: /* 4: cylinder low */
815 if (writeflag == MEM_READ) {
816 odata = d->cyl_lo;
817 if (d->cur_command == COMMAND_RESET &&
818 diskimage_is_a_cdrom(cpu->machine,
819 d->drive + d->base_drive, DISKIMAGE_IDE))
820 odata = 0x14;
821 if (d->atapi_cmd_in_progress) {
822 int x = d->atapi_len;
823 if (x > 32768)
824 x = 32768;
825 odata = x & 255;
826 }
827 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
828 } else {
829 d->cyl_lo = idata;
830 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
831 }
832 break;
833
834 case wd_cyl_hi: /* 5: cylinder high */
835 if (writeflag == MEM_READ) {
836 odata = d->cyl_hi;
837 if (d->cur_command == COMMAND_RESET &&
838 diskimage_is_a_cdrom(cpu->machine,
839 d->drive + d->base_drive, DISKIMAGE_IDE))
840 odata = 0xeb;
841 if (d->atapi_cmd_in_progress) {
842 int x = d->atapi_len;
843 if (x > 32768)
844 x = 32768;
845 odata = (x >> 8) & 255;
846 }
847 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
848 } else {
849 d->cyl_hi = idata;
850 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
851 }
852 break;
853
854 case wd_sdh: /* 6: sectorsize/drive/head */
855 if (writeflag==MEM_READ) {
856 odata = (d->sectorsize << 6) + (d->lba << 5) +
857 (d->drive << 4) + (d->head);
858 debug("[ wdc: read from SDH: 0x%02x (sectorsize %i,"
859 " lba=%i, drive %i, head %i) ]\n", (int)odata,
860 d->sectorsize, d->lba, d->drive, d->head);
861 } else {
862 d->sectorsize = (idata >> 6) & 3;
863 d->lba = (idata >> 5) & 1;
864 d->drive = (idata >> 4) & 1;
865 d->head = idata & 0xf;
866 debug("[ wdc: write to SDH: 0x%02x (sectorsize %i,"
867 " lba=%i, drive %i, head %i) ]\n", (int)idata,
868 d->sectorsize, d->lba, d->drive, d->head);
869 }
870 break;
871
872 case wd_command: /* 7: command or status */
873 if (writeflag==MEM_READ) {
874 odata = status_byte(d, cpu);
875 if (!quiet_mode)
876 debug("[ wdc: read from STATUS: 0x%02x ]\n",
877 (int)odata);
878 INTERRUPT_DEASSERT(d->irq);
879 d->int_assert = 0;
880 } else {
881 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
882 wdc_command(cpu, d, idata);
883 }
884 break;
885
886 default:
887 if (writeflag==MEM_READ)
888 debug("[ wdc: read from 0x%02x ]\n",
889 (int)relative_addr);
890 else
891 debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
892 (int)relative_addr, (int)idata);
893 }
894
895 /* Assert interrupt, if necessary: */
896 dev_wdc_tick(cpu, extra);
897
898 ret:
899 if (writeflag == MEM_READ) {
900 if (relative_addr == wd_data)
901 memory_writemax64(cpu, data, len, odata);
902 else
903 data[0] = odata;
904 }
905
906 return 1;
907 }
908
909
910 DEVINIT(wdc)
911 {
912 struct wdc_data *d;
913 uint64_t alt_status_addr;
914 int i, tick_shift = WDC_TICK_SHIFT;
915
916 d = malloc(sizeof(struct wdc_data));
917 if (d == NULL) {
918 fprintf(stderr, "out of memory\n");
919 exit(1);
920 }
921 memset(d, 0, sizeof(struct wdc_data));
922
923 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
924 d->addr_mult = devinit->addr_mult;
925 d->data_debug = 1;
926 d->io_enabled = 1;
927
928 d->inbuf = zeroed_alloc(WDC_INBUF_SIZE);
929
930 /* base_drive = 0 for the primary controller, 2 for the secondary. */
931 d->base_drive = 0;
932 if ((devinit->addr & 0xfff) == 0x170)
933 d->base_drive = 2;
934
935 alt_status_addr = devinit->addr + 0x206;
936
937 /* Special hacks for individual machines: */
938 switch (devinit->machine->machine_type) {
939 case MACHINE_MACPPC:
940 alt_status_addr = devinit->addr + 0x160;
941 break;
942 case MACHINE_HPCMIPS:
943 /* TODO: Fix */
944 if (devinit->addr == 0x14000180)
945 alt_status_addr = 0x14000386;
946 break;
947 case MACHINE_IQ80321:
948 alt_status_addr = devinit->addr + 0x402;
949 break;
950 }
951
952 /* Get disk geometries: */
953 for (i=0; i<2; i++)
954 if (diskimage_exist(devinit->machine, d->base_drive +i,
955 DISKIMAGE_IDE))
956 diskimage_getchs(devinit->machine, d->base_drive + i,
957 DISKIMAGE_IDE, &d->cyls[i], &d->heads[i],
958 &d->sectors_per_track[i]);
959
960 memory_device_register(devinit->machine->memory, "wdc_altstatus",
961 alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL);
962 memory_device_register(devinit->machine->memory, devinit->name,
963 devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access,
964 d, DM_DEFAULT, NULL);
965
966 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
967 d, tick_shift, 0.0);
968
969 devinit->return_ptr = d;
970
971 return 1;
972 }
973

  ViewVC Help
Powered by ViewVC 1.1.26