X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Ffdc.c;h=dcd1d46b4886ed6b1ca5a333a022b5be0146c068;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=0de5578bb6225311769533cfe3a48da1b10aa36b;hpb=a541f297a37e64673aac52abc858e0904e316b48;p=qemu diff --git a/hw/fdc.c b/hw/fdc.c index 0de5578..dcd1d46 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -1,8 +1,8 @@ /* - * QEMU Floppy disk emulator - * - * Copyright (c) 2003 Jocelyn Mayer - * + * QEMU Floppy disk emulator (Intel 82078) + * + * Copyright (c) 2003, 2007 Jocelyn Mayer + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +/* + * The controller is used in Sun4m systems in a slightly different + * way. There are changes in DOR register and DMA is not available. + */ #include "vl.h" /********************************************************/ @@ -62,7 +66,6 @@ typedef enum fdrive_type_t { typedef enum fdrive_flags_t { FDRIVE_MOTOR_ON = 0x01, /* motor on/off */ - FDRIVE_REVALIDATE = 0x02, /* Revalidated */ } fdrive_flags_t; typedef enum fdisk_flags_t { @@ -94,10 +97,7 @@ static void fd_init (fdrive_t *drv, BlockDriverState *bs) { /* Drive */ drv->bs = bs; - if (bs) - drv->drive = FDRIVE_DRV_144; - else - drv->drive = FDRIVE_DRV_NONE; + drv->drive = FDRIVE_DRV_NONE; drv->drflags = 0; drv->perpendicular = 0; /* Disk */ @@ -125,17 +125,17 @@ static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, if (track > drv->max_track || (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { - FLOPPY_ERROR("try to read %d %02x %02x (max=%d %d %02x %02x)\n", - head, track, sect, 1, - (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, - drv->max_track, drv->last_sect); + FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", + head, track, sect, 1, + (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, + drv->max_track, drv->last_sect); return 2; } if (sect > drv->last_sect) { - FLOPPY_ERROR("try to read %d %02x %02x (max=%d %d %02x %02x)\n", - head, track, sect, 1, - (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, - drv->max_track, drv->last_sect); + FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", + head, track, sect, 1, + (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, + drv->max_track, drv->last_sect); return 3; } sector = _fd_sector(head, track, sect, drv->last_sect); @@ -217,7 +217,7 @@ static fd_format_t fd_formats[] = { { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", }, - /* 320 kB 5"1/4 floppy disks */ + /* 320 kB 5"1/4 floppy disks */ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", }, { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", }, /* 360 kB must match 5"1/4 better than 3"1/2... */ @@ -235,13 +235,12 @@ static void fd_revalidate (fdrive_t *drv) int nb_heads, max_track, last_sect, ro; FLOPPY_DPRINTF("revalidate\n"); - drv->drflags &= ~FDRIVE_REVALIDATE; if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ro = bdrv_is_read_only(drv->bs); bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); if (nb_heads != 0 && max_track != 0 && last_sect != 0) { - printf("User defined disk (%d %d %d)", - nb_heads - 1, max_track, last_sect); + FLOPPY_DPRINTF("User defined disk (%d %d %d)", + nb_heads - 1, max_track, last_sect); } else { bdrv_get_geometry(drv->bs, &nb_sectors); match = -1; @@ -273,8 +272,8 @@ static void fd_revalidate (fdrive_t *drv) max_track = parse->max_track; last_sect = parse->last_sect; drv->drive = parse->drive; - printf("%s floppy disk (%d h %d t %d s) %s\n", parse->str, - nb_heads, max_track, last_sect, ro ? "ro" : "rw"); + FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str, + nb_heads, max_track, last_sect, ro ? "ro" : "rw"); } if (nb_heads == 1) { drv->flags &= ~FDISK_DBL_SIDES; @@ -285,12 +284,11 @@ static void fd_revalidate (fdrive_t *drv) drv->last_sect = last_sect; drv->ro = ro; } else { - printf("No disk in drive\n"); + FLOPPY_DPRINTF("No disk in drive\n"); drv->last_sect = 0; drv->max_track = 0; drv->flags &= ~FDISK_DBL_SIDES; } - drv->drflags |= FDRIVE_REVALIDATE; } /* Motor control */ @@ -312,12 +310,14 @@ static void fd_reset (fdrive_t *drv) } /********************************************************/ -/* Intel 82078 floppy disk controler emulation */ +/* Intel 82078 floppy disk controller emulation */ static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); static void fdctrl_reset_fifo (fdctrl_t *fdctrl); -static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size); +static int fdctrl_transfer_handler (void *opaque, int nchan, + int dma_pos, int dma_len); static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); +static void fdctrl_result_timer(void *opaque); static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl); static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl); @@ -331,10 +331,10 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value); static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl); enum { - FD_CTRL_ACTIVE = 0x01, + FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */ FD_CTRL_RESET = 0x02, - FD_CTRL_SLEEP = 0x04, - FD_CTRL_BUSY = 0x08, + FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */ + FD_CTRL_BUSY = 0x08, /* dma transfer in progress */ FD_CTRL_INTR = 0x10, }; @@ -365,13 +365,14 @@ do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0) struct fdctrl_t { fdctrl_t *fdctrl; - /* Controler's identification */ + /* Controller's identification */ uint8_t version; /* HW */ - int irq_lvl; + qemu_irq irq; int dma_chann; - uint32_t io_base; - /* Controler state */ + target_phys_addr_t io_base; + /* Controller state */ + QEMUTimer *result_timer; uint8_t state; uint8_t dma_en; uint8_t cur_drv; @@ -383,6 +384,7 @@ struct fdctrl_t { uint8_t data_state; uint8_t data_dir; uint8_t int_status; + uint8_t eot; /* last wanted sector */ /* States kept only to be returned back */ /* Timers state */ uint8_t timer0; @@ -403,6 +405,12 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) uint32_t retval; switch (reg & 0x07) { +#ifdef TARGET_SPARC + case 0x00: + // Identify to Linux as S82078B + retval = fdctrl_read_statusB(fdctrl); + break; +#endif case 0x01: retval = fdctrl_read_statusB(fdctrl); break; @@ -425,6 +433,7 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) retval = (uint32_t)(-1); break; } + FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); return retval; } @@ -433,6 +442,8 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) { fdctrl_t *fdctrl = opaque; + FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); + switch (reg & 0x07) { case 0x02: fdctrl_write_dor(fdctrl, value); @@ -451,33 +462,139 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) } } -static void fd_change_cb (void *opaque) +static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg) { - fdrive_t *drv = opaque; + return fdctrl_read(opaque, (uint32_t)reg); +} - FLOPPY_DPRINTF("disk change\n"); - fd_revalidate(drv); -#if 0 - fd_recalibrate(drv); - fdctrl_reset_fifo(drv->fdctrl); - fdctrl_raise_irq(drv->fdctrl, 0x20); -#endif +static void fdctrl_write_mem (void *opaque, + target_phys_addr_t reg, uint32_t value) +{ + fdctrl_write(opaque, (uint32_t)reg, value); +} + +static CPUReadMemoryFunc *fdctrl_mem_read[3] = { + fdctrl_read_mem, + fdctrl_read_mem, + fdctrl_read_mem, +}; + +static CPUWriteMemoryFunc *fdctrl_mem_write[3] = { + fdctrl_write_mem, + fdctrl_write_mem, + fdctrl_write_mem, +}; + +static void fd_save (QEMUFile *f, fdrive_t *fd) +{ + uint8_t tmp; + + tmp = fd->drflags; + qemu_put_8s(f, &tmp); + qemu_put_8s(f, &fd->head); + qemu_put_8s(f, &fd->track); + qemu_put_8s(f, &fd->sect); + qemu_put_8s(f, &fd->dir); + qemu_put_8s(f, &fd->rw); +} + +static void fdc_save (QEMUFile *f, void *opaque) +{ + fdctrl_t *s = opaque; + + qemu_put_8s(f, &s->state); + qemu_put_8s(f, &s->dma_en); + qemu_put_8s(f, &s->cur_drv); + qemu_put_8s(f, &s->bootsel); + qemu_put_buffer(f, s->fifo, FD_SECTOR_LEN); + qemu_put_be32s(f, &s->data_pos); + qemu_put_be32s(f, &s->data_len); + qemu_put_8s(f, &s->data_state); + qemu_put_8s(f, &s->data_dir); + qemu_put_8s(f, &s->int_status); + qemu_put_8s(f, &s->eot); + qemu_put_8s(f, &s->timer0); + qemu_put_8s(f, &s->timer1); + qemu_put_8s(f, &s->precomp_trk); + qemu_put_8s(f, &s->config); + qemu_put_8s(f, &s->lock); + qemu_put_8s(f, &s->pwrd); + fd_save(f, &s->drives[0]); + fd_save(f, &s->drives[1]); } -fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, - uint32_t io_base, +static int fd_load (QEMUFile *f, fdrive_t *fd) +{ + uint8_t tmp; + + qemu_get_8s(f, &tmp); + fd->drflags = tmp; + qemu_get_8s(f, &fd->head); + qemu_get_8s(f, &fd->track); + qemu_get_8s(f, &fd->sect); + qemu_get_8s(f, &fd->dir); + qemu_get_8s(f, &fd->rw); + + return 0; +} + +static int fdc_load (QEMUFile *f, void *opaque, int version_id) +{ + fdctrl_t *s = opaque; + int ret; + + if (version_id != 1) + return -EINVAL; + + qemu_get_8s(f, &s->state); + qemu_get_8s(f, &s->dma_en); + qemu_get_8s(f, &s->cur_drv); + qemu_get_8s(f, &s->bootsel); + qemu_get_buffer(f, s->fifo, FD_SECTOR_LEN); + qemu_get_be32s(f, &s->data_pos); + qemu_get_be32s(f, &s->data_len); + qemu_get_8s(f, &s->data_state); + qemu_get_8s(f, &s->data_dir); + qemu_get_8s(f, &s->int_status); + qemu_get_8s(f, &s->eot); + qemu_get_8s(f, &s->timer0); + qemu_get_8s(f, &s->timer1); + qemu_get_8s(f, &s->precomp_trk); + qemu_get_8s(f, &s->config); + qemu_get_8s(f, &s->lock); + qemu_get_8s(f, &s->pwrd); + + ret = fd_load(f, &s->drives[0]); + if (ret == 0) + ret = fd_load(f, &s->drives[1]); + + return ret; +} + +static void fdctrl_external_reset(void *opaque) +{ + fdctrl_t *s = opaque; + + fdctrl_reset(s, 0); +} + +fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped, + target_phys_addr_t io_base, BlockDriverState **fds) { fdctrl_t *fdctrl; -// int io_mem; + int io_mem; int i; - FLOPPY_DPRINTF("init controler\n"); + FLOPPY_DPRINTF("init controller\n"); fdctrl = qemu_mallocz(sizeof(fdctrl_t)); if (!fdctrl) return NULL; - fdctrl->version = 0x90; /* Intel 82078 controler */ - fdctrl->irq_lvl = irq_lvl; + fdctrl->result_timer = qemu_new_timer(vm_clock, + fdctrl_result_timer, fdctrl); + + fdctrl->version = 0x90; /* Intel 82078 controller */ + fdctrl->irq = irq; fdctrl->dma_chann = dma_chann; fdctrl->io_base = io_base; fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */ @@ -489,25 +606,24 @@ fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, } for (i = 0; i < 2; i++) { fd_init(&fdctrl->drives[i], fds[i]); - if (fds[i]) { - bdrv_set_change_cb(fds[i], - &fd_change_cb, &fdctrl->drives[i]); - } } fdctrl_reset(fdctrl, 0); fdctrl->state = FD_CTRL_ACTIVE; if (mem_mapped) { - FLOPPY_ERROR("memory mapped floppy not supported by now !\n"); -#if 0 - io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write); - cpu_register_physical_memory(base, 0x08, io_mem); -#endif + io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl); + cpu_register_physical_memory(io_base, 0x08, io_mem); } else { - register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl); - register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl); - register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl); - register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl); + register_ioport_read((uint32_t)io_base + 0x01, 5, 1, &fdctrl_read, + fdctrl); + register_ioport_read((uint32_t)io_base + 0x07, 1, 1, &fdctrl_read, + fdctrl); + register_ioport_write((uint32_t)io_base + 0x01, 5, 1, &fdctrl_write, + fdctrl); + register_ioport_write((uint32_t)io_base + 0x07, 1, 1, &fdctrl_write, + fdctrl); } + register_savevm("fdc", io_base, 1, fdc_save, fdc_load, fdctrl); + qemu_register_reset(fdctrl_external_reset, fdctrl); for (i = 0; i < 2; i++) { fd_revalidate(&fdctrl->drives[i]); } @@ -524,30 +640,37 @@ int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) /* Change IRQ state */ static void fdctrl_reset_irq (fdctrl_t *fdctrl) { - if (fdctrl->state & FD_CTRL_INTR) { - pic_set_irq(fdctrl->irq_lvl, 0); - fdctrl->state &= ~(FD_CTRL_INTR | FD_CTRL_SLEEP | FD_CTRL_BUSY); - } + FLOPPY_DPRINTF("Reset interrupt\n"); + qemu_set_irq(fdctrl->irq, 0); + fdctrl->state &= ~FD_CTRL_INTR; } static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) { +#ifdef TARGET_SPARC + // Sparc mutation + if (!fdctrl->dma_en) { + fdctrl->state &= ~FD_CTRL_BUSY; + fdctrl->int_status = status; + return; + } +#endif if (~(fdctrl->state & FD_CTRL_INTR)) { - pic_set_irq(fdctrl->irq_lvl, 1); + qemu_set_irq(fdctrl->irq, 1); fdctrl->state |= FD_CTRL_INTR; } FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); fdctrl->int_status = status; } -/* Reset controler */ +/* Reset controller */ static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) { int i; - FLOPPY_DPRINTF("reset controler\n"); + FLOPPY_DPRINTF("reset controller\n"); fdctrl_reset_irq(fdctrl); - /* Initialise controler */ + /* Initialise controller */ fdctrl->cur_drv = 0; /* FIFO state */ fdctrl->data_pos = 0; @@ -558,7 +681,7 @@ static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) fd_reset(&fdctrl->drives[i]); fdctrl_reset_fifo(fdctrl); if (do_irq) - fdctrl_raise_irq(fdctrl, 0x20); + fdctrl_raise_irq(fdctrl, 0xc0); } static inline fdrive_t *drv0 (fdctrl_t *fdctrl) @@ -579,9 +702,7 @@ static fdrive_t *get_cur_drv (fdctrl_t *fdctrl) /* Status B register : 0x01 (read-only) */ static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl) { - fdctrl_reset_irq(fdctrl); FLOPPY_DPRINTF("status register: 0x00\n"); - return 0; } @@ -608,11 +729,10 @@ static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl) static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) { - fdctrl_reset_irq(fdctrl); /* Reset mode */ if (fdctrl->state & FD_CTRL_RESET) { if (!(value & 0x04)) { - FLOPPY_DPRINTF("Floppy controler in RESET state !\n"); + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } } @@ -634,13 +754,13 @@ static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) /* Reset */ if (!(value & 0x04)) { if (!(fdctrl->state & FD_CTRL_RESET)) { - FLOPPY_DPRINTF("controler enter RESET state\n"); + FLOPPY_DPRINTF("controller enter RESET state\n"); fdctrl->state |= FD_CTRL_RESET; - fdctrl_reset(fdctrl, 1); } } else { if (fdctrl->state & FD_CTRL_RESET) { - FLOPPY_DPRINTF("controler out of RESET state\n"); + FLOPPY_DPRINTF("controller out of RESET state\n"); + fdctrl_reset(fdctrl, 1); fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP); } } @@ -653,7 +773,6 @@ static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) { uint32_t retval = 0; - fdctrl_reset_irq(fdctrl); /* Disk boot selection indicator */ retval |= fdctrl->bootsel << 2; /* Tape indicators: never allowed */ @@ -664,10 +783,9 @@ static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value) { - fdctrl_reset_irq(fdctrl); /* Reset mode */ if (fdctrl->state & FD_CTRL_RESET) { - FLOPPY_DPRINTF("Floppy controler in RESET state !\n"); + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); @@ -681,7 +799,6 @@ static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) { uint32_t retval = 0; - fdctrl_reset_irq(fdctrl); fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET); if (!(fdctrl->state & FD_CTRL_BUSY)) { /* Data transfer allowed */ @@ -703,10 +820,9 @@ static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) /* Data select rate register : 0x04 (write) */ static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) { - fdctrl_reset_irq(fdctrl); /* Reset mode */ if (fdctrl->state & FD_CTRL_RESET) { - FLOPPY_DPRINTF("Floppy controler in RESET state !\n"); + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); @@ -723,19 +839,28 @@ static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) // fdctrl.precomp = (value >> 2) & 0x07; } +static int fdctrl_media_changed(fdrive_t *drv) +{ + int ret; + if (!drv->bs) + return 0; + ret = bdrv_media_changed(drv->bs); + if (ret) { + fd_revalidate(drv); + } + return ret; +} + /* Digital input register : 0x07 (read-only) */ static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) { uint32_t retval = 0; - fdctrl_reset_irq(fdctrl); - if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE || - drv1(fdctrl)->drflags & FDRIVE_REVALIDATE) + if (fdctrl_media_changed(drv0(fdctrl)) || + fdctrl_media_changed(drv1(fdctrl))) retval |= 0x80; if (retval != 0) FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); - drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; - drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; return retval; } @@ -766,7 +891,7 @@ static void fdctrl_unimplemented (fdctrl_t *fdctrl) fdrive_t *cur_drv; cur_drv = get_cur_drv(fdctrl); - fdctrl->fifo[0] = 0x60 | (cur_drv->head << 1) | fdctrl->cur_drv; + fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv; fdctrl->fifo[1] = 0x00; fdctrl->fifo[2] = 0x00; fdctrl_set_fifo(fdctrl, 3, 1); @@ -786,8 +911,8 @@ static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, cur_drv = get_cur_drv(fdctrl); FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", status0, status1, status2, - status0 | (cur_drv->head << 1) | fdctrl->cur_drv); - fdctrl->fifo[0] = status0 | (cur_drv->head << 1) | fdctrl->cur_drv; + status0 | (cur_drv->head << 2) | fdctrl->cur_drv); + fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv; fdctrl->fifo[1] = status1; fdctrl->fifo[2] = status2; fdctrl->fifo[3] = cur_drv->track; @@ -795,8 +920,10 @@ static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, fdctrl->fifo[5] = cur_drv->sect; fdctrl->fifo[6] = FD_SECTOR_SC; fdctrl->data_dir = FD_DIR_READ; - if (fdctrl->state & FD_CTRL_BUSY) + if (fdctrl->state & FD_CTRL_BUSY) { DMA_release_DREQ(fdctrl->dma_chann); + fdctrl->state &= ~FD_CTRL_BUSY; + } fdctrl_set_fifo(fdctrl, 7, 1); } @@ -812,7 +939,7 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) kt = fdctrl->fifo[2]; kh = fdctrl->fifo[3]; ks = fdctrl->fifo[4]; - FLOPPY_DPRINTF("Start tranfert at %d %d %02x %02x (%d)\n", + FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", fdctrl->cur_drv, kh, kt, ks, _fd_sector(kh, kt, ks, cur_drv->last_sect)); did_seek = 0; @@ -860,12 +987,13 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) fdctrl->data_len = fdctrl->fifo[8]; } else { int tmp; - fdctrl->data_len = 128 << fdctrl->fifo[5]; + fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); tmp = (cur_drv->last_sect - ks + 1); if (fdctrl->fifo[0] & 0x80) tmp += cur_drv->last_sect; fdctrl->data_len *= tmp; } + fdctrl->eot = fdctrl->fifo[6]; if (fdctrl->dma_en) { int dma_mode; /* DMA transfer are enabled. Check if DMA channel is well programmed */ @@ -881,7 +1009,7 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) (direction == FD_DIR_READ && dma_mode == 1)) { /* No access is allowed until DMA transfer has completed */ fdctrl->state |= FD_CTRL_BUSY; - /* Now, we just have to wait for the DMA controler to + /* Now, we just have to wait for the DMA controller to * recall us... */ DMA_hold_DREQ(fdctrl->dma_chann); @@ -908,7 +1036,8 @@ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) } /* handlers for DMA transfers */ -static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) +static int fdctrl_transfer_handler (void *opaque, int nchan, + int dma_pos, int dma_len) { fdctrl_t *fdctrl; fdrive_t *cur_drv; @@ -916,7 +1045,6 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; fdctrl = opaque; - fdctrl_reset_irq(fdctrl); if (!(fdctrl->state & FD_CTRL_BUSY)) { FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); return 0; @@ -925,26 +1053,26 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || fdctrl->data_dir == FD_DIR_SCANH) status2 = 0x04; - if (size > fdctrl->data_len) - size = fdctrl->data_len; - if (cur_drv->bs == NULL) { + if (dma_len > fdctrl->data_len) + dma_len = fdctrl->data_len; + if (cur_drv->bs == NULL) { if (fdctrl->data_dir == FD_DIR_WRITE) fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); else fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); len = 0; - goto transfer_error; - } + goto transfer_error; + } rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; - for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) { - len = size - fdctrl->data_pos; + for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { + len = dma_len - fdctrl->data_pos; if (len + rel_pos > FD_SECTOR_LEN) len = FD_SECTOR_LEN - rel_pos; - FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x " - "(%d-0x%08x 0x%08x)\n", len, size, fdctrl->data_pos, + FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " + "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, fdctrl->data_len, fdctrl->cur_drv, cur_drv->head, cur_drv->track, cur_drv->sect, fd_sector(cur_drv), - fd_sector(cur_drv) * 512, addr); + fd_sector(cur_drv) * 512); if (fdctrl->data_dir != FD_DIR_WRITE || len < FD_SECTOR_LEN || rel_pos != 0) { /* READ & SCAN commands and realign to a sector for WRITE */ @@ -955,31 +1083,36 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) /* Sure, image size is too small... */ memset(fdctrl->fifo, 0, FD_SECTOR_LEN); } - } + } switch (fdctrl->data_dir) { case FD_DIR_READ: /* READ commands */ - cpu_physical_memory_write(addr + fdctrl->data_pos, - fdctrl->fifo + rel_pos, len); + DMA_write_memory (nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); +/* cpu_physical_memory_write(addr + fdctrl->data_pos, */ +/* fdctrl->fifo + rel_pos, len); */ break; case FD_DIR_WRITE: /* WRITE commands */ - cpu_physical_memory_read(addr + fdctrl->data_pos, - fdctrl->fifo + rel_pos, len); + DMA_read_memory (nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ +/* fdctrl->fifo + rel_pos, len); */ if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); goto transfer_error; - } + } break; default: /* SCAN commands */ { uint8_t tmpbuf[FD_SECTOR_LEN]; int ret; - cpu_physical_memory_read(addr + fdctrl->data_pos, - tmpbuf, len); + DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ +/* tmpbuf, len); */ ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); if (ret == 0) { status2 = 0x08; @@ -997,30 +1130,34 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size) rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; if (rel_pos == 0) { /* Seek to next sector */ - cur_drv->sect++; FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n", cur_drv->head, cur_drv->track, cur_drv->sect, fd_sector(cur_drv), - fdctrl->data_pos - size); - if (cur_drv->sect > cur_drv->last_sect) { + fdctrl->data_pos - len); + /* XXX: cur_drv->sect >= cur_drv->last_sect should be an + error in fact */ + if (cur_drv->sect >= cur_drv->last_sect || + cur_drv->sect == fdctrl->eot) { cur_drv->sect = 1; if (FD_MULTI_TRACK(fdctrl->data_state)) { if (cur_drv->head == 0 && - (cur_drv->flags & FDISK_DBL_SIDES) != 0) { - cur_drv->head = 1; - } else { - cur_drv->head = 0; + (cur_drv->flags & FDISK_DBL_SIDES) != 0) { + cur_drv->head = 1; + } else { + cur_drv->head = 0; cur_drv->track++; if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) break; + } + } else { + cur_drv->track++; + break; } - } else { - cur_drv->track++; - break; - } FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", cur_drv->head, cur_drv->track, cur_drv->sect, fd_sector(cur_drv)); + } else { + cur_drv->sect++; } } } @@ -1036,7 +1173,7 @@ end_transfer: status0 |= 0x20; fdctrl->data_len -= len; // if (fdctrl->data_len == 0) - fdctrl_stop_transfer(fdctrl, status0, status1, status2); + fdctrl_stop_transfer(fdctrl, status0, status1, status2); transfer_error: return len; @@ -1049,7 +1186,6 @@ static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) uint32_t retval = 0; int pos, len; - fdctrl_reset_irq(fdctrl); cur_drv = get_cur_drv(fdctrl); fdctrl->state &= ~FD_CTRL_SLEEP; if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) { @@ -1070,13 +1206,15 @@ static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) retval = fdctrl->fifo[pos]; if (++fdctrl->data_pos == fdctrl->data_len) { fdctrl->data_pos = 0; - /* Switch from transfert mode to status mode + /* Switch from transfer mode to status mode * then from status mode to command mode */ - if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) + if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); - else + } else { fdctrl_reset_fifo(fdctrl); + fdctrl_reset_irq(fdctrl); + } } FLOPPY_DPRINTF("data register: 0x%02x\n", retval); @@ -1130,7 +1268,7 @@ static void fdctrl_format_sector (fdctrl_t *fdctrl) memset(fdctrl->fifo, 0, FD_SECTOR_LEN); if (cur_drv->bs == NULL || bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv)); + FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); } else { if (cur_drv->sect == cur_drv->last_sect) { @@ -1152,11 +1290,10 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) { fdrive_t *cur_drv; - fdctrl_reset_irq(fdctrl); cur_drv = get_cur_drv(fdctrl); /* Reset mode */ if (fdctrl->state & FD_CTRL_RESET) { - FLOPPY_DPRINTF("Floppy controler in RESET state !\n"); + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } fdctrl->state &= ~FD_CTRL_SLEEP; @@ -1173,7 +1310,7 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, FD_SECTOR_LEN); } - /* Switch from transfert mode to status mode + /* Switch from transfer mode to status mode * then from status mode to command mode */ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) @@ -1262,8 +1399,16 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n", fdctrl->int_status); /* No parameters cmd: returns status if no interrupt */ +#if 0 fdctrl->fifo[0] = fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv; +#else + /* XXX: int_status handling is broken for read/write + commands, so we do this hack. It should be suppressed + ASAP */ + fdctrl->fifo[0] = + 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv; +#endif fdctrl->fifo[1] = cur_drv->track; fdctrl_set_fifo(fdctrl, 2, 0); fdctrl_reset_irq(fdctrl); @@ -1297,7 +1442,7 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) /* VERSION */ FLOPPY_DPRINTF("VERSION command\n"); /* No parameters cmd */ - /* Controler's version */ + /* Controller's version */ fdctrl->fifo[0] = fdctrl->version; fdctrl_set_fifo(fdctrl, 1, 1); return; @@ -1499,7 +1644,7 @@ enqueue: /* SPECIFY */ FLOPPY_DPRINTF("treat SPECIFY command\n"); fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; - fdctrl->timer1 = fdctrl->fifo[1] >> 1; + fdctrl->timer1 = fdctrl->fifo[2] >> 1; fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ; /* No result back */ fdctrl_reset_fifo(fdctrl); @@ -1513,7 +1658,9 @@ enqueue: /* 1 Byte status back */ fdctrl->fifo[0] = (cur_drv->ro << 6) | (cur_drv->track == 0 ? 0x10 : 0x00) | - fdctrl->cur_drv; + (cur_drv->head << 2) | + fdctrl->cur_drv | + 0x28; fdctrl_set_fifo(fdctrl, 1, 0); break; case 0x07: @@ -1583,7 +1730,10 @@ enqueue: case 0x4A: /* READ_ID */ FLOPPY_DPRINTF("treat READ_ID command\n"); - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + /* XXX: should set main status register to busy */ + cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; + qemu_mod_timer(fdctrl->result_timer, + qemu_get_clock(vm_clock) + (ticks_per_sec / 50)); break; case 0x4C: /* RESTORE */ @@ -1622,8 +1772,9 @@ enqueue: #else cur_drv->last_sect = fdctrl->fifo[3]; #endif - /* Bochs BIOS is buggy and don't send format informations - * for each sector. So, pretend all's done right now... + /* TODO: implement format using DMA expected by the Bochs BIOS + * and Linux fdformat (read 3 bytes per sector via DMA and fill + * the sector with the specified fill byte */ fdctrl->data_state &= ~FD_STATE_FORMAT; fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); @@ -1688,3 +1839,17 @@ enqueue: } } } + +static void fdctrl_result_timer(void *opaque) +{ + fdctrl_t *fdctrl = opaque; + fdrive_t *cur_drv = get_cur_drv(fdctrl); + /* Pretend we are spinning. + * This is needed for Coherent, which uses READ ID to check for + * sector interleaving. + */ + if (cur_drv->last_sect != 0) { + cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; + } + fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); +}