//public int GetHeadPos(long time) //{ // if (_trackImage == null) // return (int)(_trackTime / 2); // int pos = (int)((time % _trackTime) / _byteTime); // if (pos > _trackImage.Length) // pos = 0; // return pos; //} #region UTILS /// <summary> /// Calculate WD1793 CRC, using initial value for 0xA1,0xA1,0xA1 !!! /// </summary> /// <param name="startIndex">Start index in track image</param> /// <param name="size">Size of block</param> public ushort MakeCrc(int startIndex, int size) { if (_trackImage == null) { return(0); } return(CrcVg93.Calc3xA1(_trackImage, startIndex, size)); }
public void SetDataCrc(bool valid) { var dataBlock = CreateDataBlock()[0]; ushort crc = 0xFFFF; if (dataBlock.Length > 0) { crc = CrcVg93.Update(crc, dataBlock, 0, dataBlock.Length - 2); } if (!valid) { crc = (ushort)(crc ^ 0xFFFF); } _dataCrc = crc; }
public void SetAdCrc(bool valid) { var adBlock = CreateAdBlock()[0]; ushort crc = 0xFFFF; if (adBlock.Length > 0) { crc = CrcVg93.Update(crc, adBlock, 0, adBlock.Length - 2); } if (!valid) { crc = (ushort)(crc ^ 0xFFFF); } _adCrc = crc; }
private byte[][] generateTrackImage(List <FdiSectorHeader> sectorHeaderList) { var trackImage = new byte[2][]; // Вычисляем необходимое число байт под данные: var imageSize = 6250; var secCount = sectorHeaderList.Count; var trkdatalen = 0; for (var ilsec = 0; ilsec < secCount; ilsec++) { var hdr = sectorHeaderList[ilsec]; trkdatalen += 2 + 6; // for marks: 0xA1, 0xFE, 6bytes var slen = 128 << hdr.N; if ((hdr.Flags & 0x40) != 0) // заголовок без массива данных { slen = 0; } else { trkdatalen += 4; // for data header/crc: 0xA1, 0xFB, ...,2bytes } trkdatalen += slen; } var freeSpace = imageSize - (trkdatalen + secCount * (3 + 2)); // 3x4E & 2x00 per sector var synchroPulseLen = 1; // 1 уже учтен в trkdatalen... var firstSpaceLen = 1; var secondSpaceLen = 1; var thirdSpaceLen = 1; var synchroSpaceLen = 1; freeSpace -= firstSpaceLen + secondSpaceLen + thirdSpaceLen + synchroSpaceLen; if (freeSpace < 0) { imageSize += -freeSpace; freeSpace = 0; } // Распределяем длины пробелов и синхропромежутка: while (freeSpace > 0) { if (freeSpace >= (secCount * 2)) // Synchro for ADMARK & DATA { if (synchroSpaceLen < 12) { synchroSpaceLen++; freeSpace -= secCount * 2; } } if (freeSpace < secCount) { break; } if (firstSpaceLen < 10) { firstSpaceLen++; freeSpace -= secCount; } if (freeSpace < secCount) { break; } if (secondSpaceLen < 22) { secondSpaceLen++; freeSpace -= secCount; } if (freeSpace < secCount) { break; } if (thirdSpaceLen < 60) { thirdSpaceLen++; freeSpace -= secCount; } if (freeSpace < secCount) { break; } if ((synchroSpaceLen >= 12) && (firstSpaceLen >= 10) && (secondSpaceLen >= 22) && (thirdSpaceLen >= 60)) { break; } } // по возможности делаем три синхроимпульса... if (freeSpace > (secCount * 2) + 10) { synchroPulseLen++; freeSpace -= secCount; } if (freeSpace > (secCount * 2) + 9) { synchroPulseLen++; } if (freeSpace < 0) { imageSize += -freeSpace; freeSpace = 0; } // Форматируем дорожку... trackImage[0] = new byte[imageSize]; trackImage[1] = new byte[trackImage[0].Length / 8 + (((trackImage[0].Length & 7) != 0) ? 1 : 0)]; var tptr = 0; for (var sec = 0; sec < secCount; sec++) { var hdr = sectorHeaderList[sec]; for (var r = 0; r < firstSpaceLen; r++) // Первый пробел { trackImage[0][tptr] = 0x4E; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } for (var r = 0; r < synchroSpaceLen; r++) // Синхропромежуток { trackImage[0][tptr] = 0x00; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } var ptrcrc = tptr; for (var r = 0; r < synchroPulseLen; r++) // Синхроимпульс { trackImage[0][tptr] = 0xA1; trackImage[1][tptr / 8] |= (byte)(1 << (tptr & 7)); tptr++; } trackImage[0][tptr] = 0xFE; // Метка "Адрес" trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; trackImage[0][tptr] = hdr.C; // cyl trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; trackImage[0][tptr] = hdr.H; // head trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; trackImage[0][tptr] = hdr.R; // sector # trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; trackImage[0][tptr] = hdr.N; // len code trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; ushort vgcrc = CrcVg93.Calculate(trackImage[0], ptrcrc, tptr - ptrcrc); trackImage[0][tptr] = (byte)vgcrc; // crc trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; trackImage[0][tptr] = (byte)(vgcrc >> 8); // crc trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; for (var r = 0; r < secondSpaceLen; r++) // Второй пробел { trackImage[0][tptr] = 0x4E; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } for (var r = 0; r < synchroSpaceLen; r++) // Синхропромежуток { trackImage[0][tptr] = 0x00; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } byte fdiSectorFlags = hdr.Flags; // !!!!!!!!! // !WARNING! this feature of FDI format is NOT FULL DOCUMENTED!!! // !!!!!!!!! // // Flags::bit6 - Возможно, 1 в данном разряде // будет обозначать адресный маркер без области данных. // if ((fdiSectorFlags & 0x40) == 0) // oh-oh, data area can be not present... ;-) { ptrcrc = tptr; for (var r = 0; r < synchroPulseLen; r++) // Синхроимпульс { trackImage[0][tptr] = 0xA1; trackImage[1][tptr / 8] |= (byte)(1 << (tptr & 7)); tptr++; } if ((fdiSectorFlags & 0x80) != 0) { trackImage[0][tptr] = 0xF8; // Метка "Удаленные данные" } else { trackImage[0][tptr] = 0xFB; // Метка "Данные" } trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; //TODO: sector len from crc flags? var SL = 128 << hdr.N; for (var r = 0; r < SL; r++) // сектор SL байт { trackImage[0][tptr] = hdr.DataArray[r]; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } vgcrc = CrcVg93.Calculate(trackImage[0], ptrcrc, tptr - ptrcrc); if ((fdiSectorFlags & 0x3F) == 0) // CRC not correct? { vgcrc ^= (ushort)0xFFFF; // oh-oh, high technology... CRC bad... ;-) } trackImage[0][tptr] = (byte)vgcrc; // crc trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; trackImage[0][tptr] = (byte)(vgcrc >> 8); // crc trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } for (var r = 0; r < thirdSpaceLen; r++) // Третий пробел { trackImage[0][tptr] = 0x4E; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } } for (var eoftrk = tptr; eoftrk < trackImage[0].Length; eoftrk++) { trackImage[0][tptr] = 0x4E; trackImage[1][tptr / 8] &= (byte)~(1 << (tptr & 7)); tptr++; } return(trackImage); }
private void process(long toTact) { /*time = wd93_get_time()*/ /*comp.t_states + cpu.t*/ time = toTact; // inactive drives disregard HLT bit if (time > fdd[drive].motor && (system & 0x08) != 0) { fdd[drive].motor = 0; } if (state != WDSTATE.S_WAIT) // KLUDGE: motor emulation to fix SCORPION 128 TRDOS dead lock { if (fdd[drive].IsREADY) // RESET { status &= ~WD_STATUS.WDS_NOTRDY; } else { status |= WD_STATUS.WDS_NOTRDY; } //status |= WD_STATUS.WDS_RECORDT; } if ((cmd & 0x80) == 0 || (cmd & 0xF0) == 0xD0) // seek / step commands { WD_STATUS old_idx_status = idx_status; idx_status &= ~WD_STATUS.WDS_INDEX; status &= ~WD_STATUS.WDS_INDEX; if (state != WDSTATE.S_IDLE) { status &= ~(WD_STATUS.WDS_TRK00 | WD_STATUS.WDS_INDEX); if (fdd[drive].motor > 0 && (system & 0x08) != 0) { status |= WD_STATUS.WDS_HEADL; } if (fdd[drive].IsTRK00) { status |= WD_STATUS.WDS_TRK00; } } // todo: test spinning if (fdd[drive].IsREADY && fdd[drive].motor > 0 && ((time + tshift) % (Z80FQ / FDD_RPS) < (Z80FQ * 4 / 1000))) { if (state == WDSTATE.S_IDLE) { if (time < idx_tmo) { status |= WD_STATUS.WDS_INDEX; } } else { status |= WD_STATUS.WDS_INDEX; } idx_status |= WD_STATUS.WDS_INDEX; // index every turn, len=4ms (if disk present) } } for (; ;) { switch (state) { // ---------------------------------------------------- case WDSTATE.S_IDLE: status &= ~WD_STATUS.WDS_BUSY; if (idx_cnt >= 15 || time > idx_tmo) { idx_cnt = 15; status &= ~WD_STATUS.WDS_NOTRDY; status |= WD_STATUS.WDS_NOTRDY; fdd[drive].motor = 0; } rqs = BETA_STATUS.INTRQ; return; case WDSTATE.S_WAIT: if (time < next) { return; } state = state2; break; // ---------------------------------------------------- case WDSTATE.S_DELAY_BEFORE_CMD: if (!wd93_nodelay && (cmd & CMD_DELAY) != 0) { next += (Z80FQ * 15 / 1000); // 15ms delay // this flag should indicate motor state, but we dont have it :( // so, simulate motor off->on delay when program specify CMD_DELAY status |= WD_STATUS.WDS_NOTRDY; } status = (status | WD_STATUS.WDS_BUSY) & ~(WD_STATUS.WDS_DRQ | WD_STATUS.WDS_LOST | WD_STATUS.WDS_NOTFOUND | WD_STATUS.WDS_RECORDT | WD_STATUS.WDS_WRITEP); state2 = WDSTATE.S_CMD_RW; state = WDSTATE.S_WAIT; break; case WDSTATE.S_CMD_RW: if (((cmd & 0xE0) == 0xA0 || (cmd & 0xF0) == 0xF0) && fdd[drive].IsWP) { status |= WD_STATUS.WDS_WRITEP; state = WDSTATE.S_IDLE; break; } if ((cmd & 0xC0) == 0x80 || (cmd & 0xF8) == 0xC0) { // read/write sectors or read am - find next AM end_waiting_am = next + 5 * Z80FQ / FDD_RPS; // max wait disk 5 turns find_marker(toTact); break; } if ((cmd & 0xF8) == 0xF0) // write track { rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; next += 3 * fdd[drive].t.ts_byte; state2 = WDSTATE.S_WRTRACK; state = WDSTATE.S_WAIT; break; } if ((cmd & 0xF8) == 0xE0) // read track { load(); rwptr = 0; rwlen = fdd[drive].t.trklen; state2 = WDSTATE.S_READ; getindex(); break; } // else unknown command state = WDSTATE.S_IDLE; break; case WDSTATE.S_FOUND_NEXT_ID: if (!fdd[drive].IsREADY) { // no disk - wait again end_waiting_am = next + 5 * Z80FQ / FDD_RPS; // nextmk: find_marker(toTact); break; } if (next >= end_waiting_am || foundid == -1) { status |= WD_STATUS.WDS_NOTFOUND; state = WDSTATE.S_IDLE; break; } status &= ~WD_STATUS.WDS_CRCERR; load(); if ((cmd & 0x80) == 0) // verify after seek { if (fdd[drive].t.HeaderList[foundid].c != track) { find_marker(toTact); break; } if (!fdd[drive].t.HeaderList[foundid].c1) { status |= WD_STATUS.WDS_CRCERR; find_marker(toTact); break; } state = WDSTATE.S_IDLE; break; } if ((cmd & 0xF0) == 0xC0) // read AM { rwptr = fdd[drive].t.HeaderList[foundid].idOffset; rwlen = 6; // read_first_byte: data = fdd[drive].t.RawRead(rwptr++); rwlen--; rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; next += fdd[drive].t.ts_byte; state = WDSTATE.S_WAIT; state2 = WDSTATE.S_READ; break; } // else R/W sector(s) if (fdd[drive].t.HeaderList[foundid].c != track || fdd[drive].t.HeaderList[foundid].n != sector) { find_marker(toTact); break; } if ((cmd & CMD_SIDE_CMP_FLAG) != 0 && (((cmd >> CMD_SIDE_SHIFT) ^ fdd[drive].t.HeaderList[foundid].s) & 1) != 0) { find_marker(toTact); break; } if (!fdd[drive].t.HeaderList[foundid].c1) { status |= WD_STATUS.WDS_CRCERR; find_marker(toTact); break; } if ((cmd & 0x20) != 0) // write sector(s) { rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; next += fdd[drive].t.ts_byte * 9; state = WDSTATE.S_WAIT; state2 = WDSTATE.S_WRSEC; break; } // read sector(s) if (fdd[drive].t.HeaderList[foundid].dataOffset < 0) // no data block { find_marker(toTact); break; } if (!wd93_nodelay) { next += fdd[drive].t.ts_byte * (fdd[drive].t.HeaderList[foundid].dataOffset - fdd[drive].t.HeaderList[foundid].idOffset); // Задержка на пропуск заголовка сектора и пробела между заголовком и зоной данных } state = WDSTATE.S_WAIT; state2 = WDSTATE.S_RDSEC; break; case WDSTATE.S_RDSEC: //// TODO: Сделать поиск массива данных как и поиск массива заголовка! if (fdd[drive].t.RawRead(fdd[drive].t.HeaderList[foundid].dataOffset - 1) == 0xF8) // TODO: check dataOffset>=1 { status |= WD_STATUS.WDS_RECORDT; } else { status &= ~WD_STATUS.WDS_RECORDT; } rwptr = fdd[drive].t.HeaderList[foundid].dataOffset; // Смещение зоны данных сектора (в байтах) относительно начала трека rwlen = 128 << (fdd[drive].t.HeaderList[foundid].l & 3); // [vv] //goto read_first_byte; #region us374 data = fdd[drive].t.RawRead(rwptr++); rwlen--; rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; next += fdd[drive].t.ts_byte; state = WDSTATE.S_WAIT; state2 = WDSTATE.S_READ; #endregion #region ZXMAK //without WDSTATE.S_RDSEC (end of WDSTATE.S_FOUND_NEXT_ID) #region fixed by me (timing fix) //// not fixed: ////data = fdd[drive].t.trkd[rwptr++]; ////rwlen--; ////rqs = BETA_STATUS.DRQ; ////status |= WD_STATUS.WDS_DRQ; ////next += fdd[drive].t.ts_byte; ////state = WDSTATE.S_WAIT; ////state2 = WDSTATE.S_READ; //// fixed: //long div = fdd[drive].t.trklen * fdd[drive].t.ts_byte; //int i = (int)(((next + tshift) % div) / fdd[drive].t.ts_byte); // номер байта который пролетает под головкой //int pos = fdd[drive].t.HeaderList[foundid].dataOffset; //int dist = (pos > i) ? pos - i : fdd[drive].t.trklen + pos - i; //next += dist * fdd[drive].t.ts_byte; //state = WDSTATE.S_WAIT; //state2 = WDSTATE.S_READ; #endregion #endregion break; case WDSTATE.S_READ: if (notready()) { break; } load(); // TODO: really need? if (!fdd[drive].Present) //if(!seldrive->t.trkd) { status |= WD_STATUS.WDS_NOTFOUND; state = WDSTATE.S_IDLE; break; } if (rwlen > 0) { if ((rqs & BETA_STATUS.DRQ) != 0) { status |= WD_STATUS.WDS_LOST; } data = fdd[drive].t.RawRead(rwptr++); rwlen--; rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; if (!wd93_nodelay) { next += fdd[drive].t.ts_byte; } else { next = time + 1; } state = WDSTATE.S_WAIT; state2 = WDSTATE.S_READ; } else { if ((cmd & 0xE0) == 0x80) // read sector { if (!fdd[drive].t.HeaderList[foundid].c2) { status |= WD_STATUS.WDS_CRCERR; } if ((cmd & CMD_MULTIPLE) != 0) { sector++; state = WDSTATE.S_CMD_RW; break; } } if ((cmd & 0xF0) == 0xC0) // read address { if (!fdd[drive].t.HeaderList[foundid].c1) { status |= WD_STATUS.WDS_CRCERR; } } state = WDSTATE.S_IDLE; } break; case WDSTATE.S_WRSEC: load(); if ((rqs & BETA_STATUS.DRQ) != 0) { status |= WD_STATUS.WDS_LOST; state = WDSTATE.S_IDLE; break; } fdd[drive].ModifyFlag |= ModifyFlag.SectorLevel; rwptr = fdd[drive].t.HeaderList[foundid].idOffset + 6 + 11 + 11; LedWr = true; for (rwlen = 0; rwlen < 12; rwlen++) { fdd[drive].t.RawWrite(rwptr++, 0x00, false); } for (rwlen = 0; rwlen < 3; rwlen++) { fdd[drive].t.RawWrite(rwptr++, 0xA1, true); } fdd[drive].t.RawWrite(rwptr++, (byte)(((cmd & CMD_WRITE_DEL) != 0) ? 0xF8 : 0xFB), false); rwlen = 128 << (fdd[drive].t.HeaderList[foundid].l & 3); // [vv] state = WDSTATE.S_WRITE; break; case WDSTATE.S_WRITE: if (notready()) { break; } if ((rqs & BETA_STATUS.DRQ) != 0) { status |= WD_STATUS.WDS_LOST; data = 0; } fdd[drive].t.RawWrite(rwptr++, data, false); rwlen--; if (rwptr == fdd[drive].t.trklen) { rwptr = 0; } //fdd[drive].t.sf = SEEK_MODE.JUST_SEEK; // invalidate sectors fdd[drive].t.sf = true; // REFRESH!!! if (rwlen > 0) { if (!wd93_nodelay) { next += fdd[drive].t.ts_byte; } state = WDSTATE.S_WAIT; state2 = WDSTATE.S_WRITE; rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; } else { int len = (128 << (fdd[drive].t.HeaderList[foundid].l & 3)) + 1; byte[] sc = new byte[2056]; if (rwptr < len) { for (int memi = 0; memi < rwptr; memi++) { sc[memi] = fdd[drive].t.RawRead(fdd[drive].t.trklen - rwptr + memi); } for (int memi = 0; memi < len - rwptr; memi++) { sc[rwptr + memi] = fdd[drive].t.RawRead(memi); } //memcpy(sc, trkcache.trkd, 0, (int)(trkcache.trklen - rwptr), rwptr); //memcpy(sc, trkcache.trkd, rwptr, 0, len - rwptr); } else { for (int memi = 0; memi < len; memi++) { sc[memi] = fdd[drive].t.RawRead(rwptr - len + memi); } //memcpy(sc, trkcache.trkd, 0, rwptr - len, len); } uint crc = CrcVg93.Calc3xA1(sc, 0, len); fdd[drive].t.RawWrite(rwptr++, (byte)crc, false); fdd[drive].t.RawWrite(rwptr++, (byte)(crc >> 8), false); fdd[drive].t.RawWrite(rwptr, 0xFF, false); if ((cmd & CMD_MULTIPLE) != 0) { sector++; state = WDSTATE.S_CMD_RW; break; } state = WDSTATE.S_IDLE; } break; case WDSTATE.S_WRTRACK: if ((rqs & BETA_STATUS.DRQ) != 0) { status |= WD_STATUS.WDS_LOST; state = WDSTATE.S_IDLE; break; } fdd[drive].ModifyFlag |= ModifyFlag.TrackLevel; state2 = WDSTATE.S_WR_TRACK_DATA; start_crc = 0; getindex(); end_waiting_am = next + 5 * Z80FQ / FDD_RPS; break; case WDSTATE.S_WR_TRACK_DATA: if (notready()) { break; } if ((rqs & BETA_STATUS.DRQ) != 0) { status |= WD_STATUS.WDS_LOST; data = 0; } //trkcache.seek(fdd[drive], fdd[drive].CurrentTrack, side, SEEK_MODE.JUST_SEEK); //trkcache.sf = SEEK_MODE.JUST_SEEK; // invalidate sectors // if (trkcache.sf) // trkcache.RefreshHeaders(); fdd[drive].t = fdd[drive].CurrentTrack; fdd[drive].t.sf = true; // REFRESH!!! bool marker = false; byte _byte = data; uint _crc = 0; if (data == 0xF5) { _byte = 0xA1; marker = true; start_crc = rwptr + 1; } if (data == 0xF6) { _byte = 0xC2; marker = true; } if (data == 0xF7) { _crc = fdd[drive].t.MakeCrc(start_crc, rwptr - start_crc); _byte = (byte)(_crc & 0xFF); } fdd[drive].t.RawWrite(rwptr++, _byte, marker); rwlen--; if (data == 0xF7) { fdd[drive].t.RawWrite(rwptr++, (byte)(_crc >> 8), marker); // second byte of CRC16 rwlen--; } if (rwlen > 0) { if (!wd93_nodelay) { next += fdd[drive].t.ts_byte; } state2 = WDSTATE.S_WR_TRACK_DATA; state = WDSTATE.S_WAIT; rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ; break; } state = WDSTATE.S_IDLE; break; // ---------------------------------------------------- case WDSTATE.S_TYPE1_CMD: status = (status | WD_STATUS.WDS_BUSY) & ~(WD_STATUS.WDS_DRQ | WD_STATUS.WDS_CRCERR | WD_STATUS.WDS_SEEKERR | WD_STATUS.WDS_WRITEP); rqs = BETA_STATUS.NONE; if (fdd[drive].IsWP) { status |= WD_STATUS.WDS_WRITEP; } fdd[drive].motor = next + 2 * Z80FQ; state2 = WDSTATE.S_SEEKSTART; // default is seek/restore if ((cmd & 0xE0) != 0) // single step { if ((cmd & 0x40) != 0) { stepdirection = (sbyte)(((cmd & CMD_SEEK_DIR) != 0) ? -1 : 1); } state2 = WDSTATE.S_STEP; // TODO: check!!! break required? error in emulator? } if (!wd93_nodelay) { //next += 1 * Z80FQ / 1000; next += 32; } state = WDSTATE.S_WAIT; break; case WDSTATE.S_STEP: // TRK00 sampled only in RESTORE command if (fdd[drive].IsTRK00 && (cmd & 0xF0) == 0) { track = 0; state = WDSTATE.S_VERIFY; break; } if ((cmd & 0xE0) == 0 || (cmd & CMD_SEEK_TRKUPD) != 0) { track = (byte)((int)track + stepdirection); } fdd[drive].HeadCylynder += stepdirection; // if (fdd[drive].HeadCylynder == -1) fdd[drive].CurrentTrack = 0; if (fdd[drive].HeadCylynder >= (fdd[drive].CylynderCount - 1)) { fdd[drive].HeadCylynder = fdd[drive].CylynderCount - 1; } //trkcache.clear(); fdd[drive].t = fdd[drive].CurrentTrack; uint[] steps = new uint[4] { 6, 12, 20, 30 }; // TODO: static if (!wd93_nodelay) { next += steps[cmd & CMD_SEEK_RATE] * Z80FQ / 1000; } /* ?TODO? -- fdd noise #ifndef MOD_9X * if (!wd93_nodelay && conf.fdd_noise) Beep((stepdirection > 0)? 600 : 800, 2); #endif*/ state2 = ((cmd & 0xE0) != 0) ? WDSTATE.S_VERIFY : WDSTATE.S_SEEK; state = WDSTATE.S_WAIT; break; case WDSTATE.S_SEEKSTART: if ((cmd & 0x10) == 0) { track = 0xFF; data = 0; } // state = S_SEEK; break; //TODO: проверить!!! не ошибка ли это - дальше выполнять блок WDSTATE.SEEK?: // в исходном варианте нет брейка и далее сразу следует case WDSTATE.S_SEEK: if (data == track) { state = WDSTATE.S_VERIFY; break; } stepdirection = (data < track) ? -1 : 1; state = WDSTATE.S_STEP; break; case WDSTATE.S_SEEK: if (data == track) { state = WDSTATE.S_VERIFY; break; } stepdirection = (data < track) ? -1 : 1; state = WDSTATE.S_STEP; break; case WDSTATE.S_VERIFY: if ((cmd & CMD_SEEK_VERIFY) == 0) { status |= WD_STATUS.WDS_BUSY; state2 = WDSTATE.S_IDLE; state = WDSTATE.S_WAIT; next += 128; //next = time + 1; // do not use time - CHORDOUT issue idx_tmo = next + 15 * Z80FQ / FDD_RPS; // 15 disk turns break; } end_waiting_am = next + 6 * Z80FQ / FDD_RPS; // max wait disk 6 turns load(); find_marker(toTact); break; // ---------------------------------------------------- case WDSTATE.S_RESET: // seek to trk0, but don't be busy if (fdd[drive].IsTRK00) { state = WDSTATE.S_IDLE; } else { fdd[drive].HeadCylynder--; //trkcache.clear(); fdd[drive].t = fdd[drive].CurrentTrack; } // if (seldrive.TRK00) track = 0; next += 6 * Z80FQ / 1000; break; default: throw new InvalidOperationException("WD1793.process - WD1793 in wrong state"); } } }