/[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 42 - (show annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 25163 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1613 2007/06/15 20:11:26 debug Exp $
20070501	Continuing a little on m88k disassembly (control registers,
		more instructions).
		Adding a dummy mvme88k machine mode.
20070502	Re-adding MIPS load/store alignment exceptions.
20070503	Implementing more of the M88K disassembly code.
20070504	Adding disassembly of some more M88K load/store instructions.
		Implementing some relatively simple M88K instructions (br.n,
		xor[.u] imm, and[.u] imm).
20070505	Implementing M88K three-register and, or, xor, and jmp[.n],
		bsr[.n] including function call trace stuff.
		Applying a patch from Bruce M. Simpson which implements the
		SYSCON_BOARD_CPU_CLOCK_FREQ_ID object of the syscon call in
		the yamon PROM emulation.
20070506	Implementing M88K bb0[.n] and bb1[.n], and skeletons for
		ldcr and stcr (although no control regs are implemented yet).
20070509	Found and fixed the bug which caused Linux for QEMU_MIPS to
		stop working in 0.4.5.1: It was a faulty change to the MIPS
		'sc' and 'scd' instructions I made while going through gcc -W
		warnings on 20070428.
20070510	Updating the Linux/QEMU_MIPS section in guestoses.html to
		use mips-test-0.2.tar.gz instead of 0.1.
		A big thank you to Miod Vallat for sending me M88K manuals.
		Implementing more M88K instructions (addu, subu, div[u], mulu,
		ext[u], clr, set, cmp).
20070511	Fixing bugs in the M88K "and" and "and.u" instructions (found
		by comparing against the manual).
		Implementing more M88K instructions (mask[.u], mak, bcnd (auto-
		generated)) and some more control register details.
		Cleanup: Removing the experimental AVR emulation mode and
		corresponding devices; AVR emulation wasn't really meaningful.
		Implementing autogeneration of most M88K loads/stores. The
		rectangle drawing demo (with -O0) for M88K runs :-)
		Beginning on M88K exception handling.
		More M88K instructions: tb0, tb1, rte, sub, jsr[.n].
		Adding some skeleton MVME PROM ("BUG") emulation.
20070512	Fixing a bug in the M88K cmp instruction.
		Adding the M88K lda (scaled register) instruction.
		Fixing bugs in 64-bit (32-bit pairs) M88K loads/stores.
		Removing the unused tick_hz stuff from the machine struct.
		Implementing the M88K xmem instruction. OpenBSD/mvme88k gets
		far enough to display the Copyright banner :-)
		Implementing subu.co (guess), addu.co, addu.ci, ff0, and ff1.
		Adding a dev_mvme187, for MVME187-specific devices/registers.
		OpenBSD/mvme88k prints more boot messages. :)
20070515	Continuing on MVME187 emulation (adding more devices, beginning
		on the CMMUs, etc).
		Adding the M88K and.c, xor.c, and or.c instructions, and making
		sure that mul, div, etc cause exceptions if executed when SFD1
		is disabled.
20070517	Continuing on M88K and MVME187 emulation in general; moving
		the CMMU registers to the CPU struct, separating dev_pcc2 from
		dev_mvme187, and beginning on memory_m88k.c (BATC and PATC).
		Fixing a bug in 64-bit (32-bit pairs) M88K fast stores.
		Implementing the clock part of dev_mk48txx.
		Implementing the M88K fstcr and xcr instructions.
		Implementing m88k_cpu_tlbdump().
		Beginning on the implementation of a separate address space
		for M88K .usr loads/stores.
20070520	Removing the non-working (skeleton) Sandpoint, SonyNEWS, SHARK
		Dnard, and Zaurus machine modes.
		Experimenting with dyntrans to_be_translated read-ahead. It
		seems to give a very small performance increase for MIPS
		emulation, but a large performance degradation for SuperH. Hm.
20070522	Disabling correct SuperH ITLB emulation; it does not seem to be
		necessary in order to let SH4 guest OSes run, and it slows down
		userspace code.
		Implementing "samepage" branches for SuperH emulation, and some
		other minor speed hacks.
20070525	Continuing on M88K memory-related stuff: exceptions, memory
		transaction register contents, etc.
		Implementing the M88K subu.ci instruction.
		Removing the non-working (skeleton) Iyonix machine mode.
		OpenBSD/mvme88k reaches userland :-), starts executing
		/sbin/init's instructions, and issues a few syscalls, before
		crashing.
20070526	Fixing bugs in dev_mk48txx, so that OpenBSD/mvme88k detects
		the correct time-of-day.
		Implementing a generic IRQ controller for the test machines
		(dev_irqc), similar to a proposed patch from Petr Stepan.
		Experimenting some more with translation read-ahead.
		Adding an "expect" script for automated OpenBSD/landisk
		install regression/performance tests.
20070527	Adding a dummy mmEye (SH3) machine mode skeleton.
		FINALLY found the strange M88K bug I have been hunting: I had
		not emulated the SNIP value for exceptions occurring in
		branch delay slots correctly.
		Implementing correct exceptions for 64-bit M88K loads/stores.
		Address to symbol lookups are now disabled when M88K is
		running in usermode (because usermode addresses don't have
		anything to do with supervisor addresses).
20070531	Removing the mmEye machine mode skeleton.
20070604	Some minor code cleanup.
20070605	Moving src/useremul.c into a subdir (src/useremul/), and
		cleaning up some more legacy constructs.
		Adding -Wstrict-aliasing and -fstrict-aliasing detection to
		the configure script.
20070606	Adding a check for broken GCC on Solaris to the configure
		script. (GCC 3.4.3 on Solaris cannot handle static variables
		which are initialized to 0 or NULL. :-/)
		Removing the old (non-working) ARC emulation modes: NEC RD94,
		R94, R96, and R98, and the last traces of Olivetti M700 and
		Deskstation Tyne.
		Removing the non-working skeleton WDSC device (dev_wdsc).
20070607	Thinking about how to use the host's cc + ld at runtime to
		generate native code. (See experiments/native_cc_ld_test.i
		for an example.)
20070608	Adding a program counter sampling timer, which could be useful
		for native code generation experiments.
		The KN02_CSR_NRMMOD bit in the DECstation 5000/200 (KN02) CSR
		should always be set, to allow a 5000/200 PROM to boot.
20070609	Moving out breakpoint details from the machine struct into
		a helper struct, and removing the limit on max nr of
		breakpoints.
20070610	Moving out tick functions into a helper struct as well (which
		also gets rid of the max limit).
20070612	FINALLY figured out why Debian/DECstation stopped working when
		translation read-ahead was enabled: in src/memory_rw.c, the
		call to invalidate_code_translation was made also if the
		memory access was an instruction load (if the page was mapped
		as writable); it shouldn't be called in that case.
20070613	Implementing some more MIPS32/64 revision 2 instructions: di,
		ei, ext, dext, dextm, dextu, and ins.
20070614	Implementing an instruction combination for the NetBSD/arm
		idle loop (making the host not use any cpu if NetBSD/arm
		inside the emulator is not using any cpu).
		Increasing the nr of ARM VPH entries from 128 to 384.
20070615	Removing the ENABLE_arch stuff from the configure script, so
		that all included architectures are included in both release
		and development builds.
		Moving memory related helper functions from misc.c to memory.c.
		Adding preliminary instructions for netbooting NetBSD/pmppc to
		guestoses.html; it doesn't work yet, there are weird timeouts.
		Beginning a total rewrite of the userland emulation modes
		(removing all emulation modes, beginning from scratch with
		NetBSD/MIPS and FreeBSD/Alpha only).
20070616	After fixing a bug in the DEC21143 NIC (the TDSTAT_OWN bit was
		only cleared for the last segment when transmitting, not all
		segments), NetBSD/pmppc boots with root-on-nfs without the
		timeouts. Updating guestoses.html.
		Removing the skeleton PSP (Playstation Portable) mode.
		Moving X11-related stuff in the machine struct into a helper
		struct.
		Cleanup of out-of-memory checks, to use a new CHECK_ALLOCATION
		macro (which prints a meaningful error message).
		Adding a COMMENT to each machine and device (for automagic
		.index comment generation).
		Doing regression testing for the next release.

==============  RELEASE 0.4.6  ==============


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.76 2007/06/15 19:57:34 debug Exp $
29 *
30 * COMMENT: 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;
680 int x = 0, res;
681
682 CHECK_ALLOCATION(scsi_cmd = malloc(12));
683
684 if (d->atapi_st != NULL)
685 scsi_transfer_free(d->atapi_st);
686 d->atapi_st = scsi_transfer_alloc();
687
688 debug("[ wdc: ATAPI command ]\n");
689
690 while (inbuf_len > 0) {
691 scsi_cmd[x++] = wdc_get_inbuf(d);
692 inbuf_len --;
693 }
694
695 d->atapi_st->cmd = scsi_cmd;
696 d->atapi_st->cmd_len = 12;
697
698 if (scsi_cmd[0] == SCSIBLOCKCMD_READ_CAPACITY
699 || scsi_cmd[0] == SCSICMD_READ_10
700 || scsi_cmd[0] == SCSICMD_MODE_SENSE10)
701 d->atapi_st->cmd_len = 10;
702
703 res = diskimage_scsicommand(cpu,
704 d->drive + d->base_drive, DISKIMAGE_IDE,
705 d->atapi_st);
706
707 if (res == 0) {
708 fatal("WDC: ATAPI scsi error?\n");
709 exit(1);
710 }
711
712 d->atapi_len = 0;
713 d->atapi_received = 0;
714
715 if (res == 1) {
716 if (d->atapi_st->data_in != NULL) {
717 int i;
718 d->atapi_phase = PHASE_DATAIN;
719 d->atapi_len = d->atapi_st->
720 data_in_len;
721 for (i=0; i<d->atapi_len; i++)
722 wdc_addtoinbuf(d,
723 d->atapi_st->
724 data_in[i]);
725 if (d->atapi_len > 32768)
726 d->atapi_len = 32768;
727 } else {
728 d->atapi_phase =
729 PHASE_COMPLETED;
730 }
731 } else {
732 fatal("wdc atapi Dataout? TODO\n");
733 d->atapi_phase = PHASE_DATAOUT;
734 exit(1);
735 }
736
737 d->int_assert = 1;
738 }
739
740 if (( d->write_in_progress == WDCC_WRITEMULTI &&
741 inbuf_len % (512 * d->write_count) == 0)
742 ||
743 ( d->write_in_progress == WDCC_WRITE &&
744 inbuf_len % 512 == 0) ) {
745 int count = (d->write_in_progress ==
746 WDCC_WRITEMULTI)? d->write_count : 1;
747 unsigned char *buf, *b;
748
749 CHECK_ALLOCATION(buf = malloc(512 * count));
750 b = buf;
751
752 if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) {
753 b = d->inbuf + d->inbuf_tail;
754 d->inbuf_tail = (d->inbuf_tail + 512
755 * count) % WDC_INBUF_SIZE;
756 } else {
757 for (i=0; i<512 * count; i++)
758 buf[i] = wdc_get_inbuf(d);
759 }
760
761 diskimage_access(cpu->machine,
762 d->drive + d->base_drive, DISKIMAGE_IDE, 1,
763 d->write_offset, b, 512 * count);
764
765 d->write_count -= count;
766 d->write_offset += 512 * count;
767
768 d->int_assert = 1;
769
770 if (d->write_count == 0)
771 d->write_in_progress = 0;
772
773 free(buf);
774 }
775 }
776 break;
777
778 case wd_error: /* 1: error (r), precomp (w) */
779 if (writeflag == MEM_READ) {
780 odata = d->error;
781 debug("[ wdc: read from ERROR: 0x%02x ]\n", (int)odata);
782 /* TODO: is the error value cleared on read? */
783 d->error = 0;
784 } else {
785 d->precomp = idata;
786 debug("[ wdc: write to PRECOMP: 0x%02x ]\n",(int)idata);
787 }
788 break;
789
790 case wd_seccnt: /* 2: sector count (or "ireason" for ATAPI) */
791 if (writeflag == MEM_READ) {
792 odata = d->seccnt;
793 if (d->atapi_cmd_in_progress) {
794 odata = d->atapi_phase & (WDCI_CMD | WDCI_IN);
795 }
796 debug("[ wdc: read from SECCNT: 0x%02x ]\n",(int)odata);
797 } else {
798 d->seccnt = idata;
799 debug("[ wdc: write to SECCNT: 0x%02x ]\n", (int)idata);
800 }
801 break;
802
803 case wd_sector: /* 3: first sector */
804 if (writeflag == MEM_READ) {
805 odata = d->sector;
806 debug("[ wdc: read from SECTOR: 0x%02x ]\n",(int)odata);
807 } else {
808 d->sector = idata;
809 debug("[ wdc: write to SECTOR: 0x%02x ]\n", (int)idata);
810 }
811 break;
812
813 case wd_cyl_lo: /* 4: cylinder low */
814 if (writeflag == MEM_READ) {
815 odata = d->cyl_lo;
816 if (d->cur_command == COMMAND_RESET &&
817 diskimage_is_a_cdrom(cpu->machine,
818 d->drive + d->base_drive, DISKIMAGE_IDE))
819 odata = 0x14;
820 if (d->atapi_cmd_in_progress) {
821 int x = d->atapi_len;
822 if (x > 32768)
823 x = 32768;
824 odata = x & 255;
825 }
826 debug("[ wdc: read from CYL_LO: 0x%02x ]\n",(int)odata);
827 } else {
828 d->cyl_lo = idata;
829 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", (int)idata);
830 }
831 break;
832
833 case wd_cyl_hi: /* 5: cylinder high */
834 if (writeflag == MEM_READ) {
835 odata = d->cyl_hi;
836 if (d->cur_command == COMMAND_RESET &&
837 diskimage_is_a_cdrom(cpu->machine,
838 d->drive + d->base_drive, DISKIMAGE_IDE))
839 odata = 0xeb;
840 if (d->atapi_cmd_in_progress) {
841 int x = d->atapi_len;
842 if (x > 32768)
843 x = 32768;
844 odata = (x >> 8) & 255;
845 }
846 debug("[ wdc: read from CYL_HI: 0x%02x ]\n",(int)odata);
847 } else {
848 d->cyl_hi = idata;
849 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", (int)idata);
850 }
851 break;
852
853 case wd_sdh: /* 6: sectorsize/drive/head */
854 if (writeflag==MEM_READ) {
855 odata = (d->sectorsize << 6) + (d->lba << 5) +
856 (d->drive << 4) + (d->head);
857 debug("[ wdc: read from SDH: 0x%02x (sectorsize %i,"
858 " lba=%i, drive %i, head %i) ]\n", (int)odata,
859 d->sectorsize, d->lba, d->drive, d->head);
860 } else {
861 d->sectorsize = (idata >> 6) & 3;
862 d->lba = (idata >> 5) & 1;
863 d->drive = (idata >> 4) & 1;
864 d->head = idata & 0xf;
865 debug("[ wdc: write to SDH: 0x%02x (sectorsize %i,"
866 " lba=%i, drive %i, head %i) ]\n", (int)idata,
867 d->sectorsize, d->lba, d->drive, d->head);
868 }
869 break;
870
871 case wd_command: /* 7: command or status */
872 if (writeflag==MEM_READ) {
873 odata = status_byte(d, cpu);
874 if (!quiet_mode)
875 debug("[ wdc: read from STATUS: 0x%02x ]\n",
876 (int)odata);
877 INTERRUPT_DEASSERT(d->irq);
878 d->int_assert = 0;
879 } else {
880 debug("[ wdc: write to COMMAND: 0x%02x ]\n",(int)idata);
881 wdc_command(cpu, d, idata);
882 }
883 break;
884
885 default:
886 if (writeflag==MEM_READ)
887 debug("[ wdc: read from 0x%02x ]\n",
888 (int)relative_addr);
889 else
890 debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
891 (int)relative_addr, (int)idata);
892 }
893
894 /* Assert interrupt, if necessary: */
895 dev_wdc_tick(cpu, extra);
896
897 ret:
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 DEVINIT(wdc)
910 {
911 struct wdc_data *d;
912 uint64_t alt_status_addr;
913 int i, tick_shift = WDC_TICK_SHIFT;
914
915 CHECK_ALLOCATION(d = malloc(sizeof(struct wdc_data)));
916 memset(d, 0, sizeof(struct wdc_data));
917
918 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
919 d->addr_mult = devinit->addr_mult;
920 d->data_debug = 1;
921 d->io_enabled = 1;
922
923 d->inbuf = zeroed_alloc(WDC_INBUF_SIZE);
924
925 /* base_drive = 0 for the primary controller, 2 for the secondary. */
926 d->base_drive = 0;
927 if ((devinit->addr & 0xfff) == 0x170)
928 d->base_drive = 2;
929
930 alt_status_addr = devinit->addr + 0x206;
931
932 /* Special hacks for individual machines: */
933 switch (devinit->machine->machine_type) {
934 case MACHINE_MACPPC:
935 alt_status_addr = devinit->addr + 0x160;
936 break;
937 case MACHINE_HPCMIPS:
938 /* TODO: Fix */
939 if (devinit->addr == 0x14000180)
940 alt_status_addr = 0x14000386;
941 break;
942 case MACHINE_IQ80321:
943 alt_status_addr = devinit->addr + 0x402;
944 break;
945 }
946
947 /* Get disk geometries: */
948 for (i=0; i<2; i++)
949 if (diskimage_exist(devinit->machine, d->base_drive +i,
950 DISKIMAGE_IDE))
951 diskimage_getchs(devinit->machine, d->base_drive + i,
952 DISKIMAGE_IDE, &d->cyls[i], &d->heads[i],
953 &d->sectors_per_track[i]);
954
955 memory_device_register(devinit->machine->memory, "wdc_altstatus",
956 alt_status_addr, 2, dev_wdc_altstatus_access, d, DM_DEFAULT, NULL);
957 memory_device_register(devinit->machine->memory, devinit->name,
958 devinit->addr, DEV_WDC_LENGTH * devinit->addr_mult, dev_wdc_access,
959 d, DM_DEFAULT, NULL);
960
961 machine_add_tickfunction(devinit->machine, dev_wdc_tick,
962 d, tick_shift);
963
964 devinit->return_ptr = d;
965
966 return 1;
967 }
968

  ViewVC Help
Powered by ViewVC 1.1.26