25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $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 $ |
29 |
* |
* |
30 |
* Standard "wdc" IDE controller. |
* Standard "wdc" IDE controller. |
31 |
*/ |
*/ |
34 |
#include <stdlib.h> |
#include <stdlib.h> |
35 |
#include <string.h> |
#include <string.h> |
36 |
|
|
|
#include "console.h" |
|
37 |
#include "cpu.h" |
#include "cpu.h" |
38 |
#include "device.h" |
#include "device.h" |
39 |
#include "diskimage.h" |
#include "diskimage.h" |
45 |
|
|
46 |
#define DEV_WDC_LENGTH 8 |
#define DEV_WDC_LENGTH 8 |
47 |
#define WDC_TICK_SHIFT 14 |
#define WDC_TICK_SHIFT 14 |
48 |
#define WDC_INBUF_SIZE (512*257) |
#define WDC_MAX_SECTORS 512 |
49 |
|
#define WDC_INBUF_SIZE (512*(WDC_MAX_SECTORS+1)) |
50 |
|
|
51 |
/* |
/* |
52 |
* 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) |
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 |
* The only reason for this delay to exist is because (some versions of) |
* See the following URL for more info: |
57 |
* NetBSD for hpcmips have interrupt problems. These problems are not only |
* http://mail-index.netbsd.org/port-hpcmips/2004/12/30/0003.html |
|
* triggered inside the emulator, but also on real hardware. Using the |
|
|
* delay is an ugly (but working) work-around. |
|
58 |
*/ |
*/ |
59 |
#define INT_DELAY 1 |
#define INT_DELAY 1 |
60 |
|
|
124 |
|
|
125 |
d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE; |
d->inbuf_head = (d->inbuf_head + 1) % WDC_INBUF_SIZE; |
126 |
if (d->inbuf_head == d->inbuf_tail) |
if (d->inbuf_head == d->inbuf_tail) |
127 |
fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun ]\n"); |
fatal("[ wdc_addtoinbuf(): WARNING! wdc inbuf overrun!" |
128 |
|
" Increase WDC_MAX_SECTORS. ]\n"); |
129 |
} |
} |
130 |
|
|
131 |
|
|
225 |
*/ |
*/ |
226 |
void wdc__read(struct cpu *cpu, struct wdc_data *d) |
void wdc__read(struct cpu *cpu, struct wdc_data *d) |
227 |
{ |
{ |
228 |
unsigned char buf[512]; |
const int max_sectors_per_chunk = 64; |
229 |
|
unsigned char buf[512 * max_sectors_per_chunk]; |
230 |
int i, cyl = d->cyl_hi * 256+ d->cyl_lo; |
int i, cyl = d->cyl_hi * 256+ d->cyl_lo; |
231 |
int count = d->seccnt? d->seccnt : 256; |
int count = d->seccnt? d->seccnt : 256; |
232 |
uint64_t offset = 512 * (d->sector - 1 |
uint64_t offset = 512 * (d->sector - 1 |
242 |
#endif |
#endif |
243 |
|
|
244 |
while (count > 0) { |
while (count > 0) { |
245 |
diskimage_access(cpu->machine, d->drive + d->base_drive, |
int to_read = count > max_sectors_per_chunk? |
246 |
DISKIMAGE_IDE, 0, offset, buf, 512); |
max_sectors_per_chunk : count; |
247 |
/* TODO: result code */ |
|
248 |
for (i=0; i<512; i++) |
/* TODO: result code from the read? */ |
249 |
wdc_addtoinbuf(d, buf[i]); |
|
250 |
offset += 512; |
if (d->inbuf_head + 512 * to_read <= WDC_INBUF_SIZE) { |
251 |
count --; |
diskimage_access(cpu->machine, d->drive + d->base_drive, |
252 |
|
DISKIMAGE_IDE, 0, offset, |
253 |
|
d->inbuf + d->inbuf_head, 512 * to_read); |
254 |
|
d->inbuf_head += 512 * to_read; |
255 |
|
if (d->inbuf_head == WDC_INBUF_SIZE) |
256 |
|
d->inbuf_head = 0; |
257 |
|
} else { |
258 |
|
diskimage_access(cpu->machine, d->drive + d->base_drive, |
259 |
|
DISKIMAGE_IDE, 0, offset, buf, 512 * to_read); |
260 |
|
for (i=0; i<512 * to_read; i++) |
261 |
|
wdc_addtoinbuf(d, buf[i]); |
262 |
|
} |
263 |
|
|
264 |
|
offset += 512 * to_read; |
265 |
|
count -= to_read; |
266 |
} |
} |
267 |
|
|
268 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
333 |
struct wdc_data *d = extra; |
struct wdc_data *d = extra; |
334 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
335 |
|
|
336 |
idata = memory_readmax64(cpu, data, len); |
idata = data[0]; |
337 |
|
|
338 |
/* Same as the normal status byte? */ |
/* Same as the normal status byte? */ |
339 |
odata = status_byte(d, cpu); |
odata = status_byte(d, cpu); |
346 |
(int)idata); |
(int)idata); |
347 |
|
|
348 |
if (writeflag == MEM_READ) |
if (writeflag == MEM_READ) |
349 |
memory_writemax64(cpu, data, len, odata); |
data[0] = odata; |
350 |
|
|
351 |
return 1; |
return 1; |
352 |
} |
} |
363 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
364 |
int i; |
int i; |
365 |
|
|
366 |
idata = memory_readmax64(cpu, data, len); |
if (writeflag == MEM_WRITE) { |
367 |
|
if (relative_addr == wd_data) |
368 |
|
idata = memory_readmax64(cpu, data, len); |
369 |
|
else |
370 |
|
idata = data[0]; |
371 |
|
} |
372 |
|
|
373 |
switch (relative_addr) { |
switch (relative_addr) { |
374 |
|
|
375 |
case wd_data: /* 0: data */ |
case wd_data: /* 0: data */ |
376 |
if (writeflag==MEM_READ) { |
if (writeflag == MEM_READ) { |
377 |
odata = 0; |
odata = 0; |
378 |
|
|
379 |
/* TODO: This is hardcoded for little-endian? */ |
/* TODO: This is hardcoded for little-endian? */ |
389 |
if (d->data_debug) |
if (d->data_debug) |
390 |
debug("[ wdc: read from DATA: 0x%04x ]\n", |
debug("[ wdc: read from DATA: 0x%04x ]\n", |
391 |
(int)odata); |
(int)odata); |
392 |
|
#if 0 |
393 |
if (d->inbuf_tail != d->inbuf_head) |
if (d->inbuf_tail != d->inbuf_head) |
394 |
|
#else |
395 |
|
if (d->inbuf_tail != d->inbuf_head && |
396 |
|
((d->inbuf_tail - d->inbuf_head) % 512) == 0) |
397 |
|
#endif |
398 |
d->delayed_interrupt = INT_DELAY; |
d->delayed_interrupt = INT_DELAY; |
399 |
} else { |
} else { |
400 |
int inbuf_len; |
int inbuf_len; |
431 |
if ((inbuf_len % 512) == 0) { |
if ((inbuf_len % 512) == 0) { |
432 |
#endif |
#endif |
433 |
int count = 1; /* d->write_count; */ |
int count = 1; /* d->write_count; */ |
434 |
unsigned char *buf = malloc(count * 512); |
unsigned char buf[512 * count]; |
435 |
if (buf == NULL) { |
unsigned char *b = buf; |
|
fprintf(stderr, "out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
436 |
|
|
437 |
for (i=0; i<512 * count; i++) |
if (d->inbuf_tail+512*count <= WDC_INBUF_SIZE) { |
438 |
buf[i] = wdc_get_inbuf(d); |
b = d->inbuf + d->inbuf_tail; |
439 |
|
d->inbuf_tail = (d->inbuf_tail + 512 |
440 |
|
* count) % WDC_INBUF_SIZE; |
441 |
|
} else { |
442 |
|
for (i=0; i<512 * count; i++) |
443 |
|
buf[i] = wdc_get_inbuf(d); |
444 |
|
} |
445 |
|
|
446 |
diskimage_access(cpu->machine, |
diskimage_access(cpu->machine, |
447 |
d->drive + d->base_drive, DISKIMAGE_IDE, 1, |
d->drive + d->base_drive, DISKIMAGE_IDE, 1, |
448 |
d->write_offset, buf, 512 * count); |
d->write_offset, b, 512 * count); |
|
free(buf); |
|
449 |
|
|
450 |
d->write_count --; |
d->write_count --; |
451 |
d->write_offset += 512; |
d->write_offset += 512; |
655 |
(int)relative_addr, (int)idata); |
(int)relative_addr, (int)idata); |
656 |
} |
} |
657 |
|
|
658 |
if (writeflag == MEM_READ) |
if (cpu->machine->machine_type != MACHINE_HPCMIPS) |
659 |
memory_writemax64(cpu, data, len, odata); |
dev_wdc_tick(cpu, extra); |
660 |
|
|
661 |
|
if (writeflag == MEM_READ) { |
662 |
|
if (relative_addr == wd_data) |
663 |
|
memory_writemax64(cpu, data, len, odata); |
664 |
|
else |
665 |
|
data[0] = odata; |
666 |
|
} |
667 |
|
|
668 |
return 1; |
return 1; |
669 |
} |
} |
676 |
{ |
{ |
677 |
struct wdc_data *d; |
struct wdc_data *d; |
678 |
uint64_t alt_status_addr; |
uint64_t alt_status_addr; |
679 |
int i; |
int i, tick_shift = WDC_TICK_SHIFT; |
680 |
|
|
681 |
d = malloc(sizeof(struct wdc_data)); |
d = malloc(sizeof(struct wdc_data)); |
682 |
if (d == NULL) { |
if (d == NULL) { |
711 |
devinit->addr, DEV_WDC_LENGTH, dev_wdc_access, d, MEM_DEFAULT, |
devinit->addr, DEV_WDC_LENGTH, dev_wdc_access, d, MEM_DEFAULT, |
712 |
NULL); |
NULL); |
713 |
|
|
714 |
|
if (devinit->machine->machine_type != MACHINE_HPCMIPS) |
715 |
|
tick_shift += 2; |
716 |
|
|
717 |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
machine_add_tickfunction(devinit->machine, dev_wdc_tick, |
718 |
d, WDC_TICK_SHIFT); |
d, tick_shift); |
719 |
|
|
720 |
return 1; |
return 1; |
721 |
} |
} |