public byte Read(long tact, WD93REG reg) { process(tact); byte value = 0xFF; switch (reg) { case WD93REG.CMD: // COMMAND/STATUS #1F LedRd = true; rqs &= ~BETA_STATUS.INTRQ; value = (byte)status; if ((system & (int)WD_SYS.SYS_HLT) == 0) { value &= (byte)(value & (int)~WD_STATUS.WDS_HEADL); } if (!fdd[drive].Present) { value = (byte)(value & ~(byte)WD_STATUS.WDS_INDEX); // No disk emulation } break; case WD93REG.TRK: // #3F value = track; break; case WD93REG.SEC: // #5F value = sector; break; case WD93REG.DAT: // #7F LedRd = true; status &= ~WD_STATUS.WDS_DRQ; rqs &= ~BETA_STATUS.DRQ; value = data; break; case WD93REG.SYS: // #FF LedRd = true; value = (byte)((byte)rqs | 0x3F); break; default: throw new InvalidOperationException(); } return(value); }
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 = (int)(fdd[drive].t.HeaderList[foundid].idOffset + 6 + 11 + 11); 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 = wd93_crc(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.WD1793_CRC(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 ((int)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 Exception("WD1793.process - WD1793 in wrong state"); } } }
public void SetReg(WD93REG reg, byte value) { long tact = m_cpu.Tact; if (LogIO) { LogAgent.Debug( "WD93 {0} <== #{1:X2} [PC=#{2:X4}, T={3}]", reg, value, m_cpu.regs.PC, tact); } process(tact); switch (reg) { case WD93REG.CMD: // COMMAND/STATUS LedDiskIO = true; // force interrupt if ((value & 0xF0) == 0xD0) { int cond = value & 0xF; next = tact; idx_cnt = 0; idx_tmo = next + 15 * Z80FQ / FDD_RPS; // 15 disk turns cmd = value; if (cond == 0) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.NONE; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 8) != 0) // Immediate Interrupt { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 4) != 0) // Index Pulse (unimplemented yet) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 2) != 0) // Ready to Not-Ready Transition (unimplemented yet) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 1) != 0) // Not-Ready to Ready Transition (unimplemented yet) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } break; } if ((status & WD_STATUS.WDS_BUSY) != 0) break; cmd = value; next = tact; status |= WD_STATUS.WDS_BUSY; rqs = BETA_STATUS.NONE; idx_cnt = 0; idx_tmo = long.MaxValue; //----------------------------------------------------------------------- if ((cmd & 0x80) != 0) // read/write command { // abort if no disk if ((status & WD_STATUS.WDS_NOTRDY) != 0) { state2 = WDSTATE.S_IDLE; state = WDSTATE.S_WAIT; next = tact + Z80FQ / FDD_RPS; rqs = BETA_STATUS.INTRQ; break; } // continue disk spinning if (fdd[drive].motor > 0 || wd93_nodelay) fdd[drive].motor = next + 2 * Z80FQ; state = WDSTATE.S_DELAY_BEFORE_CMD; break; } // else seek/step command state = WDSTATE.S_TYPE1_CMD; break; case WD93REG.TRK: track = value; break; case WD93REG.SEC: sector = value; break; case WD93REG.DAT: LedDiskIO = true; data = value; rqs &= ~BETA_STATUS.DRQ; status &= ~WD_STATUS.WDS_DRQ; break; case WD93REG.SYS: LedDiskIO = true; //system = value; drive = value & 3; side = 1 & ~(value >> 4); fdd[drive].HeadSide = side; //seldrive->t.clear(); fdd[drive].t = fdd[drive].CurrentTrack; if ((value & (int)WD_SYS.SYS_RST) == 0) // reset { status = WD_STATUS.WDS_NOTRDY; rqs = BETA_STATUS.INTRQ; fdd[drive].motor = 0; state = WDSTATE.S_IDLE; idx_cnt = 0; idx_status = 0; #if NO_COMPILE // move head to trk00 steptime = 6 * (Z80FQ / 1000); // 6ms next += 1*Z80FQ/1000; // 1ms before command state = S_RESET; //seldrive->track = 0; #endif } else if (((system ^ value) & (int)WD_SYS.SYS_HLT) != 0) // hlt 0->1 { if ((status & WD_STATUS.WDS_BUSY) == 0) { idx_cnt++; } } system = value; break; default: throw new Exception("WD1793.SetReg: Invalid register"); } process(tact); }
public byte GetReg(WD93REG reg) { long tact = m_cpu.Tact; process(tact); byte value = 0xFF; switch (reg) { case WD93REG.CMD: // COMMAND/STATUS #1F LedDiskIO = true; rqs &= ~BETA_STATUS.INTRQ; value = (byte)status; if ((system & (int)WD_SYS.SYS_HLT) == 0) value &= (byte)(value & (int)~WD_STATUS.WDS_HEADL); if (!fdd[drive].Present) value = (byte)(value & ~(byte)WD_STATUS.WDS_INDEX); // No disk emulation break; case WD93REG.TRK: // #3F value = track; break; case WD93REG.SEC: // #5F value = sector; break; case WD93REG.DAT: // #7F LedDiskIO = true; status &= ~WD_STATUS.WDS_DRQ; rqs &= ~BETA_STATUS.DRQ; value = data; break; case WD93REG.SYS: // #FF LedDiskIO = true; value = (byte)((byte)rqs | 0x3F); break; default: throw new InvalidOperationException(); } if (LogIO) { LogAgent.Debug( "WD93 {0} ==> #{1:X2} [PC=#{2:X4}, T={3}]", reg, value, m_cpu.regs.PC, tact); } return value; }
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"); } } }
public void Write(long tact, WD93REG reg, byte value) { process(tact); switch (reg) { case WD93REG.CMD: // COMMAND/STATUS LedRd = true; // force interrupt if ((value & 0xF0) == 0xD0) { int cond = value & 0xF; next = tact; idx_cnt = 0; idx_tmo = next + 15 * Z80FQ / FDD_RPS; // 15 disk turns cmd = value; if (cond == 0) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.NONE; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 8) != 0) // Immediate Interrupt { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 4) != 0) // Index Pulse (unimplemented yet) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 2) != 0) // Ready to Not-Ready Transition (unimplemented yet) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } if ((cond & 1) != 0) // Not-Ready to Ready Transition (unimplemented yet) { state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ; status &= ~WD_STATUS.WDS_BUSY; break; } break; } if ((status & WD_STATUS.WDS_BUSY) != 0) { break; } cmd = value; next = tact; status |= WD_STATUS.WDS_BUSY; rqs = BETA_STATUS.NONE; idx_cnt = 0; idx_tmo = long.MaxValue; //----------------------------------------------------------------------- if ((cmd & 0x80) != 0) // read/write command { // abort if no disk if ((status & WD_STATUS.WDS_NOTRDY) != 0) { state2 = WDSTATE.S_IDLE; state = WDSTATE.S_WAIT; next = tact + Z80FQ / FDD_RPS; rqs = BETA_STATUS.INTRQ; break; } // continue disk spinning if (fdd[drive].motor > 0 || wd93_nodelay) { fdd[drive].motor = next + 2 * Z80FQ; } state = WDSTATE.S_DELAY_BEFORE_CMD; break; } // else seek/step command state = WDSTATE.S_TYPE1_CMD; break; case WD93REG.TRK: track = value; break; case WD93REG.SEC: sector = value; break; case WD93REG.DAT: LedRd = true; data = value; rqs &= ~BETA_STATUS.DRQ; status &= ~WD_STATUS.WDS_DRQ; break; case WD93REG.SYS: LedRd = true; //system = value; drive = (value & 3) % fdd.Length; side = 1 & ~(value >> 4); fdd[drive].HeadSide = side; //seldrive->t.clear(); fdd[drive].t = fdd[drive].CurrentTrack; if ((value & (int)WD_SYS.SYS_RST) == 0) // reset { status = WD_STATUS.WDS_NOTRDY; rqs = BETA_STATUS.INTRQ; fdd[drive].motor = 0; state = WDSTATE.S_IDLE; idx_cnt = 0; idx_status = 0; #if NO_COMPILE // move head to trk00 steptime = 6 * (Z80FQ / 1000); // 6ms next += 1 * Z80FQ / 1000; // 1ms before command state = S_RESET; //seldrive->track = 0; #endif } else if (((system ^ value) & (int)WD_SYS.SYS_HLT) != 0) // hlt 0->1 { if ((status & WD_STATUS.WDS_BUSY) == 0) { idx_cnt++; } } system = value; break; default: throw new ArgumentOutOfRangeException("reg"); } process(tact); }