t_bool dp_dter (UNIT *uptr, uint32 first) { uint32 hd, sc, sa; uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ ((uptr->flags & UNIT_WPRT) && (dp_cmd == CMC_WR))) { dp_done (STC_DTE); /* error, done */ return TRUE; } hd = GET_SRF (dp_hdsc); /* get head */ sc = GET_SEC (dp_hdsc); /* get sector */ if (dp_cyl != (uint32) uptr->CYL) { /* wrong cylinder? */ if (dp_cyl == 0) uptr->CYL = 0; else { dp_done (STC_ACF); /* error, done */ return TRUE; } } if (sc >= DP_NUMSC) { /* bad sector? */ dp_done (STC_OVR); /* error, done */ return TRUE; } if (!first && (sc == 0) && (hd == 0)) { /* cyl overflow? */ dp_done (STC_CYO); /* error, done */ return TRUE; } sa = GET_SA (dp_plat, uptr->CYL, hd, sc, dtype); /* curr disk addr */ fseek (uptr->fileref, sa * DP_NUMBY, SEEK_SET); if ((sc + 1) < DP_NUMSC) /* end of track? */ dp_hdsc = dp_hdsc + 1; else dp_hdsc = (dp_hdsc ^ HS_HMASK) & HS_HMASK; /* sec 0, nxt srf */ return FALSE; }
t_stat dp_go1 (uint32 dat) { int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ uint32 u = CW1_GETUNIT (dat); UNIT *uptr = dp_dev.units + u; dp_cw1 = dat; /* store CW1 */ dp_otas = OTA_NOP; /* assume no CW2 */ uptr->FNC = dp_fnc; if (sim_is_active (uptr)) /* still seeking? */ return dp_done (1, STA_UNSER); /* unsafe */ if (!(uptr->flags & UNIT_ATT)) /* not attached? */ return dp_done (1, STA_OFLER); /* offline */ switch (dp_fnc) { /* case on function */ case FNC_SEEK: /* seek */ case FNC_SK0: /* recalibrate */ case FNC_UNL: /* unload */ sim_activate (uptr, dp_btime); /* quick timeout */ break; case FNC_FMT: /* format */ if (uptr->flags & UNIT_WPRT) /* write protect? */ return dp_done (1, STA_WPRER); /* stop now */ case FNC_RCA: /* read current addr */ dp_xip = u | XIP_SCHED; /* operation started */ sim_activate (uptr, dp_xtime * 10); /* rotation timeout */ break; case FNC_RW: /* read/write */ dp_otas = OTA_CW2; /* expect CW2 */ dp_sta = dp_sta | STA_RDY; /* set ready */ if (dp_dma && Q_DMA (ch)) /* DMA? set chan request */ SET_CH_REQ (ch); break; } return SCPE_OK; }
t_stat dp_wds (UNIT *uptr) { for ( ; dp_bptr < DP_NUMBY; dp_bptr++) dpxb[dp_bptr] = dp_db; /* fill with last */ fxwrite (dpxb, sizeof (uint8), DP_NUMBY, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ sim_perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (STC_DTE); return SCPE_IOERR; } return SCPE_OK; }
t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h) { uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN; fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref); if (ferror (uptr->fileref)) { perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (1, STA_UNSER); return SCPE_IOERR; } return SCPE_OK; }
t_stat dp_rds (UNIT *uptr) { uint32 i; i = fxread (dpxb, sizeof (uint8), DP_NUMBY, uptr->fileref); for ( ; i < DP_NUMBY; i++) /* fill with 0's */ dpxb[i] = 0; if (ferror (uptr->fileref)) { /* error? */ sim_perror ("DP I/O error"); clearerr (uptr->fileref); dp_done (STC_DTE); return SCPE_IOERR; } return SCPE_OK; }
t_stat dp_svc (UNIT *uptr) { uint32 u = uptr - dp_dev.units; /* get unit number */ int32 cyl = uptr->CYL; /* get cylinder */ uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ t_stat r; if (uptr->STD & STD_MOV) { /* seek? */ uptr->STD = 0; /* clr seek in prog */ if ((uptr->flags & UNIT_ATT) == 0) /* offl? hangs */ return SCPE_OK; if (cyl >= drv_tab[dtype].cyl) { /* bad cylinder? */ uptr->STD = STD_ILA; /* error */ uptr->CYL = drv_tab[dtype].cyl - 1; /* put at edge */ } if (dpd_arm[u]) /* req intr */ SET_INT (v_DPC + u + 1); return SCPE_OK; } switch (dp_cmd & 0x7) { /* case on func */ case CMC_RCHK: /* read check */ dp_dter (uptr, 1); /* check xfr err */ break; case CMC_RD: /* read */ if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* sch transfer? */ if (dp_dter (uptr, dp_1st)) /* check xfr err */ return SCPE_OK; if ((r = dp_rds (uptr))) /* read sec, err? */ return r; dp_1st = 0; sch_wrmem (dp_dib.sch, dpxb, DP_NUMBY); /* write to memory */ if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* more to do? */ sim_activate (uptr, dp_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } dp_sta = dp_sta | STC_DTE; /* can't work */ break; case CMC_WR: /* write */ if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* sch transfer? */ if (dp_dter (uptr, dp_1st)) /* check xfr err */ return SCPE_OK; dp_bptr = sch_rdmem (dp_dib.sch, dpxb, DP_NUMBY); /* read from mem */ dp_db = dpxb[dp_bptr - 1]; /* last byte */ if ((r = dp_wds (uptr))) /* write sec, err? */ return r; dp_1st = 0; if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* more to do? */ sim_activate (uptr, dp_rtime); /* reschedule */ return SCPE_OK; } break; /* no, set done */ } dp_sta = dp_sta | STC_DTE; /* can't work */ break; } /* end case func */ dp_done (0); /* done */ return SCPE_OK; }
t_stat dp_wrdone (UNIT *uptr, uint32 flg) { dp_done (1, flg); return dp_wrtrk (uptr, dpxb, uptr->CYL, CW1_GETHEAD (dp_cw1)); }
t_stat dp_svc (UNIT *uptr) { int32 dcyl = 0; /* assume recalibrate */ int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ uint32 h = CW1_GETHEAD (dp_cw1); /* head */ int32 st; uint32 i, offs, lnt, ming, tpos; t_stat r; if (!(uptr->flags & UNIT_ATT)) { /* not attached? */ dp_done (1, STA_OFLER); /* offline */ return IORETURN (dp_stopioe, SCPE_UNATT); } switch (uptr->FNC) { /* case on function */ case FNC_SEEK: /* seek, need cyl */ offs = CW1_GETOFFS (dp_cw1); /* get offset */ if (dp_cw1 & CW1_DIR) /* get desired cyl */ dcyl = uptr->CYL - offs; else dcyl = uptr->CYL + offs; if ((offs == 0) || (dcyl < 0) || (dcyl >= (int32) dp_tab[dp_ctype].cyl)) return dp_done (1, STA_SEKER); /* bad seek? */ case FNC_SK0: /* recalibrate */ dp_sta = dp_sta & ~STA_BUSY; /* clear busy */ uptr->FNC = FNC_SEEK | FNC_2ND; /* next state */ st = (abs (dcyl - uptr->CYL)) * dp_stime; /* schedule seek */ if (st == 0) st = dp_stime; uptr->CYL = dcyl; /* put on cylinder */ sim_activate (uptr, st); return SCPE_OK; case FNC_SEEK | FNC_2ND: /* seek, 2nd state */ if (dp_sta & STA_BUSY) /* busy? queue intr */ dp_defint = 1; else SET_INT (INT_DP); /* no, req intr */ return SCPE_OK; case FNC_UNL: /* unload */ detach_unit (uptr); /* detach unit */ return dp_done (0, 0); /* clear busy, no intr */ case FNC_RCA: /* read current addr */ if (h >= dp_tab[dp_ctype].surf) /* invalid head? */ return dp_done (1, STA_ADRER); /* error */ if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */ return r; dp_rptr = 0; /* init rec ptr */ if (dpxb[dp_rptr + REC_LNT] == 0) /* unformated? */ return dp_done (1, STA_ADRER); /* error */ tpos = (uint32) (fmod (sim_gtime () / (double) dp_xtime, DP_TRKLEN)); do { /* scan down track */ dp_buf = dpxb[dp_rptr + REC_ADDR]; /* get rec addr */ dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD; } while ((dp_rptr < tpos) && (dpxb[dp_rptr + REC_LNT] != 0)); if (dp_dma) { /* DMA/DMC? */ if (Q_DMA (ch)) /* DMA? */ dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ SET_CH_REQ (ch); /* request chan */ } return dp_done (1, STA_RDY); /* clr busy, set rdy */ /* Formating takes place in five states: init - clear track buffer, start at first record address - store address word data - store data word(s) until end of range pause - wait for gap word or stop command gap - validate gap word, advance to next record Note that formating is stopped externally by an OCP command; the track buffer is flushed at that point. If the stop does not occur in the proper state (gap word received), a format error occurs. */ case FNC_FMT: /* format */ for (i = 0; i < DP_TRKLEN; i++) /* clear track */ dpxb[i] = 0; dp_xip = dp_xip | XIP_FMT; /* format in progress */ dp_rptr = 0; /* init record ptr */ dp_gap = 0; /* no gap before first */ dp_bctr = (uint32) (16.0 * dp_tab[dp_ctype].wrds); /* init bit cntr */ uptr->FNC = uptr->FNC | FNC_2ND; /* address state */ break; /* set up next word */ case FNC_FMT | FNC_2ND: /* format, address word */ dp_wptr = 0; /* clear word ptr */ if (dp_bctr < (dp_gap + REC_OVHD_BITS + 16)) /* room for gap, record? */ return dp_wrdone (uptr, STA_FMTER); /* no, format error */ dp_bctr = dp_bctr - dp_gap - REC_OVHD_BITS; /* charge for gap, ovhd */ dpxb[dp_rptr + REC_ADDR] = dp_buf; /* store address */ uptr->FNC = FNC_FMT | FNC_3RD; /* data state */ if (dp_eor) { /* record done? */ dp_eor = 0; /* clear for restart */ if (dp_dma) /* DMA/DMC? intr */ SET_INT (INT_DP); } break; /* set up next word */ case FNC_FMT | FNC_3RD: /* format, data word */ if (dp_sta & STA_RDY) /* timing failure? */ return dp_wrdone (uptr, STA_DTRER); /* write trk, err */ else { /* no, have word */ if (dp_bctr < 16) /* room for it? */ return dp_wrdone (uptr, STA_FMTER); /* no, error */ dp_bctr = dp_bctr - 16; /* charge for word */ dp_csum = dp_csum ^ dp_buf; /* update checksum */ dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_buf;/* store word */ dpxb[dp_rptr + REC_LNT]++; /* incr rec lnt */ dp_wptr++; /* incr word ptr */ } if (dp_eor) { /* record done? */ dp_eor = 0; /* clear for restart */ if (dp_dma) /* DMA/DMC? intr */ SET_INT (INT_DP); dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* store checksum */ uptr->FNC = uptr->FNC | FNC_4TH; /* pause state */ sim_activate (uptr, 5 * dp_xtime); /* schedule pause */ return SCPE_OK; /* don't request word */ } break; /* set up next word */ case FNC_FMT | FNC_4TH: /* format, pause */ uptr->FNC = FNC_FMT | FNC_5TH; /* gap state */ break; /* request word */ case FNC_FMT | FNC_5TH: /* format, gap word */ ming = ((16 * dp_wptr) + REC_OVHD_BITS) / 20; /* min 5% gap */ if (dp_buf < ming) /* too small? */ return dp_wrdone (uptr, STA_FMTER); /* yes, format error */ dp_rptr = dp_rptr + dp_wptr + REC_OVHD; /* next record */ uptr->FNC = FNC_FMT | FNC_2ND; /* address state */ if (dp_eor) { /* record done? */ dp_eor = 0; /* clear for restart */ if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */ } dp_gap = dp_buf; /* save gap */ dp_csum = 0; /* clear checksum */ break; /* set up next word */ /* Read and write take place in two states: init - read track into buffer, find record, validate parameters data - (read) fetch data from buffer, stop on end of range - (write) write data into buffer, flush on end of range */ case FNC_RW: /* read/write */ if (h >= dp_tab[dp_ctype].surf) /* invalid head? */ return dp_done (1, STA_ADRER); /* error */ if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */ return r; if (!dp_findrec (dp_cw2)) /* find rec; error? */ return dp_done (1, STA_ADRER); /* address error */ if ((dpxb[dp_rptr + REC_LNT] >= (DP_TRKLEN - dp_rptr - REC_OVHD)) || (dpxb[dp_rptr + REC_EXT] >= REC_MAXEXT)) { /* bad lnt or ext? */ dp_done (1, STA_UNSER); /* stop simulation */ return STOP_DPFMT; /* bad format */ } uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ if (dp_cw1 & CW1_RW) { /* write? */ if (uptr->flags & UNIT_WPRT) /* write protect? */ return dp_done (1, STA_WPRER); /* error */ dp_xip = dp_xip | XIP_WRT; /* write in progress */ dp_sta = dp_sta | STA_RDY; /* set ready */ if (dp_dma) /* if DMA/DMC, req chan */ SET_CH_REQ (ch); } else if (Q_DMA (ch)) /* read; DMA? */ dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ sim_activate (uptr, dp_xtime); /* schedule word */ dp_wptr = 0; /* init word pointer */ return SCPE_OK; case FNC_RW | FNC_2ND: /* read/write, word */ if (dp_cw1 & CW1_RW) { /* write? */ if (dp_sta & STA_RDY) /* timing failure? */ return dp_wrdone (uptr, STA_DTRER); /* yes, error */ if (r = dp_wrwd (uptr, dp_buf)) /* wr word, error? */ return r; if (dp_eor) { /* transfer done? */ dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; return dp_wrdone (uptr, 0); /* clear busy, intr req */ } } else { /* read? */ lnt = dpxb[dp_rptr + REC_LNT] + dpxb[dp_rptr + REC_EXT]; dp_buf = dpxb[dp_rptr + REC_DATA + dp_wptr];/* current word */ dp_csum = dp_csum ^ dp_buf; /* xor to csum */ if ((dp_wptr > lnt) || dp_eor) /* transfer done? */ return dp_done (1, (dp_csum? STA_CSMER: 0) | ((dp_wptr >= lnt)? STA_EOR: 0)); if (dp_sta & STA_RDY) /* data buf full? */ return dp_done (1, STA_DTRER); /* no, underrun */ dp_wptr++; /* next word */ } break; default: return SCPE_IERR; } /* end case */ dp_sta = dp_sta | STA_RDY; /* set ready */ if (dp_dma) /* if DMA/DMC, req chan */ SET_CH_REQ (ch); sim_activate (uptr, dp_xtime); /* schedule word */ return SCPE_OK; }
int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev) { int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ int32 u; UNIT *uptr; switch (inst) { /* case on opcode */ case ioOCP: /* OCP */ switch (fnc) { /* case on function */ case FNC_SK0: case FNC_SEEK: case FNC_RCA: /* data transfer */ case FNC_UNL: case FNC_FMT: case FNC_RW: dp_go (fnc); /* if !busy, start */ break; case FNC_STOP: /* stop transfer */ if (dp_xip) { /* transfer in prog? */ uptr = dp_dev.units + (dp_xip & XIP_UMSK); /* get unit */ sim_cancel (uptr); /* stop operation */ if (dp_xip & (XIP_WRT|XIP_FMT)) /* write or format? */ dp_wrdone (uptr, /* write track */ ((dp_xip & XIP_FMT) && /* check fmt state */ (uptr->FNC != (FNC_FMT|FNC_2ND)))? STA_DTRER: 0); else dp_done (1, dp_csum? STA_CSMER: 0);/* no, just clr busy */ dp_xip = 0; /* clear flag */ } dp_otas = OTA_NOP; /* clear state */ dp_sta = dp_sta & ~STA_BUSY; /* clear busy */ break; case FNC_RDS: /* read status */ if (dp_sta & STA_BUSY) /* ignore if busy */ return dat; dp_sta = (dp_sta | STA_RDY) & ~(STA_MBZ | STA_ANYER); if (dp_sta & STA_ALLERR) dp_sta = dp_sta | STA_ANYER; dp_buf = dp_sta; if (dp_dma && Q_DMA (ch)) /* DMA? set chan req */ SET_CH_REQ (ch); break; case FNC_DMA: /* set DMA/DMC */ dp_dma = 1; break; case FNC_IOBUS: /* set IO bus */ dp_dma = 0; break; case FNC_AKI: /* ack intr */ CLR_INT (INT_DP); break; default: /* undefined */ return IOBADFNC (dat); } break; case ioINA: /* INA */ if (fnc) /* fnc 0 only */ return IOBADFNC (dat); if (dp_sta & STA_RDY) { /* ready? */ dp_sta = dp_sta & ~STA_RDY; /* clear ready */ return IOSKIP (dat | dp_buf); /* ret buf, skip */ } break; case ioOTA: /* OTA */ if (fnc) /* fnc 0 only */ return IOBADFNC (dat); if (dp_sta & STA_RDY) { /* ready? */ dp_sta = dp_sta & ~STA_RDY; /* clear ready */ dp_buf = dat; /* store buf */ if (dp_otas == OTA_CW1) /* expecting CW1? */ dp_go1 (dat); else if (dp_otas == OTA_CW2) /* expecting CW2? */ dp_go2 (dat); return IOSKIP (dat); } break; case ioSKS: /* SKS */ u = 7; /* assume unit 7 */ switch (fnc) { case 000: /* ready */ if (dp_sta & STA_RDY) return IOSKIP (dat); break; case 001: /* !interrupting */ if (!TST_INTREQ (INT_DP)) return IOSKIP (dat); break; case 002: /* operational */ if (!(dp_sta & (STA_BUSY | STA_ALLERR))) return IOSKIP (dat); break; case 003: /* !error */ if (!(dp_sta & STA_ALLERR)) return IOSKIP (dat); break; case 004: /* !busy */ if (!(dp_sta & STA_BUSY)) return IOSKIP (dat); break; case 011: case 012: case 013: /* !not seeking 0-6 */ case 014: case 015: case 016: case 017: u = fnc - 011; case 007: /* !not seeking 7 */ if (!sim_is_active (&dp_unit[u]) || /* quiescent? */ (dp_unit[u].FNC != (FNC_SEEK | FNC_2ND))) return IOSKIP (dat); /* seeking sets late */ break; } break; case ioEND: /* end of range */ dp_eor = 1; /* transfer done */ break; } return dat; }