📄 fdc.c
字号:
fdctrl->data_dir = direction; fdctrl->data_pos = 0; FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */ if (fdctrl->fifo[0] & 0x80) fdctrl->data_state |= FD_STATE_MULTI; else fdctrl->data_state &= ~FD_STATE_MULTI; if (did_seek) fdctrl->data_state |= FD_STATE_SEEK; else fdctrl->data_state &= ~FD_STATE_SEEK; if (fdctrl->fifo[5] == 00) { fdctrl->data_len = fdctrl->fifo[8]; } else { int tmp; 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 */ dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); dma_mode = (dma_mode >> 2) & 3; FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", dma_mode, direction, (128 << fdctrl->fifo[5]) * (cur_drv->last_sect - ks + 1), fdctrl->data_len); if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || direction == FD_DIR_SCANH) && dma_mode == 0) || (direction == FD_DIR_WRITE && dma_mode == 2) || (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 controller to * recall us... */ DMA_hold_DREQ(fdctrl->dma_chann); DMA_schedule(fdctrl->dma_chann); return; } else { FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction); } } FLOPPY_DPRINTF("start non-DMA transfer\n"); /* IO based transfer: calculate len */ fdctrl_raise_irq(fdctrl, 0x00); return;}/* Prepare a transfer of deleted data */static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction){ /* We don't handle deleted data, * so we don't return *ANYTHING* */ fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);}/* handlers for DMA transfers */static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len){ fdctrl_t *fdctrl; fdrive_t *cur_drv; int len, start_pos, rel_pos; uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; fdctrl = opaque; if (!(fdctrl->state & FD_CTRL_BUSY)) { FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); return 0; } cur_drv = get_cur_drv(fdctrl); if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || fdctrl->data_dir == FD_DIR_SCANH) status2 = 0x04; 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; } rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; 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 " "(%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); if (fdctrl->data_dir != FD_DIR_WRITE || len < FD_SECTOR_LEN || rel_pos != 0) { /* READ & SCAN commands and realign to a sector for WRITE */ if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_DPRINTF("Floppy: error getting sector %d\n", fd_sector(cur_drv)); /* Sure, image size is too small... */ memset(fdctrl->fifo, 0, FD_SECTOR_LEN); } } switch (fdctrl->data_dir) { case FD_DIR_READ: /* READ commands */ 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 */ 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; 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; goto end_transfer; } if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { status2 = 0x00; goto end_transfer; } } break; } fdctrl->data_pos += len; rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; if (rel_pos == 0) { /* Seek to next sector */ 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 - 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->track++; if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) 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++; } } }end_transfer: len = fdctrl->data_pos - start_pos; FLOPPY_DPRINTF("end transfer %d %d %d\n", fdctrl->data_pos, len, fdctrl->data_len); if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || fdctrl->data_dir == FD_DIR_SCANH) status2 = 0x08; if (FD_DID_SEEK(fdctrl->data_state)) status0 |= 0x20; fdctrl->data_len -= len; // if (fdctrl->data_len == 0) fdctrl_stop_transfer(fdctrl, status0, status1, status2);transfer_error: return len;}/* Data register : 0x05 */static uint32_t fdctrl_read_data (fdctrl_t *fdctrl){ fdrive_t *cur_drv; uint32_t retval = 0; int pos, len; cur_drv = get_cur_drv(fdctrl); fdctrl->state &= ~FD_CTRL_SLEEP; if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) { FLOPPY_ERROR("can't read data in CMD state\n"); return 0; } pos = fdctrl->data_pos; if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { pos %= FD_SECTOR_LEN; if (pos == 0) { len = fdctrl->data_len - fdctrl->data_pos; if (len > FD_SECTOR_LEN) len = FD_SECTOR_LEN; if (cur_drv->bs) { bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, len); } else { FLOPPY_ERROR("can't read data from drive\n"); return 0; } } } retval = fdctrl->fifo[pos]; if (++fdctrl->data_pos == fdctrl->data_len) { fdctrl->data_pos = 0; /* Switch from transfer mode to status mode * then from status mode to command mode */ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); } else { fdctrl_reset_fifo(fdctrl); fdctrl_reset_irq(fdctrl); } } FLOPPY_DPRINTF("data register: 0x%02x\n", retval); return retval;}static void fdctrl_format_sector (fdctrl_t *fdctrl){ fdrive_t *cur_drv; uint8_t kh, kt, ks; int did_seek; fdctrl->cur_drv = fdctrl->fifo[1] & 1; cur_drv = get_cur_drv(fdctrl); kt = fdctrl->fifo[6]; kh = fdctrl->fifo[7]; ks = fdctrl->fifo[8]; FLOPPY_DPRINTF("format sector 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; switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { case 2: /* sect too big */ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 3: /* track too big */ fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 4: /* No seek enabled */ fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 1: did_seek = 1; fdctrl->data_state |= FD_STATE_SEEK; break; default: break; } 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)); fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); } else { if (cur_drv->sect == cur_drv->last_sect) { fdctrl->data_state &= ~FD_STATE_FORMAT; /* Last sector done */ if (FD_DID_SEEK(fdctrl->data_state)) fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); else fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); } else { /* More to do */ fdctrl->data_pos = 0; fdctrl->data_len = 4; } }}static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value){ fdrive_t *cur_drv; cur_drv = get_cur_drv(fdctrl); /* Reset mode */ if (fdctrl->state & FD_CTRL_RESET) { FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } fdctrl->state &= ~FD_CTRL_SLEEP; if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) { FLOPPY_ERROR("can't write data in status mode\n"); return; } /* Is it write command time ? */ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { /* FIFO data write */ fdctrl->fifo[fdctrl->data_pos++] = value; if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) || fdctrl->data_pos == fdctrl->data_len) { bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, FD_SECTOR_LEN); } /* Switch from transfer mode to status mode * then from status mode to command mode */ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); return; } if (fdctrl->data_pos == 0) { /* Command */ switch (value & 0x5F) { case 0x46: /* READ variants */ FLOPPY_DPRINTF("READ command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; case 0x4C: /* READ_DELETED variants */ FLOPPY_DPRINTF("READ_DELETED command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; case 0x50: /* SCAN_EQUAL variants */ FLOPPY_DPRINTF("SCAN_EQUAL command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; case 0x56: /* VERIFY variants */ FLOPPY_DPRINTF("VERIFY command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; case 0x59: /* SCAN_LOW_OR_EQUAL variants */ FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; case 0x5D: /* SCAN_HIGH_OR_EQUAL variants */ FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; default: break; } switch (value & 0x7F) { case 0x45: /* WRITE variants */ FLOPPY_DPRINTF("WRITE command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; case 0x49: /* WRITE_DELETED variants */ FLOPPY_DPRINTF("WRITE_DELETED command\n"); /* 8 parameters cmd */ fdctrl->data_len = 9; goto enqueue; default: break; } switch (value) { case 0x03: /* SPECIFY */ FLOPPY_DPRINTF("SPECIFY command\n"); /* 1 parameter cmd */ fdctrl->data_len = 3; goto enqueue; case 0x04: /* SENSE_DRIVE_STATUS */ FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n"); /* 1 parameter cmd */ fdctrl->data_len = 2; goto enqueue; case 0x07: /* RECALIBRATE */ FLOPPY_DPRINTF("RECALIBRATE command\n"); /* 1 parameter cmd */ fdctrl->data_len = 2; goto enqueue; case 0x08: /* SENSE_INTERRUPT_STATUS */ 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] =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -