示例#1
0
static pc8477_state_t pc8477_execute(pc8477_t *drv)
{
    int res = -1;

    switch (drv->command) {
        case PC8477_CMD_SPECIFY:
            debug((pc8477_log, "SPECIFY %d, %d, %d, %d", drv->cmd[1] >> 4, drv->cmd[1] & 0xf, drv->cmd[2] >> 1, drv->cmd[2] & 1));
            drv->step_rate = drv->cmd[1] >> 4;
            drv->motor_off_time = drv->cmd[1] & 0xf;
            drv->motor_on_time = drv->cmd[2] >> 1;
            drv->nodma = drv->cmd[2] & 1;
            return PC8477_WAIT;
        case PC8477_CMD_SENSE_INTERRUPT:
            if (!drv->irq) {
                break;
            }
            debug((pc8477_log, "SENSE INTERRUPT"));
            drv->irq = 0;
            drv->current->seeking = 0; /* TODO: Too early */
            return PC8477_RESULT;
        case PC8477_CMD_VERSION:
            if (!drv->is8477) {
                break;
            }
            debug((pc8477_log, "VERSION"));
            return PC8477_RESULT;
        case PC8477_CMD_NSC:
            if (!drv->is8477) {
                break;
            }
            debug((pc8477_log, "NSC"));
            return PC8477_RESULT;
        case PC8477_CMD_SENSE_DRIVE_STATUS:
            debug((pc8477_log, "SENSE DRIVE STATUS #%d", drv->current->num));
            return PC8477_RESULT;
        case PC8477_CMD_READ_ID:
            switch (drv->int_step) {
                case 0:
                    debug((pc8477_log, "READ ID #%d", drv->current->num));
                    drv->st[1] |= PC8477_ST1_MA;
                    drv->sub_step = 0;
                    drv->int_step++;
                case 1:
                    while (*drv->mycontext->clk_ptr >= drv->clk + BYTE_RATE) {
                        res = pc8477_micro_find_sync(drv);
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (res == 0xfe) {
                            break;
                        }
                    }
                    if (res != 0xfe) {
                        break;
                    }
                    drv->sub_step = 0;
                    drv->int_step++;
                case 2:
                    res = pc8477_micro_readid(drv);
                    if (res > 0) {
                        break;
                    }
                    if (res < 0) {
                        drv->st[0] |= 0x40;
                    }
                    return PC8477_RESULT;
            }
            return PC8477_EXEC;
        case PC8477_CMD_RECALIBRATE:
            debug((pc8477_log, "RECALIBRATE #%d", drv->current->num));
            drv->current->seek_pulses = drv->is8477 ? -77 : -85;
            drv->current->track = 0;
            drv->current->recalibrating = 1;
            if (!drv->seeking_active) {
                alarm_set(drv->seek_alarm, *drv->mycontext->clk_ptr + STEP_RATE);
                drv->seeking_active = 1;
            }
            return PC8477_WAIT;
        case PC8477_CMD_SEEK:
            debug((pc8477_log, "SEEK #%d %d", drv->current->num, drv->cmd[2]));
            drv->current->seek_pulses = drv->cmd[2] - drv->current->track;
            drv->current->track = drv->cmd[2];
            drv->current->recalibrating = 0;
            if (!drv->seeking_active) {
                alarm_set(drv->seek_alarm, *drv->mycontext->clk_ptr + STEP_RATE);
                drv->seeking_active = 1;
            }
            return PC8477_WAIT;
        case PC8477_CMD_DUMPREG:
            if (!drv->is8477) {
                break;
            }
            debug((pc8477_log, "DUMPREG"));
            return PC8477_RESULT;
        case PC8477_CMD_PERPENDICULAR_MODE:
            if (!drv->is8477) {
                break;
            }
            debug((pc8477_log, "PERPENDICULAR MODE %02x", drv->cmd[1]));
            if (drv->cmd[1] & 0x80) {
                for (res = 0; res < 4; res++) {
                    drv->fdds[res].perpendicular = (drv->cmd[1] >> (2 + res)) & 1;
                }
            }
            return PC8477_WAIT;
        case PC8477_CMD_SET_TRACK:
            if ((drv->cmd[1] & 0xf8) != 0x30) {
                break;
            }
            debug((pc8477_log, "SET TRACK #%d %d", drv->current->num, drv->cmd[2]));
            if (drv->cmd[0] & 0x40) {
                if (drv->cmd[1] & 4) {
                    drv->current->track = (drv->current->track & 0xff) | (drv->cmd[2] << 8);
                } else {
                    drv->current->track = (drv->current->track & 0xff00) | drv->cmd[2];
                }
            }
            return PC8477_RESULT;
        case PC8477_CMD_READ_DATA:
            while (*drv->mycontext->clk_ptr >= drv->clk + BYTE_RATE) {
                switch (drv->int_step) {
                    case 0:
                        debug((pc8477_log, "READ DATA #%d (%d/%d/%d)-%d %d", drv->current->num, drv->cmd[2], drv->cmd[3], drv->cmd[4], drv->cmd[6], 128 << drv->cmd[5]));
                        drv->sector = drv->cmd[4];
                        drv->st[1] |= PC8477_ST1_MA;
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 1:
                        res = pc8477_micro_find_sync(drv);
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (res != 0xfe) {
                            break;
                        }
                        drv->st[1] &= ~PC8477_ST1_MA;
                        drv->st[1] |= PC8477_ST1_ND;
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 2:
                        res = pc8477_micro_readid(drv);
                        if (res > 0) {
                            break;
                        }
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (0xff == drv->res[3]) {
                            drv->st[2] = PC8477_ST2_BT;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (drv->cmd[2] != drv->res[3]) {
                            drv->st[2] = PC8477_ST2_WT;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (drv->cmd[3] != drv->res[4] || drv->sector != drv->res[5]
                            || drv->cmd[5] != drv->res[6]) {
                            drv->sub_step = 0;
                            drv->int_step = 1;
                            break;
                        }
                        drv->byte_count = 128 << drv->res[6];
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 3:
                        res = pc8477_micro_find_sync(drv);
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (res == 0x200) {
                            break;
                        }
                        if (res == 0xf8) {
                            drv->st[2] |= PC8477_ST2_CM;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (res != 0xfb) {
                            drv->st[2] |= PC8477_ST2_MD;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        drv->st[1] &= ~PC8477_ST1_ND;
                        drv->int_step++;
                        break;
                    case 4:
                        drv->clk += BYTE_RATE;
                        drv->fifo[drv->fifop2] = (BYTE)fdd_read(drv->fdd);
                        drv->fifop2++;
                        if (drv->fifop2 >= drv->fifo_size) {
                            drv->fifop2 = 0;
                        }
                        if (drv->fifo_fill >= drv->fifo_size) {
                            debug((pc8477_log, "Overrun"));
                            drv->st[1] |= PC8477_ST1_OR;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        } else {
                            drv->fifo_fill++;
                        }
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                    case 5:
                        if (drv->fifo_fill) {
                            drv->clk += fdd_rotate(drv->fdd, (*drv->mycontext->clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                            return PC8477_READ;
                        }
                        if (drv->cmd[6] != drv->sector) {
                            drv->sector++;
                            fdd_index_count_reset(drv->fdd);
                            drv->sub_step = 0;
                            drv->int_step = 1;
                            break;
                        }
                        drv->st[1] |= PC8477_ST1_EOT;
                        drv->st[0] |= 0x40;
                        return PC8477_RESULT;
                }
            }
            return PC8477_READ;
        case PC8477_CMD_WRITE_DATA:
            while (*drv->mycontext->clk_ptr >= drv->clk + BYTE_RATE) {
                switch (drv->int_step) {
                    case 0:
                        debug((pc8477_log, "WRITE DATA #%d (%d/%d/%d)-%d %d", drv->current->num, drv->cmd[2], drv->cmd[3], drv->cmd[4], drv->cmd[6], 128 << drv->cmd[5]));
                        drv->st[1] |= PC8477_ST1_MA;
                        drv->sector = drv->cmd[4];
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 1:
                        res = pc8477_micro_find_sync(drv);
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (res != 0xfe) {
                            break;
                        }
                        drv->st[1] &= ~PC8477_ST1_MA;
                        drv->st[1] |= PC8477_ST1_ND;
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 2:
                        res = pc8477_micro_readid(drv);
                        if (res > 0) {
                            break;
                        }
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (0xff == drv->res[3]) {
                            drv->st[2] = PC8477_ST2_BT;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (drv->cmd[2] != drv->res[3]) {
                            drv->st[2] = PC8477_ST2_WT;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (fdd_write_protect(drv->fdd)) {
                            drv->st[1] |= PC8477_ST1_NW;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (drv->cmd[3] != drv->res[4] || drv->sector != drv->res[5]
                            || drv->cmd[5] != drv->res[6]) {
                            drv->sub_step = 0;
                            drv->int_step = 1;
                            break;
                        }
                        drv->byte_count = 128 << drv->res[6];
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 3:
                        res = pc8477_micro_find_sync(drv);
                        if (res < 0) {
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        if (res == 0x200) {
                            break;
                        }
                        if (res != 0xfb) {
                            drv->st[2] |= 0x01; /* no data mark */
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        drv->st[1] &= ~PC8477_ST1_ND;
                        drv->int_step++;
                        break;
                    case 4:
                        if (drv->fifo_fill == 0) {
                            debug((pc8477_log, "Underrun"));
                            drv->st[1] |= PC8477_ST1_OR;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        fdd_write(drv->fdd, drv->fifo[drv->fifop2]);
                        drv->clk += BYTE_RATE;
                        drv->fifop2++;
                        if (drv->fifop2 >= drv->fifo_size) {
                            drv->fifop2 = 0;
                        }
                        drv->fifo_fill--;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                    case 5:
                        if (drv->cmd[6] != drv->sector) {
                            drv->sector++;
                            fdd_index_count_reset(drv->fdd);
                            drv->sub_step = 0;
                            drv->int_step = 1;
                            break;
                        }
                        drv->st[1] |= 0x80; /* end of track */
                        drv->st[0] |= 0x40;
                        return PC8477_RESULT;
                }
            }
            return PC8477_WRITE;
        case PC8477_CMD_FORMAT_A_TRACK:
            while (*drv->mycontext->clk_ptr >= drv->clk + BYTE_RATE) {
                switch (drv->int_step) {
                    case 0:
                        debug((pc8477_log, "FORMAT TRACK #%d %d %d*%d %d %02x", drv->current->num, (drv->cmd[1] >> 2) & 1, 128 << drv->cmd[2], drv->cmd[3], drv->cmd[4], drv->cmd[5]));
                        drv->sector = 0;
                        drv->sub_step = 0;
                        drv->int_step++;
                    case 1:
                        drv->clk += BYTE_RATE;
                        fdd_read(drv->fdd);
                        if (!fdd_index_count(drv->fdd)) {
                            break;
                        }
                        if (fdd_write_protect(drv->fdd)) {
                            drv->st[1] |= PC8477_ST1_NW;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        drv->int_step++;
                        drv->byte_count = 80;
                        break;
                    case 2:
                        fdd_write(drv->fdd, 0x4e);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->byte_count = 12;
                        drv->int_step++;
                        break;
                    case 3:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->byte_count = 3;
                        drv->int_step++;
                        break;
                    case 4:
                        fdd_write(drv->fdd, 0x1a1);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                        break;
                    case 5:
                        fdd_write(drv->fdd, 0xfc);
                        drv->clk += BYTE_RATE;
                        drv->byte_count = 50;
                        drv->int_step++;
                        break;
                    case 6:
                        fdd_write(drv->fdd, 0x4e);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->byte_count = 12;
                        drv->int_step++;
                        break;
                    case 7:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->byte_count = 3;
                        drv->int_step++;
                        break;
                    case 8:
                        fdd_write(drv->fdd, 0x1a1);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                        break;
                    case 9:
                        fdd_write(drv->fdd, 0xfe);
                        drv->clk += BYTE_RATE;
                        drv->byte_count = 4;
                        drv->int_step++;
                        break;
                    case 10:
                        if (drv->fifo_fill == 0) {
                            debug((pc8477_log, "Underrun"));
                            drv->st[1] |= PC8477_ST1_OR;
                            drv->st[0] |= 0x40;
                            return PC8477_RESULT;
                        }
                        fdd_write(drv->fdd, drv->fifo[drv->fifop2]);
                        drv->clk += BYTE_RATE;
                        drv->fifop2++;
                        if (drv->fifop2 >= drv->fifo_size) {
                            drv->fifop2 = 0;
                        }
                        drv->fifo_fill--;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                        break;
                    case 11:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->int_step++;
                        break;
                    case 12:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->byte_count = (drv->rate == 1000 && drv->current->perpendicular) ? 41 : 22;
                        drv->int_step++;
                        break;
                    case 13:
                        fdd_write(drv->fdd, 0x4e);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->byte_count = 12;
                        drv->int_step++;
                        break;
                    case 14:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->byte_count = 3;
                        drv->int_step++;
                        break;
                    case 15:
                        fdd_write(drv->fdd, 0x1a1);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                        break;
                    case 16:
                        fdd_write(drv->fdd, 0xfb);
                        drv->clk += BYTE_RATE;
                        drv->byte_count = 128 << drv->cmd[2];
                        drv->int_step++;
                        break;
                    case 17:
                        fdd_write(drv->fdd, drv->cmd[5]);
                        drv->clk += BYTE_RATE;
                        drv->byte_count--;
                        if (drv->byte_count) {
                            break;
                        }
                        drv->int_step++;
                        break;
                    case 18:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->int_step++;
                        break;
                    case 19:
                        fdd_write(drv->fdd, 0x00);
                        drv->clk += BYTE_RATE;
                        drv->byte_count = drv->cmd[4];
                        drv->sector++;
                        if (drv->sector < drv->cmd[3]) {
                            drv->int_step = 6;
                            break;
                        }
                        drv->int_step++;
                        break;
                    case 20:
                        fdd_write(drv->fdd, 0x4e);
                        drv->clk += BYTE_RATE;
                        break;
                }
                if (fdd_index_count(drv->fdd) > 1) {
                    drv->clk += fdd_rotate(drv->fdd, (*drv->mycontext->clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                    drv->cmd[3] = drv->sector;
                    drv->st[0] |= 0x40;
                    return PC8477_RESULT;
                }
            }
            return PC8477_WRITE;
        default:
            break;
    }
    debug((pc8477_log, "invalid command %02x", drv->cmd[0]));
    drv->command = PC8477_CMD_INVALID;
    drv->st[0] = drv->st[3] | 0x80; /* invalid command */
    drv->res_size = 1;
    return PC8477_RESULT;
}
示例#2
0
文件: wd1770.c 项目: AreaScout/vice
/* Execute microcode */
static void wd1770_execute(wd1770_t *drv)
{
    int res;

    for (;; ) {
        switch (drv->type) {
            case -1:
                drv->status &= ~(WD_WP | WD_IP | WD_T0);
                drv->status |= fdd_index(drv->fdd) ? WD_IP : 0;
                drv->status |= fdd_track0(drv->fdd) ? WD_T0 : 0;
                drv->status |= fdd_write_protect(drv->fdd) ? WD_WP : 0;
            case 0: /* idle */
                if (*drv->cpu_clk_ptr < drv->clk + PREPARE) {
                    return;
                }
                drv->status &= ~WD_BSY;
                drv->clk += fdd_rotate(drv->fdd, (*drv->cpu_clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                if (fdd_index_count(drv->fdd) >= 10) {
                    drv->status &= ~WD_MO;
                }
                if ((drv->cmd & WD_I2) && fdd_index_count(drv->fdd) != drv->tmp) {
                    drv->irq = 1;
                    drv->tmp = fdd_index_count(drv->fdd);
                }
                return;
            case 1: /* type 1 */
                switch (drv->step) {
                    case 0:
                        if (*drv->cpu_clk_ptr < drv->clk + PREPARE) {
                            return;
                        }
                        drv->clk += PREPARE;
                        drv->status |= WD_BSY;
                        drv->status &= ~(WD_CRC | WD_SE | WD_DRQ);
                        drv->irq = 0;
                        drv->step++;
                    case 1:
                        if ((drv->cmd & WD_H) || (drv->status & WD_MO)) {
                            drv->status |= WD_MO;
                            drv->step += 2;
                            continue;
                        }
                        drv->status |= WD_MO;
                        fdd_index_count_reset(drv->fdd);
                        drv->step++;
                    case 2:
                        drv->clk += fdd_rotate(drv->fdd, (*drv->cpu_clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                        if (fdd_index_count(drv->fdd) < 6) {
                            return;
                        }
                        drv->step++;
                    case 3:
                        switch (drv->command) {
                            case WD_STEP:
                                break;
                            case WD_STEP_IN:
                                drv->direction = 1;
                                break;
                            case WD_STEP_OUT:
                                drv->direction = 0;
                                break;
                            case WD_RESTORE:
                                drv->track = 0xff;
                                drv->data = 0x00;
                            default:
                                drv->step++;
                                continue;
                        }
                        drv->step = (drv->cmd & WD_U) ? 5 : 6;
                        continue;
                    case 4:
                        if (drv->data == drv->track) {
                            drv->step = 8;
                            continue;
                        }
                        drv->direction = (drv->data > drv->track);
                        drv->step++;
                    case 5:
                        drv->track += drv->direction ? 1 : -1;
                        drv->step++;
                    case 6:
                        if (fdd_track0(drv->fdd) && !drv->direction) {
                            drv->track = 0;
                            drv->step = 8;
                            continue;
                        }
                        fdd_seek_pulse(drv->fdd, drv->direction);
                        drv->step++;
                    case 7:
                        if (*drv->cpu_clk_ptr < drv->clk + STEP_RATE) {
                            return;
                        }
                        drv->clk += STEP_RATE;
                        if (drv->cmd < WD_STEP) {
                            drv->step = 4;
                            continue;
                        }
                        drv->step++;
                    case 8:
                        if (!(drv->cmd & WD_V)) {
                            drv->type = -1;
                            break;
                        }
                        drv->step++;
                    case 9:
                        if (*drv->cpu_clk_ptr < drv->clk + SETTLING) {
                            return;
                        }
                        drv->clk += SETTLING;
                        fdd_index_count_reset(drv->fdd);
                        drv->sync = 0;
                        drv->step++;
                    case 10:
                        if (fdd_index_count(drv->fdd) >= 6) {
                            drv->status |= WD_SE;
                            drv->type = -1;
                            break;
                        }
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (!drv->dden || res != 0x1fe) {
                            if (!drv->sync || res != 0xfe) {
                                drv->sync = (res == 0x1a1);
                                continue;
                            }
                        }
                        drv->sync = 0;
                        drv->crc = 0xb230;
                        drv->byte_count = 6;
                        drv->step++;
                    case 11:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (drv->byte_count == 6 && res != drv->track) {
                            drv->step--;
                            continue;
                        }
                        drv->crc = fdd_crc(drv->crc, (BYTE)res);
                        if (--drv->byte_count) {
                            continue;
                        }
                        if (drv->crc) {
                            drv->status |= WD_CRC;
                            drv->step--;
                            continue;
                        }
                        drv->status &= ~WD_CRC;
                        drv->type = -1;
                        break;
                }
                break;
            case 2: /* type 2 */
                switch (drv->step) {
                    case 0:
                        if (*drv->cpu_clk_ptr < drv->clk + PREPARE) {
                            return;
                        }
                        drv->clk += PREPARE;
                        drv->status |= WD_BSY;
                        drv->status &= ~(WD_DRQ | WD_LD | WD_RNF | WD_RT | WD_WP);
                        drv->step++;
                    case 1:
                        if ((drv->cmd & WD_H) || (drv->status & WD_MO)) {
                            drv->status |= WD_MO;
                            drv->step += 2;
                            continue;
                        }
                        drv->status |= WD_MO;
                        fdd_index_count_reset(drv->fdd);
                        drv->step++;
                    case 2:
                        drv->clk += fdd_rotate(drv->fdd, (*drv->cpu_clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                        if (fdd_index_count(drv->fdd) < 6) {
                            return;
                        }
                        drv->step++;
                    case 3:
                        if (!(drv->cmd & WD_E)) {
                            drv->step += 2;
                            continue;
                        }
                        drv->step++;
                    case 4:
                        if (*drv->cpu_clk_ptr < drv->clk + SETTLING) {
                            return;
                        }
                        drv->clk += SETTLING;
                        drv->step++;
                    case 5:
                        if (drv->command == WD_WRITE_SECTOR && fdd_write_protect(drv->fdd)) {
                            drv->status |= WD_WP;
                            drv->type = 0;
                            break;
                        }
                        fdd_index_count_reset(drv->fdd);
                        drv->sync = 0;
                        drv->step++;
                    case 6:
                        if (fdd_index_count(drv->fdd) >= 5) {
                            drv->status |= WD_RNF;
                            drv->type = 0;
                            break;
                        }
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (!drv->dden || res != 0x1fe) {
                            if (!drv->sync || res != 0xfe) {
                                drv->sync = (res == 0x1a1);
                                continue;
                            }
                        }
                        drv->sync = 0;
                        drv->crc = 0xb230;
                        drv->byte_count = 6;
                        drv->step++;
                    case 7:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (drv->byte_count == 6 && res != drv->track) {
                            drv->step--;
                            continue;
                        }
                        if (drv->byte_count == 4 && res != drv->sector) {
                            drv->step--;
                            continue;
                        }
                        if (drv->byte_count == 3) {
                            drv->tmp = res;
                        }
                        drv->crc = fdd_crc(drv->crc, (BYTE)res);
                        if (--drv->byte_count) {
                            continue;
                        }
                        if (drv->crc) {
                            drv->status |= WD_CRC;
                            drv->step--;
                            continue;
                        }
                        drv->status &= ~WD_CRC;
                        drv->crc = 0xffff;
                        if (drv->command == WD_WRITE_SECTOR) {
                            drv->byte_count = 0;
                            drv->step = 10;
                            continue;
                        }
                        drv->byte_count = 43;
                        drv->step++;
                    case 8:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        if (!drv->byte_count--) {
                            drv->step -= 2;
                            continue;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (!drv->dden || (res != 0x1fb && res != 0x1f8)) {
                            if (!drv->sync || (res != 0xfb && res != 0xf8)) {
                                if (!drv->sync) {
                                    drv->crc = 0xffff;
                                }
                                drv->crc = fdd_crc(drv->crc, (BYTE)res);
                                drv->sync = (res == 0x1a1);
                                continue;
                            }
                        }
                        drv->crc = fdd_crc(drv->crc, (BYTE)res);
                        drv->status |= ((res & 0xff) == 0xf8) ? WD_RT : 0;
                        drv->byte_count = (128 << drv->tmp) + 2;
                        drv->step++;
                    case 9:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (drv->byte_count > 2) {
                            drv->status |= (drv->status & WD_DRQ) ? WD_LD : WD_DRQ;
                            drv->data = res;
                        }
                        drv->crc = fdd_crc(drv->crc, (BYTE)res);
                        if (--drv->byte_count) {
                            continue;
                        }
                        if (drv->crc) {
                            drv->status |= WD_CRC;
                            drv->type = 0;
                            break;
                        }
                        if (drv->cmd & WD_M) {
                            drv->sector++;
                            drv->step = 5;
                            continue;
                        }
                        drv->type = 0;
                        break;
                    case 10:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        drv->byte_count++;
                        drv->status |= (drv->byte_count == 2) ? WD_DRQ : 0;
                        if (drv->byte_count == (2 + 9) && (drv->status & WD_DRQ)) {
                            drv->status ^= WD_DRQ | WD_LD;
                            drv->type = 0;
                            break;
                        }
                        if (drv->byte_count <= (drv->dden ? 0 : 11) + 2 + 9) {
                            fdd_read(drv->fdd);
                            continue;
                        }
                        if (drv->byte_count <= (drv->dden ? 6 : (11 + 12)) + 2 + 9) {
                            fdd_write(drv->fdd, 0);
                            continue;
                        }
                        if (!drv->dden && drv->byte_count <= (11 + 12 + 2 + 9 + 3)) {
                            fdd_write(drv->fdd, 0x1a1);
                            drv->crc = fdd_crc(drv->crc, 0xa1);
                            continue;
                        }
                        res = ((drv->cmd & WD_A) ? 0xf8 : 0xfb) | (drv->dden ? 0x100 : 0);
                        fdd_write(drv->fdd, (BYTE)res);
                        drv->crc = fdd_crc(drv->crc, (BYTE)res);
                        drv->byte_count = (128 << drv->tmp) + 3;
                        drv->step++;
                    case 11:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        switch (--drv->byte_count) {
                            case 0:
                                fdd_write(drv->fdd, 0xff);
                                break;
                            case 1:
                                fdd_write(drv->fdd, (BYTE)(drv->crc & 0xff));
                                continue;
                            case 2:
                                fdd_write(drv->fdd, (BYTE)(drv->crc >> 8));
                                continue;
                            default:
                                drv->status |= (drv->status & WD_DRQ) ? WD_LD : WD_DRQ;
                                drv->crc = fdd_crc(drv->crc, drv->data);
                                fdd_write(drv->fdd, drv->data);
                                drv->data = 0;
                                continue;
                        }
                        if (drv->cmd & WD_M) {
                            drv->sector++;
                            drv->step = 5;
                            continue;
                        }
                        drv->type = 0;
                        break;
                }
                break;
            case 3: /* type 3 */
                switch (drv->step) {
                    case 0:
                        if (*drv->cpu_clk_ptr < drv->clk + PREPARE) {
                            return;
                        }
                        drv->clk += PREPARE;
                        drv->status |= WD_BSY;
                        drv->status &= ~(WD_DRQ | WD_LD | WD_RNF | WD_CRC);
                        drv->step++;
                    case 1:
                        if ((drv->cmd & WD_H) || (drv->status & WD_MO)) {
                            drv->status |= WD_MO;
                            drv->step += 2;
                            continue;
                        }
                        drv->status |= WD_MO;
                        fdd_index_count_reset(drv->fdd);
                        drv->step++;
                    case 2:
                        drv->clk += fdd_rotate(drv->fdd, (*drv->cpu_clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                        if (fdd_index_count(drv->fdd) < 6) {
                            return;
                        }
                        drv->step++;
                    case 3:
                        if (!(drv->cmd & WD_E)) {
                            drv->step += 2;
                            continue;
                        }
                        drv->step++;
                    case 4:
                        if (*drv->cpu_clk_ptr < drv->clk + SETTLING) {
                            return;
                        }
                        drv->clk += SETTLING;
                        drv->step++;
                    case 5:
                        fdd_index_count_reset(drv->fdd);
                        drv->sync = 0;
                        drv->step++;
                        if (drv->command == WD_WRITE_TRACK) {
                            if (fdd_write_protect(drv->fdd)) {
                                drv->status |= WD_WP;
                                drv->type = 0;
                                break;
                            }
                            drv->status |= WD_DRQ;
                            drv->byte_count = 3;
                            drv->step = 9;
                            continue;
                        }
                        if (drv->command != WD_READ_TRACK) {
                            drv->step++;
                            continue;
                        }
                    case 6:
                        if (fdd_index_count(drv->fdd) < 1) {
                            drv->clk += fdd_rotate(drv->fdd, (*drv->cpu_clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                            return;
                        }
                        if (fdd_index_count(drv->fdd) > 1) {
                            drv->type = 0;
                            break;
                        }
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        drv->data = (BYTE)fdd_read(drv->fdd);
                        drv->status |= (drv->status & WD_DRQ) ? WD_LD : WD_DRQ;
                        continue;
                    case 7:
                        if (fdd_index_count(drv->fdd) >= 6) {
                            drv->status |= WD_RNF;
                            drv->type = 0;
                            break;
                        }
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = fdd_read(drv->fdd);
                        if (!drv->dden || res != 0x1fe) {
                            if (!drv->sync || res != 0xfe) {
                                drv->sync = (res == 0x1a1);
                                continue;
                            }
                        }
                        drv->crc = 0xb230;
                        drv->byte_count = 6;
                        drv->step++;
                    case 8:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->status |= (drv->status & WD_DRQ) ? WD_LD : WD_DRQ;
                        drv->clk += BYTE_RATE;
                        drv->data = (BYTE)fdd_read(drv->fdd);
                        if (drv->byte_count == 6) {
                            drv->sector = drv->data;
                        }
                        drv->crc = fdd_crc(drv->crc, drv->data);
                        if (--drv->byte_count) {
                            continue;
                        }
                        drv->status |= drv->crc ? WD_CRC : 0;
                        drv->type = 0;
                        break;
                    case 9:
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        fdd_read(drv->fdd);
                        if (--drv->byte_count) {
                            continue;
                        }
                        if (drv->status & WD_DRQ) {
                            drv->status ^= WD_DRQ | WD_LD;
                            drv->type = 0;
                            break;
                        }
                        drv->byte_count = 0;
                        drv->tmp = 0;
                        drv->step++;
                    case 10:
                        if (fdd_index_count(drv->fdd) < 1) {
                            drv->clk += fdd_rotate(drv->fdd, (*drv->cpu_clk_ptr - drv->clk) / BYTE_RATE) * BYTE_RATE;
                            return;
                        }
                        if (fdd_index_count(drv->fdd) > 1) {
                            drv->status &= ~WD_DRQ;
                            drv->type = 0;
                            break;
                        }
                        if (*drv->cpu_clk_ptr < drv->clk + BYTE_RATE) {
                            return;
                        }
                        drv->clk += BYTE_RATE;
                        res = drv->data;
                        if (drv->byte_count) {
                            fdd_write(drv->fdd, (BYTE)(drv->crc & 0xff));
                            drv->byte_count--;
                        } else {
                            drv->status |= (drv->status & WD_DRQ) ? WD_LD : WD_DRQ;

                            if (drv->dden) {
                                switch (res) {
                                    case 0xf7:
                                        drv->byte_count = 1;
                                        res = drv->crc >> 8;
                                        drv->tmp = 0;
                                        break;
                                    case 0xf8:
                                    case 0xf9:
                                    case 0xfa:
                                    case 0xfb:
                                    case 0xfe:
                                        if (!drv->tmp) {
                                            drv->crc = 0xffff;
                                            drv->tmp = 1;
                                        }
                                    case 0xfc:
                                        res |= 0x100;
                                }
                            } else {
                                switch (res) {
                                    case 0xf5:
                                        res = 0x1a1;
                                        if (!drv->tmp) {
                                            drv->crc = 0xffff;
                                            drv->tmp = 1;
                                        }
                                        break;
                                    case 0xf6:
                                        res = 0x1c2;
                                        break;
                                    case 0xf7:
                                        drv->byte_count = 1;
                                        res = drv->crc >> 8;
                                        drv->tmp = 0;
                                        break;
                                }
                            }
                            if (drv->tmp) {
                                drv->crc = fdd_crc(drv->crc, (BYTE)res);
                            }
                            fdd_write(drv->fdd, (BYTE)res);
                            drv->data = 0;
                        }
                        continue;
                }
                break;
            case 4: /* type 4 */
                if (*drv->cpu_clk_ptr < drv->clk + PREPARE) {
                    return;
                }
                drv->clk += PREPARE;
                drv->status &= WD_BSY;
                if (drv->cmd & WD_I3) {
                    drv->irq = 1;
                }
                fdd_index_count_reset(drv->fdd);
                drv->tmp = fdd_index_count(drv->fdd);
                drv->type = (drv->status & WD_BSY) ? 0 : -1;
                continue;
        }
        drv->cmd = 0;
        drv->irq = 1;
        fdd_index_count_reset(drv->fdd);
    }