--- trunk/src/devices/dev_wdc.c 2007/10/08 16:19:05 17 +++ trunk/src/devices/dev_wdc.c 2007/10/08 16:19:11 18 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: dev_wdc.c,v 1.39 2005/09/27 23:55:44 debug Exp $ + * $Id: dev_wdc.c,v 1.44 2005/10/26 14:37:05 debug Exp $ * * Standard "wdc" IDE controller. */ @@ -34,7 +34,6 @@ #include #include -#include "console.h" #include "cpu.h" #include "device.h" #include "diskimage.h" @@ -46,15 +45,16 @@ #define DEV_WDC_LENGTH 8 #define WDC_TICK_SHIFT 14 -#define WDC_INBUF_SIZE (512*257) +#define WDC_MAX_SECTORS 512 +#define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1)) /* - * INT_DELAY=2 to be safe, 1 is faster but maybe buggy. 0 is fastest. + * INT_DELAY: This is an old hack which only exists because (some versions of) + * NetBSD for hpcmips have interrupt problems. These problems are probably not + * specific to GXemul, but are also triggered on real hardware. * - * The only reason for this delay to exist is because (some versions of) - * NetBSD for hpcmips have interrupt problems. These problems are not only - * triggered inside the emulator, but also on real hardware. Using the - * delay is an ugly (but working) work-around. + * See the following URL for more info: + * http://mail-index.netbsd.org/port-hpcmips/2004/12/30/0003.html */ #define INT_DELAY 1 @@ -124,7 +124,8 @@ d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE; if (d->inbuf_head == d->inbuf_tail) - fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun ]\n"); + fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!" + " Increase WDC_MAX_SECTORS. ]\n"); } @@ -224,7 +225,8 @@ */ void wdc__read(struct cpu *cpu, struct wdc_data *d) { - unsigned char buf[512]; + const int max_sectors_per_chunk = 64; + unsigned char buf[512 * max_sectors_per_chunk]; int i, cyl = d->cyl_hi * 256+ d->cyl_lo; int count = d->seccnt? d->seccnt : 256; uint64_t offset = 512 * (d->sector - 1 @@ -240,13 +242,27 @@ #endif while (count > 0) { - diskimage_access(cpu->machine, d->drive + d->base_drive, - DISKIMAGE_IDE, 0, offset, buf, 512); - /* TODO: result code */ - for (i=0; i<512; i++) - wdc_addtoinbuf(d, buf[i]); - offset += 512; - count --; + int to_read = count > max_sectors_per_chunk? + max_sectors_per_chunk : count; + + /* TODO: result code from the read? */ + + if (d->inbuf_head + 512 * to_read <= WDC_INBUF_SIZE) { + diskimage_access(cpu->machine, d->drive + d->base_drive, + DISKIMAGE_IDE, 0, offset, + d->inbuf + d->inbuf_head, 512 * to_read); + d->inbuf_head += 512 * to_read; + if (d->inbuf_head == WDC_INBUF_SIZE) + d->inbuf_head = 0; + } else { + diskimage_access(cpu->machine, d->drive + d->base_drive, + DISKIMAGE_IDE, 0, offset, buf, 512 * to_read); + for (i=0; i<512 * to_read; i++) + wdc_addtoinbuf(d, buf[i]); + } + + offset += 512 * to_read; + count -= to_read; } d->delayed_interrupt = INT_DELAY; @@ -317,7 +333,7 @@ struct wdc_data *d = extra; uint64_t idata = 0, odata = 0; - idata = memory_readmax64(cpu, data, len); + idata = data[0]; /* Same as the normal status byte? */ odata = status_byte(d, cpu); @@ -330,7 +346,7 @@ (int)idata); if (writeflag == MEM_READ) - memory_writemax64(cpu, data, len, odata); + data[0] = odata; return 1; } @@ -347,12 +363,17 @@ uint64_t idata = 0, odata = 0; int i; - idata = memory_readmax64(cpu, data, len); + if (writeflag == MEM_WRITE) { + if (relative_addr == wd_data) + idata = memory_readmax64(cpu, data, len); + else + idata = data[0]; + } switch (relative_addr) { case wd_data: /* 0: data */ - if (writeflag==MEM_READ) { + if (writeflag == MEM_READ) { odata = 0; /* TODO: This is hardcoded for little-endian? */ @@ -368,7 +389,12 @@ if (d->data_debug) debug("[ wdc: read from DATA: 0x%04x ]\n", (int)odata); +#if 0 if (d->inbuf_tail != d->inbuf_head) +#else + if (d->inbuf_tail != d->inbuf_head && + ((d->inbuf_tail - d->inbuf_head) % 512) == 0) +#endif d->delayed_interrupt = INT_DELAY; } else { int inbuf_len; @@ -405,19 +431,21 @@ if ((inbuf_len % 512) == 0) { #endif int count = 1; /* d->write_count; */ - unsigned char *buf = malloc(count * 512); - if (buf == NULL) { - fprintf(stderr, "out of memory\n"); - exit(1); - } + unsigned char buf[512 * count]; + unsigned char *b = buf; - for (i=0; i<512 * count; i++) - buf[i] = wdc_get_inbuf(d); + if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) { + b = d->inbuf + d->inbuf_tail; + d->inbuf_tail = (d->inbuf_tail + 512 + * count) % WDC_INBUF_SIZE; + } else { + for (i=0; i<512 * count; i++) + buf[i] = wdc_get_inbuf(d); + } diskimage_access(cpu->machine, d->drive + d->base_drive, DISKIMAGE_IDE, 1, - d->write_offset, buf, 512 * count); - free(buf); + d->write_offset, b, 512 * count); d->write_count --; d->write_offset += 512; @@ -627,8 +655,15 @@ (int)relative_addr, (int)idata); } - if (writeflag == MEM_READ) - memory_writemax64(cpu, data, len, odata); + if (cpu->machine->machine_type != MACHINE_HPCMIPS) + dev_wdc_tick(cpu, extra); + + if (writeflag == MEM_READ) { + if (relative_addr == wd_data) + memory_writemax64(cpu, data, len, odata); + else + data[0] = odata; + } return 1; } @@ -641,7 +676,7 @@ { struct wdc_data *d; uint64_t alt_status_addr; - int i; + int i, tick_shift = WDC_TICK_SHIFT; d = malloc(sizeof(struct wdc_data)); if (d == NULL) { @@ -676,8 +711,11 @@ devinit->addr, DEV_WDC_LENGTH, dev_wdc_access, d, MEM_DEFAULT, NULL); + if (devinit->machine->machine_type != MACHINE_HPCMIPS) + tick_shift += 2; + machine_add_tickfunction(devinit->machine, dev_wdc_tick, - d, WDC_TICK_SHIFT); + d, tick_shift); return 1; }