/[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 20 - (show annotations)
Mon Oct 8 16:19:23 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 25544 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1055 2005/11/25 22:48:36 debug Exp $
20051031	Adding disassembly support for more ARM instructions (clz,
		smul* etc), and adding a hack to support "new tiny" pages
		for StrongARM.
20051101	Minor documentation updates (NetBSD 2.0.2 -> 2.1, and OpenBSD
		3.7 -> 3.8, and lots of testing).
		Changing from 1-sector PIO mode 0 transfers to 128-sector PIO
		mode 3 (in dev_wdc).
		Various minor ARM dyntrans updates (pc-relative loads from
		within the same page as the instruction are now treated as
		constant "mov").
20051102	Re-enabling instruction combinations (they were accidentally
		disabled).
		Dyntrans TLB entries are now overwritten using a round-robin
		scheme instead of randomly. This increases performance.
		Fixing a typo in file.c (thanks to Chuan-Hua Chang for
		noticing it).
		Experimenting with adding ATAPI support to dev_wdc (to make
		emulated *BSD detect cdroms as cdroms, not harddisks).
20051104	Various minor updates.
20051105	Continuing on the ATAPI emulation. Seems to work well enough
		for a NetBSD/cats installation, but not OpenBSD/cats.
		Various other updates.
20051106	Modifying the -Y command line option to allow scaleup with
		certain graphic controllers (only dev_vga so far), not just
		scaledown.
		Some minor dyntrans cleanups.
20051107	Beginning a cleanup up the PCI subsystem (removing the
		read_register hack, etc).
20051108	Continuing the cleanup; splitting up some pci devices into a
		normal autodev device and some separate pci glue code.
20051109	Continuing on the PCI bus stuff; all old pci_*.c have been
		incorporated into normal devices and/or rewritten as glue code
		only, adding a dummy Intel 82371AB PIIX4 for Malta (not really
		tested yet).
		Minor pckbc fix so that Linux doesn't complain.
		Working on the DEC 21143 NIC (ethernet mac rom stuff mostly).
		Various other minor fixes.
20051110	Some more ARM dyntrans fine-tuning (e.g. some instruction
		combinations (cmps followed by conditional branch within the
		same page) and special cases for DPIs with regform when the
		shifter isn't used).
20051111	ARM dyntrans updates: O(n)->O(1) for just-mark-as-non-
		writable in the generic pc_to_pointers function, and some other
		minor hacks.
		Merging Cobalt and evbmips (Malta) ISA interrupt handling,
		and some minor fixes to allow Linux to accept harddisk irqs.
20051112	Minor device updates (pckbc, dec21143, lpt, ...), most
		importantly fixing the ALI M1543/M5229 so that harddisk irqs
		work with Linux/CATS.
20051113	Some more generalizations of the PCI subsystem.
		Finally took the time to add a hack for SCSI CDROM TOCs; this
		enables OpenBSD to use partition 'a' (as needed by the OpenBSD
		installer), and Windows NT's installer to get a bit further.
		Also fixing dev_wdc to allow Linux to detect ATAPI CDROMs.
		Continuing on the DEC 21143.
20051114	Minor ARM dyntrans tweaks; ARM cmps+branch optimization when
		comparing with 0, and generalizing the xchg instr. comb.
		Adding disassembly of ARM mrrc/mcrr and q{,d}{add,sub}.
20051115	Continuing on various PPC things (BATs, other address trans-
		lation things, various loads/stores, BeBox emulation, etc.).
		Beginning to work on PPC interrupt/exception support.
20051116	Factoring out some code which initializes legacy ISA devices
		from those machines that use them (bus_isa).
		Continuing on PPC interrupt/exception support.
20051117	Minor Malta fixes: RTC year offset = 80, disabling a speed hack
		which caused NetBSD to detect a too fast cpu, and adding a new
		hack to make Linux detect a faster cpu.
		Continuing on the Artesyn PM/PPC emulation mode.
		Adding an Algor emulation skeleton (P4032 and P5064);
		implementing some of the basics.
		Continuing on PPC emulation in general; usage of unimplemented
		SPRs is now easier to track, continuing on memory/exception
		related issues, etc.
20051118	More work on PPC emulation (tgpr0..3, exception handling,
		memory stuff, syscalls, etc.).
20051119	Changing the ARM dyntrans code to mostly use cpu->pc, and not
		necessarily use arm reg 15. Seems to work.
		Various PPC updates; continuing on the PReP emulation mode.
20051120	Adding a workaround/hack to dev_mc146818 to allow NetBSD/prep
		to detect the clock.
20051121	More cleanup of the PCI bus (memory and I/O bases, etc).
		Continuing on various PPC things (decrementer and timebase,
		WDCs on obio (on PReP) use irq 13, not 14/15).
20051122	Continuing on the CPC700 controller (interrupts etc) for PMPPC,
		and on PPC stuff in general.
		Finally! After some bug fixes to the virtual to physical addr
		translation, NetBSD/{prep,pmppc} 2.1 reach userland and are
		stable enough to be interacted with.
		More PCI updates; reverse-endian device access for PowerPC etc.
20051123	Generalizing the IEEE floating point subsystem (moving it out
		from src/cpus/cpu_mips_coproc.c into a new src/float_emul.c).
		Input via slave xterms was sometimes not really working; fixing
		this for ns16550, and a warning message is now displayed if
		multiple non-xterm consoles are active.
		Adding some PPC floating point support, etc.
		Various interrupt related updates (dev_wdc, _ns16550, _8259,
		and the isa32 common code in machine.c).
		NetBSD/prep can now be installed! :-) (Well, with some manual
		commands necessary before running sysinst.) Updating the
		documentation and various other things to reflect this.
20051124	Various minor documentation updates.
		Continuing the work on the DEC 21143 NIC.
20051125	LOTS of work on the 21143. Both OpenBSD and NetBSD work fine
		with it now, except that OpenBSD sometimes gives a time-out
		warning.
		Minor documentation updates.

==============  RELEASE 0.3.7  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26