//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)); }
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"); } } }