/[gxemul]/upstream/0.3.1/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 /upstream/0.3.1/devices/dev_wdc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 15904 byte(s)
0.3.1
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.28 2005/04/01 16:44:34 debug Exp $
29 *
30 * Standard IDE controller.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "console.h"
38 #include "cop0.h"
39 #include "cpu.h"
40 #include "devices.h"
41 #include "diskimage.h"
42 #include "machine.h"
43 #include "memory.h"
44 #include "misc.h"
45
46 #include "wdcreg.h"
47
48
49 extern int single_step;
50
51 #define WDC_TICK_SHIFT 14
52 #define WDC_INBUF_SIZE (512*257)
53
54 /* INT_DELAY=2 to be safe, 1 is faster but maybe buggy. */
55 #define INT_DELAY 1
56
57 /* #define debug fatal */
58 /* #define DATA_DEBUG */
59
60 struct wdc_data {
61 int irq_nr;
62 int base_drive;
63
64 int delayed_interrupt;
65
66 unsigned char identify_struct[512];
67
68 unsigned char inbuf[WDC_INBUF_SIZE];
69 int inbuf_head;
70 int inbuf_tail;
71
72 int write_in_progress;
73 int write_count;
74 int64_t write_offset;
75
76 int error;
77 int precomp;
78 int seccnt;
79 int sector;
80 int cyl_lo;
81 int cyl_hi;
82 int sectorsize;
83 int lba;
84 int drive;
85 int head;
86 int cur_command;
87 };
88
89
90 /*
91 * dev_wdc_tick():
92 */
93 void dev_wdc_tick(struct cpu *cpu, void *extra)
94 {
95 struct wdc_data *d = extra;
96
97 if (d->delayed_interrupt) {
98 d->delayed_interrupt --;
99
100 if (d->delayed_interrupt == 0)
101 cpu_interrupt(cpu, d->irq_nr);
102 }
103 }
104
105
106 /*
107 * wdc_addtoinbuf():
108 *
109 * Write to the inbuf at its head, read at its tail.
110 */
111 static void wdc_addtoinbuf(struct wdc_data *d, int c)
112 {
113 d->inbuf[d->inbuf_head] = c;
114
115 d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE;
116 if (d->inbuf_head == d->inbuf_tail)
117 fatal("WARNING! wdc inbuf overrun\n");
118 }
119
120
121 /*
122 * wdc_get_inbuf():
123 *
124 * Read from the tail of inbuf.
125 */
126 static uint64_t wdc_get_inbuf(struct wdc_data *d)
127 {
128 int c = d->inbuf[d->inbuf_tail];
129
130 if (d->inbuf_head == d->inbuf_tail) {
131 fatal("WARNING! someone is reading too much from the "
132 "wdc inbuf!\n");
133 return -1;
134 }
135
136 d->inbuf_tail = (d->inbuf_tail + 1) % WDC_INBUF_SIZE;
137 return c;
138 }
139
140
141 /*
142 * wdc_initialize_identify_struct(d):
143 */
144 static void wdc_initialize_identify_struct(struct cpu *cpu, struct wdc_data *d)
145 {
146 uint64_t total_size, cyls;
147
148 total_size = diskimage_getsize(cpu->machine, d->drive + d->base_drive);
149
150 memset(d->identify_struct, 0, sizeof(d->identify_struct));
151
152 cyls = total_size / (63 * 16 * 512);
153 if (cyls * 63*16*512 < total_size)
154 cyls ++;
155
156 /* Offsets are in 16-bit WORDS! High byte, then low. */
157
158 /* 0: general flags */
159 d->identify_struct[2 * 0 + 0] = 0;
160 d->identify_struct[2 * 0 + 1] = 1 << 6;
161
162 /* 1: nr of cylinders */
163 d->identify_struct[2 * 1 + 0] = (cyls >> 8);
164 d->identify_struct[2 * 1 + 1] = cyls & 255;
165
166 /* 3: nr of heads */
167 d->identify_struct[2 * 3 + 0] = 0;
168 d->identify_struct[2 * 3 + 1] = 16;
169
170 /* 6: sectors per track */
171 d->identify_struct[2 * 6 + 0] = 0;
172 d->identify_struct[2 * 6 + 1] = 63;
173
174 /* 10-19: Serial number */
175 memcpy(&d->identify_struct[2 * 10], "S/N 1234-5678 ", 20);
176
177 /* 23-26: Firmware version */
178 memcpy(&d->identify_struct[2 * 23], "VER 1.0 ", 8);
179
180 /* 27-46: Model number */
181 memcpy(&d->identify_struct[2 * 27],
182 "Fake GXemul disk ", 40);
183 /* TODO: Use the diskimage's filename instead? */
184
185 /* 47: max sectors per multitransfer */
186 d->identify_struct[2 * 47 + 0] = 0x80;
187 d->identify_struct[2 * 47 + 1] = 1; /* 1 or 16? */
188
189 /* 57-58: current capacity in sectors */
190 d->identify_struct[2 * 57 + 0] = ((total_size / 512) >> 24) % 255;
191 d->identify_struct[2 * 57 + 1] = ((total_size / 512) >> 16) % 255;
192 d->identify_struct[2 * 58 + 0] = ((total_size / 512) >> 8) % 255;
193 d->identify_struct[2 * 58 + 1] = (total_size / 512) & 255;
194
195 /* 60-61: total nr of addresable sectors */
196 d->identify_struct[2 * 60 + 0] = ((total_size / 512) >> 24) % 255;
197 d->identify_struct[2 * 60 + 1] = ((total_size / 512) >> 16) % 255;
198 d->identify_struct[2 * 61 + 0] = ((total_size / 512) >> 8) % 255;
199 d->identify_struct[2 * 61 + 1] = (total_size / 512) & 255;
200
201 }
202
203
204 /*
205 * dev_wdc_altstatus_access():
206 */
207 int dev_wdc_altstatus_access(struct cpu *cpu, struct memory *mem,
208 uint64_t relative_addr, unsigned char *data, size_t len,
209 int writeflag, void *extra)
210 {
211 struct wdc_data *d = extra;
212 uint64_t idata = 0, odata = 0;
213
214 idata = memory_readmax64(cpu, data, len);
215
216 /* Same as the normal status byte? */
217 odata = 0;
218 if (diskimage_exist(cpu->machine, d->drive + d->base_drive))
219 odata |= WDCS_DRDY;
220 if (d->inbuf_head != d->inbuf_tail)
221 odata |= WDCS_DRQ;
222
223 if (writeflag==MEM_READ)
224 debug("[ wdc: read from ALTSTATUS: 0x%02x ]\n",
225 (int)relative_addr, odata);
226 else
227 debug("[ wdc: write to ALT. CTRL: 0x%02x ]\n",
228 (int)relative_addr, idata);
229
230 if (writeflag == MEM_READ)
231 memory_writemax64(cpu, data, len, odata);
232
233 return 1;
234 }
235
236
237 /*
238 * dev_wdc_access():
239 */
240 int dev_wdc_access(struct cpu *cpu, struct memory *mem,
241 uint64_t relative_addr, unsigned char *data, size_t len,
242 int writeflag, void *extra)
243 {
244 struct wdc_data *d = extra;
245 uint64_t idata = 0, odata = 0;
246 int i;
247
248 idata = memory_readmax64(cpu, data, len);
249
250 switch (relative_addr) {
251
252 case wd_data: /* 0: data */
253 if (writeflag==MEM_READ) {
254 odata = 0;
255 #if 0
256 if (len == 4) {
257 odata += (wdc_get_inbuf(d) << 24);
258 odata += (wdc_get_inbuf(d) << 16);
259 }
260 if (len >= 2)
261 odata += (wdc_get_inbuf(d) << 8);
262 odata += wdc_get_inbuf(d);
263 #else
264 odata += wdc_get_inbuf(d);
265 if (len >= 2)
266 odata += (wdc_get_inbuf(d) << 8);
267 if (len == 4) {
268 odata += (wdc_get_inbuf(d) << 16);
269 odata += (wdc_get_inbuf(d) << 24);
270 }
271 #endif
272
273 #ifdef DATA_DEBUG
274 debug("[ wdc: read from DATA: 0x%04x ]\n", odata);
275 #endif
276
277
278
279 if (d->inbuf_tail != d->inbuf_head)
280 d->delayed_interrupt = INT_DELAY;
281
282 } else {
283 int inbuf_len;
284 #ifdef DATA_DEBUG
285 debug("[ wdc: write to DATA (len=%i): 0x%08lx ]\n",
286 (int)len, (long)idata);
287 #endif
288 if (!d->write_in_progress) {
289 fatal("[ wdc: write to DATA, but not "
290 "expecting any? (len=%i): 0x%08lx ]\n",
291 (int)len, (long)idata);
292 }
293
294 switch (len) {
295 case 4: wdc_addtoinbuf(d, idata & 0xff);
296 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
297 wdc_addtoinbuf(d, (idata >> 16) & 0xff);
298 wdc_addtoinbuf(d, (idata >> 24) & 0xff);
299 break;
300 case 2: wdc_addtoinbuf(d, idata & 0xff);
301 wdc_addtoinbuf(d, (idata >> 8) & 0xff);
302 break;
303 case 1: wdc_addtoinbuf(d, idata); break;
304 default:fatal("wdc: unimplemented write len %i\n", len);
305 exit(1);
306 }
307
308 inbuf_len = d->inbuf_head - d->inbuf_tail;
309 while (inbuf_len < 0)
310 inbuf_len += WDC_INBUF_SIZE;
311
312 #if 0
313 if ((inbuf_len % (512 * d->write_count)) == 0) {
314 #endif
315 if ((inbuf_len % 512) == 0) {
316 int count = 1; /* d->write_count; */
317 unsigned char *buf = malloc(count * 512);
318 if (buf == NULL) {
319 fprintf(stderr, "out of memory\n");
320 exit(1);
321 }
322
323 for (i=0; i<512 * count; i++)
324 buf[i] = wdc_get_inbuf(d);
325
326 diskimage_access(cpu->machine,
327 d->drive + d->base_drive, 1,
328 d->write_offset, buf, 512 * count);
329 free(buf);
330
331 d->write_count --;
332 d->write_offset += 512;
333
334 d->delayed_interrupt = INT_DELAY;
335
336 if (d->write_count == 0)
337 d->write_in_progress = 0;
338 }
339 }
340 break;
341
342 case wd_error: /* 1: error (r), precomp (w) */
343 if (writeflag==MEM_READ) {
344 odata = d->error;
345 debug("[ wdc: read from ERROR: 0x%02x ]\n", odata);
346 /* TODO: is the error value cleared on read? */
347 d->error = 0;
348 } else {
349 d->precomp = idata;
350 debug("[ wdc: write to PRECOMP: 0x%02x ]\n", idata);
351 }
352 break;
353
354 case wd_seccnt: /* 2: sector count */
355 if (writeflag==MEM_READ) {
356 odata = d->seccnt;
357 debug("[ wdc: read from SECCNT: 0x%02x ]\n", odata);
358 } else {
359 d->seccnt = idata;
360 debug("[ wdc: write to SECCNT: 0x%02x ]\n", idata);
361 }
362 break;
363
364 case wd_sector: /* 3: first sector */
365 if (writeflag==MEM_READ) {
366 odata = d->sector;
367 debug("[ wdc: read from SECTOR: 0x%02x ]\n", odata);
368 } else {
369 d->sector = idata;
370 debug("[ wdc: write to SECTOR: 0x%02x ]\n", idata);
371 }
372 break;
373
374 case wd_cyl_lo: /* 4: cylinder low */
375 if (writeflag==MEM_READ) {
376 odata = d->cyl_lo;
377 debug("[ wdc: read from CYL_LO: 0x%02x ]\n", odata);
378 } else {
379 d->cyl_lo = idata;
380 debug("[ wdc: write to CYL_LO: 0x%02x ]\n", idata);
381 }
382 break;
383
384 case wd_cyl_hi: /* 5: cylinder low */
385 if (writeflag==MEM_READ) {
386 odata = d->cyl_hi;
387 debug("[ wdc: read from CYL_HI: 0x%02x ]\n", odata);
388 } else {
389 d->cyl_hi = idata;
390 debug("[ wdc: write to CYL_HI: 0x%02x ]\n", idata);
391 }
392 break;
393
394 case wd_sdh: /* 6: sectorsize/drive/head */
395 if (writeflag==MEM_READ) {
396 odata = (d->sectorsize << 6) + (d->lba << 5) +
397 (d->drive << 4) + (d->head);
398 debug("[ wdc: read from SDH: 0x%02x (sectorsize %i,"
399 " lba=%i, drive %i, head %i) ]\n", (int)odata,
400 d->sectorsize, d->lba, d->drive, d->head);
401 } else {
402 d->sectorsize = (idata >> 6) & 3;
403 d->lba = (idata >> 5) & 1;
404 d->drive = (idata >> 4) & 1;
405 d->head = idata & 0xf;
406 debug("[ wdc: write to SDH: 0x%02x (sectorsize %i,"
407 " lba=%i, drive %i, head %i) ]\n", (int)idata,
408 d->sectorsize, d->lba, d->drive, d->head);
409 }
410 break;
411
412 case wd_command: /* 7: command or status */
413 if (writeflag==MEM_READ) {
414 odata = 0;
415 if (diskimage_exist(cpu->machine,
416 d->drive + d->base_drive))
417 odata |= WDCS_DRDY;
418 if (d->inbuf_head != d->inbuf_tail)
419 odata |= WDCS_DRQ;
420 if (d->write_in_progress)
421 odata |= WDCS_DRQ;
422 if (d->error)
423 odata |= WDCS_ERR;
424
425 /* TODO: Is this correct behaviour? */
426 if (!diskimage_exist(cpu->machine,
427 d->drive + d->base_drive))
428 odata = 0xff;
429
430 #if 1
431 debug("[ wdc: read from STATUS: 0x%02x ]\n", odata);
432 #endif
433
434 cpu_interrupt_ack(cpu, d->irq_nr);
435 d->delayed_interrupt = 0;
436 } else {
437 debug("[ wdc: write to COMMAND: 0x%02x ]\n", idata);
438 d->cur_command = idata;
439
440 /* TODO: Is this correct behaviour? */
441 if (!diskimage_exist(cpu->machine,
442 d->drive + d->base_drive))
443 break;
444
445 /* Handle the command: */
446 switch (d->cur_command) {
447 case WDCC_READ:
448 debug("[ wdc: READ from drive %i, head %i, "
449 "cylinder %i, sector %i, nsecs %i ]\n",
450 d->drive, d->head, d->cyl_hi*256+d->cyl_lo,
451 d->sector, d->seccnt);
452 /* TODO: HAHA! This should be removed
453 quickly */
454 {
455 unsigned char buf[512*256];
456 int cyl = d->cyl_hi * 256+ d->cyl_lo;
457 int count = d->seccnt? d->seccnt : 256;
458 uint64_t offset = 512 * (d->sector - 1
459 + d->head * 63 + 16*63*cyl);
460
461 #if 0
462 /* LBA: */
463 if (d->lba)
464 offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8) + d->sector);
465 printf("WDC read from offset %lli\n", (long long)offset);
466 #endif
467 /* single_step = 1; */
468 diskimage_access(cpu->machine,
469 d->drive + d->base_drive, 0,
470 offset, buf, 512 * count);
471 /* TODO: result code */
472 for (i=0; i<512 * count; i++)
473 wdc_addtoinbuf(d, buf[i]);
474 }
475 d->delayed_interrupt = INT_DELAY;
476 break;
477 case WDCC_WRITE:
478 debug("[ wdc: WRITE to drive %i, head %i, "
479 "cylinder %i, sector %i, nsecs %i ]\n",
480 d->drive, d->head, d->cyl_hi*256+d->cyl_lo,
481 d->sector, d->seccnt);
482 /* TODO: HAHA! This should be removed
483 quickly */
484 {
485 int cyl = d->cyl_hi * 256+ d->cyl_lo;
486 int count = d->seccnt? d->seccnt : 256;
487 uint64_t offset = 512 * (d->sector - 1
488 + d->head * 63 + 16*63*cyl);
489
490 #if 0
491 /* LBA: */
492 if (d->lba)
493 offset = 512 * (((d->head & 0xf) << 24) + (cyl << 8) + d->sector);
494 printf("WDC write to offset %lli\n", (long long)offset);
495 #endif
496
497 d->write_in_progress = 1;
498 d->write_count = count;
499 d->write_offset = offset;
500
501 /* TODO: result code */
502 }
503 /* TODO: Really interrupt here? */
504 #if 0
505 d->delayed_interrupt = INT_DELAY;
506 #endif
507 break;
508
509 case WDCC_IDP: /* Initialize drive parameters */
510 debug("[ wdc: IDP drive %i (TODO) ]\n",
511 d->drive);
512 /* TODO */
513 d->delayed_interrupt = INT_DELAY;
514 break;
515 case SET_FEATURES:
516 fatal("[ wdc: SET_FEATURES drive %i (TODO), "
517 "feature 0x%02x ]\n", d->drive, d->precomp);
518 /* TODO */
519 switch (d->precomp) {
520 case WDSF_SET_MODE:
521 fatal("[ wdc: WDSF_SET_MODE drive %i, "
522 "pio/dma flags 0x%02x ]\n",
523 d->drive, d->seccnt);
524 break;
525 default:
526 d->error |= WDCE_ABRT;
527 }
528 /* TODO: always interrupt? */
529 d->delayed_interrupt = INT_DELAY;
530 break;
531 case WDCC_RECAL:
532 debug("[ wdc: RECAL drive %i ]\n", d->drive);
533 d->delayed_interrupt = INT_DELAY;
534 break;
535 case WDCC_IDENTIFY:
536 debug("[ wdc: IDENTIFY drive %i ]\n", d->drive);
537 wdc_initialize_identify_struct(cpu, d);
538 /* The IDENTIFY data block is sent out
539 in low/high byte order: */
540 for (i=0; i<sizeof(d->identify_struct); i+=2) {
541 wdc_addtoinbuf(d, d->identify_struct
542 [i+1]);
543 wdc_addtoinbuf(d, d->identify_struct
544 [i+0]);
545 }
546 d->delayed_interrupt = INT_DELAY;
547 break;
548 default:
549 fatal("[ wdc: unknown command 0x%02x ("
550 "drive %i, head %i, cylinder %i, sector %i,"
551 " nsecs %i) ]\n", d->cur_command, d->drive,
552 d->head, d->cyl_hi*256+d->cyl_lo,
553 d->sector, d->seccnt);
554 exit(1);
555 }
556 }
557 break;
558
559 default:
560 if (writeflag==MEM_READ)
561 debug("[ wdc: read from 0x%02x ]\n",
562 (int)relative_addr);
563 else
564 debug("[ wdc: write to 0x%02x: 0x%02x ]\n",
565 (int)relative_addr, (int)idata);
566 }
567
568 if (writeflag == MEM_READ)
569 memory_writemax64(cpu, data, len, odata);
570
571 return 1;
572 }
573
574
575 /*
576 * dev_wdc_init():
577 *
578 * base_drive should be 0 for the primary device, and 2 for the secondary.
579 */
580 void dev_wdc_init(struct machine *machine, struct memory *mem,
581 uint64_t baseaddr, int irq_nr, int base_drive)
582 {
583 struct wdc_data *d;
584 uint64_t alt_status_addr;
585
586 d = malloc(sizeof(struct wdc_data));
587 if (d == NULL) {
588 fprintf(stderr, "out of memory\n");
589 exit(1);
590 }
591 memset(d, 0, sizeof(struct wdc_data));
592 d->irq_nr = irq_nr;
593 d->base_drive = base_drive;
594
595 alt_status_addr = baseaddr + 0x206;
596
597 /* Special hack for pcic/hpcmips: TODO: Fix */
598 if (baseaddr == 0x14000180)
599 alt_status_addr = 0x14000386;
600
601 memory_device_register(mem, "wdc_altstatus", alt_status_addr, 2,
602 dev_wdc_altstatus_access, d, MEM_DEFAULT, NULL);
603 memory_device_register(mem, "wdc", baseaddr, DEV_WDC_LENGTH,
604 dev_wdc_access, d, MEM_DEFAULT, NULL);
605
606 machine_add_tickfunction(machine, dev_wdc_tick,
607 d, WDC_TICK_SHIFT);
608 }
609

  ViewVC Help
Powered by ViewVC 1.1.26