/[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 22 - (show 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 /*
2 * Copyright (C) 2004-2006 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.63 2006/02/18 13:15:21 debug Exp $
29 *
30 * Standard "wdc" IDE controller.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cpu.h"
38 #include "device.h"
39 #include "diskimage.h"
40 #include "machine.h"
41 #include "memory.h"
42 #include "misc.h"
43
44 #include "wdcreg.h"
45
46 #define DEV_WDC_LENGTH 8
47 #define WDC_TICK_SHIFT 14
48 #define WDC_MAX_SECTORS 512
49 #define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1))
50
51 /*
52 * INT_DELAY: This is an old hack which only exists because (some versions of)
53 * NetBSD for hpcmips have interrupt problems. These problems are probably not
54 * specific to GXemul, but are also triggered on real hardware.
55 *
56 * See the following URL for more info:
57 * http://mail-index.netbsd.org/port-hpcmips/2004/12/30/0003.html
58 *
59 * NetBSD/malta also bugs out if wdc interrupts come too quickly. Hm.
60 */
61 #define INT_DELAY 1
62
63 extern int quiet_mode;
64
65 /* #define debug fatal */
66
67 struct wdc_data {
68 int irq_nr;
69 int addr_mult;
70 int base_drive;
71 int data_debug;
72
73 /* Cached values: */
74 int cyls[2];
75 int heads[2];
76 int sectors_per_track[2];
77
78 unsigned char *inbuf;
79 int inbuf_head;
80 int inbuf_tail;
81
82 int delayed_interrupt;
83 int int_asserted;
84
85 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
101 int atapi_cmd_in_progress;
102 int atapi_phase;
103 struct scsi_transfer *atapi_st;
104 int atapi_len;
105 size_t atapi_received;
106
107 unsigned char identify_struct[512];
108 };
109
110
111 #define COMMAND_RESET 0x100
112
113
114 /*
115 * dev_wdc_tick():
116 */
117 void dev_wdc_tick(struct cpu *cpu, void *extra)
118 {
119 struct wdc_data *d = extra;
120 int old_di = d->delayed_interrupt;
121
122 if (d->delayed_interrupt)
123 d->delayed_interrupt --;
124
125 if (old_di == 1 || d->int_asserted) {
126 cpu_interrupt(cpu, d->irq_nr);
127 d->int_asserted = 1;
128 }
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 fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!"
144 " Increase WDC_MAX_SECTORS. ]\n");
145 }
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 fatal("[ wdc: WARNING! someone is reading too much from the "
159 "wdc inbuf! ]\n");
160 return -1;
161 }
162
163 d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
164 return c;
165 }
166
167
168 /*
169 * wdc_initialize_identify_struct():
170 */
171 static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
172 {
173 uint64_t total_size;
174 int flags, cdrom = 0;
175 char namebuf[40];
176
177 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive,
178 DISKIMAGE_IDE);
179 if (diskimage_is_a_cdrom(cpu->machine, d->drive + d->base_drive,
180 DISKIMAGE_IDE))
181 cdrom = 1;
182
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 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
194 /* 1: nr of cylinders */
195 d->identify_struct[2 * 1 + 0] = d->cyls[d->drive] >> 8;
196 d->identify_struct[2 * 1 + 1] = d->cyls[d->drive];
197
198 /* 3: nr of heads */
199 d->identify_struct[2 * 3 + 0] = d->heads[d->drive] >> 8;
200 d->identify_struct[2 * 3 + 1] = d->heads[d->drive];
201
202 /* 6: sectors per track */
203 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
206 /* 10-19: Serial number */
207 memcpy(&d->identify_struct[2 * 10], "#0 ", 20);
208
209 /* 23-26: Firmware version */
210 memcpy(&d->identify_struct[2 * 23], "1.0 ", 8);
211
212 /* 27-46: Model number */
213 if (diskimage_getname(cpu->machine, d->drive + d->base_drive,
214 DISKIMAGE_IDE, namebuf, sizeof(namebuf))) {
215 size_t i;
216 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
227 /* 47: max sectors per multitransfer */
228 d->identify_struct[2 * 47 + 0] = 0x80;
229 d->identify_struct[2 * 47 + 1] = 128;
230
231 /* 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 /* 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 /* 60-61: total nr of addressable sectors */
251 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 /* 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 }
266
267
268 /*
269 * wdc__read():
270 */
271 void wdc__read(struct cpu *cpu, struct wdc_data *d)
272 {
273 #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 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 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 }
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 d->write_in_progress = d->cur_command;
337 d->write_count = count;
338 d->write_offset = offset;
339
340 /* TODO: result code? */
341 }
342
343
344 /*
345 * status_byte():
346 *
347 * Return a reasonable status byte corresponding to the controller's current
348 * state.
349 */
350 static int status_byte(struct wdc_data *d, struct cpu *cpu)
351 {
352 int odata = 0;
353 if (diskimage_exist(cpu->machine, d->drive + d->base_drive,
354 DISKIMAGE_IDE))
355 odata |= WDCS_DRDY | WDCS_DSC;
356 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 if (d->atapi_cmd_in_progress && (d->atapi_phase & WDCS_DRQ)) {
363 odata |= WDCS_DRQ;
364 }
365 return odata;
366 }
367
368
369 /*
370 * dev_wdc_altstatus_access():
371 */
372 DEVICE_ACCESS(wdc_altstatus)
373 {
374 struct wdc_data *d = extra;
375 uint64_t idata = 0, odata = 0;
376
377 idata = data[0];
378
379 /* Same as the normal status byte: */
380 odata = status_byte(d, cpu);
381
382 if (writeflag==MEM_READ)
383 debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
384 (int)odata);
385 else {
386 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
387 (int)idata);
388 if (idata & WDCTL_4BIT)
389 d->cur_command = COMMAND_RESET;
390 }
391
392 if (writeflag == MEM_READ)
393 data[0] = odata;
394
395 return 1;
396 }
397
398
399 /*
400 * wdc_command():
401 */
402 void wdc_command(struct cpu *cpu, struct wdc_data *d, int idata)
403 {
404 size_t i;
405
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 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 /* 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 * dev_wdc_access():
549 */
550 DEVICE_ACCESS(wdc)
551 {
552 struct wdc_data *d = extra;
553 uint64_t idata = 0, odata = 0;
554 int i;
555
556 relative_addr /= d->addr_mult;
557
558 if (writeflag == MEM_WRITE) {
559 if (relative_addr == wd_data)
560 idata = memory_readmax64(cpu, data, len);
561 else {
562 if (len != 1)
563 fatal("[ wdc: WARNING! non-8-bit access! ]\n");
564 idata = data[0];
565 }
566 }
567
568 switch (relative_addr) {
569
570 case wd_data: /* 0: data */
571 if (writeflag == MEM_READ) {
572 odata = wdc_get_inbuf(d);
573
574 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 }
589
590 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 #if 0
621 if (d->inbuf_tail != d->inbuf_head)
622 #else
623 if (d->inbuf_tail != d->inbuf_head &&
624 ((d->inbuf_tail - d->inbuf_head) % 512)
625 == 0)
626 #endif
627 d->delayed_interrupt = INT_DELAY;
628 }
629 } else {
630 int inbuf_len;
631 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 fatal("[ wdc: write to DATA, but not "
645 "expecting any? (len=%i): 0x%08lx ]\n",
646 (int)len, (long)idata);
647 }
648
649 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 }
680
681 inbuf_len = d->inbuf_head - d->inbuf_tail;
682 while (inbuf_len < 0)
683 inbuf_len += WDC_INBUF_SIZE;
684
685 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 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 }
763
764 diskimage_access(cpu->machine,
765 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
766 d->write_offset, b, 512 * count);
767
768 d->write_count -= count;
769 d->write_offset += 512 * count;
770
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 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 cpu_interrupt_ack(cpu, d->irq_nr);
879 d->int_asserted = 0;
880 d->delayed_interrupt = 0;
881 } else {
882 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
883 wdc_command(cpu, d, idata);
884 }
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 if (cpu->machine->machine_type != MACHINE_HPCMIPS &&
897 cpu->machine->machine_type != MACHINE_EVBMIPS &&
898 cpu->machine->machine_type != MACHINE_BEBOX)
899 dev_wdc_tick(cpu, extra);
900
901 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 return 1;
909 }
910
911
912 DEVINIT(wdc)
913 {
914 struct wdc_data *d;
915 uint64_t alt_status_addr;
916 int i, tick_shift = WDC_TICK_SHIFT;
917
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 d->irq_nr = devinit->irq_nr;
925 d->addr_mult = devinit->addr_mult;
926 d->data_debug = 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 if (devinit->machine->machine_type != MACHINE_HPCMIPS &&
967 devinit->machine->machine_type != MACHINE_EVBMIPS)
968 tick_shift += 1;
969
970 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
971 d, tick_shift);
972
973 return 1;
974 }
975

  ViewVC Help
Powered by ViewVC 1.1.26