Index: hw/fdc.c =================================================================== --- hw/fdc.c (revision 4215) +++ hw/fdc.c (working copy) @@ -321,7 +321,7 @@ static void fdctrl_reset_fifo (fdctrl_t *fdctrl); 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_raise_irq (fdctrl_t *fdctrl); static uint32_t fdctrl_read_statusA (fdctrl_t *fdctrl); static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl); @@ -356,7 +356,6 @@ FD_STATE_DATA = 0x02, FD_STATE_STATE = 0x03, FD_STATE_MULTI = 0x10, - FD_STATE_SEEK = 0x20, FD_STATE_FORMAT = 0x40, }; @@ -421,6 +420,15 @@ }; enum { + FD_SR1_EC = 0x80, /* End of cylinder */ +}; + +enum { + FD_SR2_SNS = 0x04, /* Scan not satisfied */ + FD_SR2_SEH = 0x08, /* Scan equal hit */ +}; + +enum { FD_SRA_DIR = 0x01, FD_SRA_nWP = 0x02, FD_SRA_nINDX = 0x04, @@ -479,7 +487,6 @@ #define FD_SET_STATE(state, new_state) \ do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0) #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) -#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK) #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) struct fdctrl_t { @@ -498,13 +505,15 @@ uint8_t dma_en; uint8_t cur_drv; uint8_t bootsel; + uint8_t status0; + uint8_t status1; + uint8_t status2; /* Command FIFO */ uint8_t *fifo; uint32_t data_pos; uint32_t data_len; 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 */ @@ -641,13 +650,17 @@ qemu_put_8s(f, &s->dma_en); qemu_put_8s(f, &s->cur_drv); qemu_put_8s(f, &s->bootsel); + qemu_put_8s(f, &s->status0); + qemu_put_8s(f, &s->status1); + qemu_put_8s(f, &s->status2); + /* Command FIFO */ 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); + /* States kept only to be returned back */ qemu_put_8s(f, &s->timer0); qemu_put_8s(f, &s->timer1); qemu_put_8s(f, &s->precomp_trk); @@ -688,13 +701,17 @@ qemu_get_8s(f, &s->dma_en); qemu_get_8s(f, &s->cur_drv); qemu_get_8s(f, &s->bootsel); + qemu_get_8s(f, &s->status0); + qemu_get_8s(f, &s->status1); + qemu_get_8s(f, &s->status2); + /* Command FIFO */ 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); + /* States kept only to be returned back */ qemu_get_8s(f, &s->timer0); qemu_get_8s(f, &s->timer1); qemu_get_8s(f, &s->precomp_trk); @@ -742,20 +759,18 @@ fdctrl->sra &= ~FD_SRA_INTPEND; } -static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) +static void fdctrl_raise_irq (fdctrl_t *fdctrl) { // Sparc mutation if (fdctrl->sun4m && !fdctrl->dma_en) { fdctrl->state &= ~FD_CTRL_BUSY; - fdctrl->int_status = status; return; } if (!(fdctrl->sra & FD_SRA_INTPEND)) { qemu_set_irq(fdctrl->irq, 1); fdctrl->sra |= FD_SRA_INTPEND; } - FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); - fdctrl->int_status = status; + FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0); } /* Reset controller */ @@ -779,8 +794,10 @@ for (i = 0; i < MAX_FD; i++) fd_reset(&fdctrl->drives[i]); fdctrl_reset_fifo(fdctrl); - if (do_irq) - fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG); + if (do_irq) { + fdctrl->status0 |= FD_SR0_RDYCHG; + fdctrl_raise_irq(fdctrl); + } } static inline fdrive_t *drv0 (fdctrl_t *fdctrl) @@ -1012,7 +1029,7 @@ fdctrl->data_pos = 0; FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS); if (do_irq) - fdctrl_raise_irq(fdctrl, 0x00); + fdctrl_raise_irq(fdctrl); } /* Set an error: unimplemented/unknown command */ @@ -1064,22 +1081,26 @@ } else { cur_drv->sect++; } + fdctrl->status0 |= FD_SR0_SEEK; return 1; } /* Callback for transfer end (stop or abort) */ -static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, - uint8_t status1, uint8_t status2) +static void fdctrl_stop_transfer (fdctrl_t *fdctrl) { fdrive_t *cur_drv; + uint8_t status0 = fdctrl->status0; cur_drv = get_cur_drv(fdctrl); + /* Add head and drive to status0 */ + status0 |= (cur_drv->head << 2) | (fdctrl->cur_drv); + FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", - status0, status1, status2, - 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->status0, fdctrl->status1, fdctrl->status2, + status0); + fdctrl->fifo[0] = status0; + fdctrl->fifo[1] = fdctrl->status1; + fdctrl->fifo[2] = fdctrl->status2; fdctrl->fifo[3] = cur_drv->track; fdctrl->fifo[4] = cur_drv->head; fdctrl->fifo[5] = cur_drv->sect; @@ -1097,7 +1118,6 @@ { fdrive_t *cur_drv; uint8_t kh, kt, ks; - int did_seek; fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; cur_drv = get_cur_drv(fdctrl); @@ -1107,31 +1127,35 @@ 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; - switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { + switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 3: /* track too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x80, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl->status1 |= FD_SR1_EC; + fdctrl_stop_transfer(fdctrl); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 4: /* No seek enabled */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; + case 0: case 1: - did_seek = 1; + fdctrl->status0 |= FD_SR0_SEEK; break; default: break; @@ -1144,10 +1168,6 @@ 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 { @@ -1186,7 +1206,7 @@ } FLOPPY_DPRINTF("start non-DMA transfer\n"); /* IO based transfer: calculate len */ - fdctrl_raise_irq(fdctrl, 0x00); + fdctrl_raise_irq(fdctrl); return; } @@ -1194,10 +1214,13 @@ /* Prepare a transfer of deleted data */ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) { + FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n"); + /* We don't handle deleted data, * so we don't return *ANYTHING* */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); } /* handlers for DMA transfers */ @@ -1207,7 +1230,6 @@ 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)) { @@ -1215,16 +1237,11 @@ 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, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - else - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); len = 0; goto transfer_error; } @@ -1261,8 +1278,9 @@ fdctrl->data_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, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); + FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv)); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); goto transfer_error; } break; @@ -1274,14 +1292,16 @@ DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); if (ret == 0) { - status2 = 0x08; + fdctrl->status2 |= FD_SR2_SEH; + fdctrl->status2 &= ~FD_SR2_SNS; goto end_transfer; } if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { - status2 = 0x00; + fdctrl->status2 &= ~FD_SR2_SNS; goto end_transfer; } + fdctrl->status2 |= FD_SR2_SNS; } break; } @@ -1297,15 +1317,8 @@ 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 |= FD_SR0_SEEK; fdctrl->data_len -= len; - // if (fdctrl->data_len == 0) - fdctrl_stop_transfer(fdctrl, status0, status1, status2); + fdctrl_stop_transfer(fdctrl); transfer_error: return len; @@ -1344,7 +1357,7 @@ * then from status mode to command mode */ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { - fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00); + fdctrl_stop_transfer(fdctrl); } else { fdctrl_reset_fifo(fdctrl); fdctrl_reset_irq(fdctrl); @@ -1359,7 +1372,6 @@ { fdrive_t *cur_drv; uint8_t kh, kt, ks; - int did_seek; fdctrl->cur_drv = fdctrl->fifo[1] & FD_DOR_SELMASK; cur_drv = get_cur_drv(fdctrl); @@ -1369,32 +1381,35 @@ 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 & FD_CONFIG_EIS)) { case 2: /* sect too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 3: /* track too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x80, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl->status1 |= FD_SR1_EC; + fdctrl_stop_transfer(fdctrl); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; case 4: /* No seek enabled */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; + case 0: case 1: - did_seek = 1; - fdctrl->data_state |= FD_STATE_SEEK; + fdctrl->status0 |= FD_SR0_SEEK; break; default: break; @@ -1403,15 +1418,13 @@ if (cur_drv->bs == NULL || bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv)); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); + fdctrl->status0 |= FD_SR0_ABNTERM; + fdctrl_stop_transfer(fdctrl); } 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, FD_SR0_SEEK, 0x00, 0x00); - else - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + fdctrl_stop_transfer(fdctrl); } else { /* More to do */ fdctrl->data_pos = 0; @@ -1525,7 +1538,6 @@ fdctrl->data_state |= FD_STATE_MULTI; else fdctrl->data_state &= ~FD_STATE_MULTI; - fdctrl->data_state &= ~FD_STATE_SEEK; cur_drv->bps = fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; #if 0 @@ -1540,7 +1552,7 @@ * the sector with the specified fill byte */ fdctrl->data_state &= ~FD_STATE_FORMAT; - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + fdctrl_stop_transfer(fdctrl); } static void fdctrl_handle_specify (fdctrl_t *fdctrl, int direction) @@ -1577,27 +1589,20 @@ fd_recalibrate(cur_drv); fdctrl_reset_fifo(fdctrl); /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); + fdctrl->status0 |= FD_SR0_SEEK; + fdctrl_raise_irq(fdctrl); } static void fdctrl_handle_sense_interrupt_status (fdctrl_t *fdctrl, int direction) { fdrive_t *cur_drv = get_cur_drv(fdctrl); -#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->status0 | (cur_drv->head << 2) | fdctrl->cur_drv; fdctrl->fifo[1] = cur_drv->track; fdctrl_set_fifo(fdctrl, 2, 0); fdctrl_reset_irq(fdctrl); - fdctrl->int_status = FD_SR0_RDYCHG; + fdctrl->status0 |= FD_SR0_RDYCHG; } static void fdctrl_handle_seek (fdctrl_t *fdctrl, int direction) @@ -1612,13 +1617,13 @@ else cur_drv->dir = 0; fdctrl_reset_fifo(fdctrl); - if (fdctrl->fifo[2] > cur_drv->max_track) { - fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK); - } else { + fdctrl->status0 |= FD_SR0_SEEK; + if (fdctrl->fifo[2] > cur_drv->max_track) + fdctrl->status0 |= FD_SR0_ABNTERM; + else cur_drv->track = fdctrl->fifo[2]; - /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); - } + /* Raise Interrupt */ + fdctrl_raise_irq(fdctrl); } static void fdctrl_handle_perpendicular_mode (fdctrl_t *fdctrl, int direction) @@ -1688,7 +1693,8 @@ cur_drv->track += fdctrl->fifo[2]; } fdctrl_reset_fifo(fdctrl); - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); + fdctrl->status0 |= FD_SR0_SEEK; + fdctrl_raise_irq(fdctrl); } static void fdctrl_handle_relative_seek_in (fdctrl_t *fdctrl, int direction) @@ -1706,7 +1712,8 @@ } fdctrl_reset_fifo(fdctrl); /* Raise Interrupt */ - fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); + fdctrl->status0 |= FD_SR0_SEEK; + fdctrl_raise_irq(fdctrl); } static const struct { @@ -1786,7 +1793,7 @@ * then from status mode to command mode */ if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) - fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00); + fdctrl_stop_transfer(fdctrl); return; } if (fdctrl->data_pos == 0) { @@ -1807,6 +1814,15 @@ return; } + if (fdctrl->fifo[0] != FD_CMD_SENSE_INTERRUPT_STATUS) + { + /* Reset status0, except for SENSE_INTERRUPT_STATUS + * which returns it */ + fdctrl->status0 = 0; + } + fdctrl->status1 = 0; + fdctrl->status2 = 0; + pos = command_to_handler[fdctrl->fifo[0] & 0xff]; FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name); (*handlers[pos].handler)(fdctrl, handlers[pos].parameter); @@ -1825,7 +1841,7 @@ if (cur_drv->last_sect != 0) { cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; } - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + fdctrl_stop_transfer(fdctrl); } /* Init functions */