| /* pdp8_ct.c: PDP-8 cassette tape simulator | |
| Copyright (c) 2006-2011, Robert M Supnik | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| ct TA8E/TU60 cassette tape | |
| 13-Aug-07 RMS Fixed handling of BEOT | |
| 06-Aug-07 RMS Foward op at BOT skips initial file gap | |
| 30-May-2007 RMS Fixed typo (Norm Lastovica) | |
| Magnetic tapes are represented as a series of variable records | |
| of the form: | |
| 32b byte count | |
| byte 0 | |
| byte 1 | |
| : | |
| byte n-2 | |
| byte n-1 | |
| 32b byte count | |
| If the byte count is odd, the record is padded with an extra byte | |
| of junk. File marks are represented by a byte count of 0. | |
| Cassette format differs in one very significant way: it has file gaps | |
| rather than file marks. If the controller spaces or reads into a file | |
| gap and then reverses direction, the file gap is not seen again. This | |
| is in contrast to magnetic tapes, where the file mark is a character | |
| sequence and is seen again if direction is reversed. In addition, | |
| cassettes have an initial file gap which is automatically skipped on | |
| forward operations from beginning of tape. | |
| Note that the read and write sequences for the cassette are asymmetric: | |
| Read: KLSA /SELECT READ | |
| KGOA /INIT READ, CLEAR DF | |
| <data flag sets, char in buf> | |
| KGOA /READ 1ST CHAR, CLEAR DF | |
| DCA CHAR | |
| : | |
| <data flag sets, char in buf> | |
| KGOA /READ LAST CHAR, CLEAR DF | |
| DCA CHAR | |
| <data flag sets, CRC1 in buf> | |
| KLSA /SELECT CRC MODE | |
| KGOA /READ 1ST CRC | |
| <data flag sets, CRC2 in buf> | |
| KGOA /READ 2ND CRC | |
| <ready flag/CRC error flag sets> | |
| Write: KLSA /SELECT WRITE | |
| TAD CHAR /1ST CHAR | |
| KGOA /INIT WRITE, CHAR TO BUF, CLEAR DF | |
| <data flag sets, char to tape> | |
| : | |
| TAD CHAR /LAST CHAR | |
| KGOA /CHAR TO BUF, CLEAR DF | |
| <data flag sets, char to tape> | |
| KLSA /SELECT CRC MODE | |
| KGOA /WRITE CRC, CLEAR DF | |
| <ready flag sets, CRC on tape> | |
| */ | |
| #include "pdp8_defs.h" | |
| #include "sim_tape.h" | |
| #define CT_NUMDR 2 /* #drives */ | |
| #define FNC u3 /* unit function */ | |
| #define UST u4 /* unit status */ | |
| #define CT_MAXFR (CT_SIZE) /* max record lnt */ | |
| #define CT_SIZE 93000 /* chars/tape */ | |
| /* Status Register A */ | |
| #define SRA_ENAB 0200 /* enable */ | |
| #define SRA_V_UNIT 6 /* unit */ | |
| #define SRA_M_UNIT (CT_NUMDR - 1) | |
| #define SRA_V_FNC 3 /* function */ | |
| #define SRA_M_FNC 07 | |
| #define SRA_READ 00 | |
| #define SRA_REW 01 | |
| #define SRA_WRITE 02 | |
| #define SRA_SRF 03 | |
| #define SRA_WFG 04 | |
| #define SRA_SRB 05 | |
| #define SRA_CRC 06 | |
| #define SRA_SFF 07 | |
| #define SRA_2ND 010 | |
| #define SRA_IE 0001 /* int enable */ | |
| #define GET_UNIT(x) (((x) >> SRA_V_UNIT) & SRA_M_UNIT) | |
| #define GET_FNC(x) (((x) >> SRA_V_FNC) & SRA_M_FNC) | |
| /* Function code flags */ | |
| #define OP_WRI 01 /* op is a write */ | |
| #define OP_REV 02 /* op is rev motion */ | |
| #define OP_FWD 04 /* op is fwd motion */ | |
| /* Unit status flags */ | |
| #define UST_REV (OP_REV) /* last op was rev */ | |
| #define UST_GAP 01 /* last op hit gap */ | |
| /* Status Register B, ^ = computed on the fly */ | |
| #define SRB_WLE 0400 /* "write lock err" */ | |
| #define SRB_CRC 0200 /* CRC error */ | |
| #define SRB_TIM 0100 /* timing error */ | |
| #define SRB_BEOT 0040 /* ^BOT/EOT */ | |
| #define SRB_EOF 0020 /* end of file */ | |
| #define SRB_EMP 0010 /* ^drive empty */ | |
| #define SRB_REW 0004 /* rewinding */ | |
| #define SRB_WLK 0002 /* ^write locked */ | |
| #define SRB_RDY 0001 /* ^ready */ | |
| #define SRB_ALLERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_BEOT|SRB_EOF|SRB_EMP) | |
| #define SRB_XFRERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_EOF) | |
| extern int32 int_req, stop_inst; | |
| extern UNIT cpu_unit; | |
| extern FILE *sim_deb; | |
| uint32 ct_sra = 0; /* status reg A */ | |
| uint32 ct_srb = 0; /* status reg B */ | |
| uint32 ct_db = 0; /* data buffer */ | |
| uint32 ct_df = 0; /* data flag */ | |
| uint32 ct_write = 0; /* TU60 write flag */ | |
| uint32 ct_bptr = 0; /* buf ptr */ | |
| uint32 ct_blnt = 0; /* buf length */ | |
| int32 ct_stime = 1000; /* start time */ | |
| int32 ct_ctime = 100; /* char latency */ | |
| uint32 ct_stopioe = 1; /* stop on error */ | |
| uint8 *ct_xb = NULL; /* transfer buffer */ | |
| static uint8 ct_fnc_tab[SRA_M_FNC + 1] = { | |
| OP_FWD, 0 , OP_WRI|OP_FWD, OP_REV, | |
| OP_WRI|OP_FWD, OP_REV, 0, OP_FWD | |
| }; | |
| DEVICE ct_dev; | |
| int32 ct70 (int32 IR, int32 AC); | |
| t_stat ct_svc (UNIT *uptr); | |
| t_stat ct_reset (DEVICE *dptr); | |
| t_stat ct_attach (UNIT *uptr, char *cptr); | |
| t_stat ct_detach (UNIT *uptr); | |
| t_stat ct_boot (int32 unitno, DEVICE *dptr); | |
| uint32 ct_updsta (UNIT *uptr); | |
| int32 ct_go_start (int32 AC); | |
| int32 ct_go_cont (UNIT *uptr, int32 AC); | |
| t_stat ct_map_err (UNIT *uptr, t_stat st); | |
| UNIT *ct_busy (void); | |
| void ct_set_df (t_bool timchk); | |
| t_bool ct_read_char (void); | |
| uint32 ct_crc (uint8 *buf, uint32 cnt); | |
| /* CT data structures | |
| ct_dev CT device descriptor | |
| ct_unit CT unit list | |
| ct_reg CT register list | |
| ct_mod CT modifier list | |
| */ | |
| DIB ct_dib = { DEV_CT, 1, { &ct70 } }; | |
| UNIT ct_unit[] = { | |
| { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, | |
| { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, | |
| }; | |
| REG ct_reg[] = { | |
| { ORDATA (CTSRA, ct_sra, 8) }, | |
| { ORDATA (CTSRB, ct_srb, 8) }, | |
| { ORDATA (CTDB, ct_db, 8) }, | |
| { FLDATA (CTDF, ct_df, 0) }, | |
| { FLDATA (RDY, ct_srb, 0) }, | |
| { FLDATA (WLE, ct_srb, 8) }, | |
| { FLDATA (WRITE, ct_write, 0) }, | |
| { FLDATA (INT, int_req, INT_V_CT) }, | |
| { DRDATA (BPTR, ct_bptr, 17) }, | |
| { DRDATA (BLNT, ct_blnt, 17) }, | |
| { DRDATA (STIME, ct_stime, 24), PV_LEFT + REG_NZ }, | |
| { DRDATA (CTIME, ct_ctime, 24), PV_LEFT + REG_NZ }, | |
| { FLDATA (STOP_IOE, ct_stopioe, 0) }, | |
| { URDATA (UFNC, ct_unit[0].FNC, 8, 4, 0, CT_NUMDR, 0), REG_HRO }, | |
| { URDATA (UST, ct_unit[0].UST, 8, 2, 0, CT_NUMDR, 0), REG_HRO }, | |
| { URDATA (POS, ct_unit[0].pos, 10, T_ADDR_W, 0, | |
| CT_NUMDR, PV_LEFT | REG_RO) }, | |
| { FLDATA (DEVNUM, ct_dib.dev, 6), REG_HRO }, | |
| { NULL } | |
| }; | |
| MTAB ct_mod[] = { | |
| { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, | |
| { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, | |
| // { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", | |
| // &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, | |
| { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, | |
| NULL, &sim_tape_show_capac, NULL }, | |
| { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", | |
| &set_dev, &show_dev, NULL }, | |
| { 0 } | |
| }; | |
| DEVICE ct_dev = { | |
| "CT", ct_unit, ct_reg, ct_mod, | |
| CT_NUMDR, 10, 31, 1, 8, 8, | |
| NULL, NULL, &ct_reset, | |
| &ct_boot, &ct_attach, &ct_detach, | |
| &ct_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | |
| }; | |
| /* IOT routines */ | |
| int32 ct70 (int32 IR, int32 AC) | |
| { | |
| int32 srb; | |
| UNIT *uptr; | |
| srb = ct_updsta (NULL); /* update status */ | |
| switch (IR & 07) { /* decode IR<9:11> */ | |
| case 0: /* KCLR */ | |
| ct_reset (&ct_dev); /* reset the world */ | |
| break; | |
| case 1: /* KSDR */ | |
| if (ct_df) | |
| AC |= IOT_SKP; | |
| break; | |
| case 2: /* KSEN */ | |
| if (srb & SRB_ALLERR) | |
| AC |= IOT_SKP; | |
| break; | |
| case 3: /* KSBF */ | |
| if ((srb & SRB_RDY) && !(srb & SRB_EMP)) | |
| AC |= IOT_SKP; | |
| break; | |
| case 4: /* KLSA */ | |
| ct_sra = AC & 0377; | |
| ct_updsta (NULL); | |
| return ct_sra ^ 0377; | |
| case 5: /* KSAF */ | |
| if (ct_df || (srb & (SRB_ALLERR|SRB_RDY))) | |
| AC |= IOT_SKP; | |
| break; | |
| case 6: /* KGOA */ | |
| ct_df = 0; /* clear data flag */ | |
| if ((uptr = ct_busy ())) /* op in progress? */ | |
| AC = ct_go_cont (uptr, AC); /* yes */ | |
| else AC = ct_go_start (AC); /* no, start */ | |
| ct_updsta (NULL); | |
| break; | |
| case 7: /* KSRB */ | |
| return srb & 0377; | |
| } /* end switch */ | |
| return AC; | |
| } | |
| /* Start a new operation - cassette is not busy */ | |
| int32 ct_go_start (int32 AC) | |
| { | |
| UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra); | |
| uint32 fnc = GET_FNC (ct_sra); | |
| uint32 flg = ct_fnc_tab[fnc]; | |
| uint32 old_ust = uptr->UST; | |
| if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, | |
| ">>CT start: op=%o, old_sta = %o, pos=%d\n", | |
| fnc, uptr->UST, uptr->pos); | |
| if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) { /* enabled, att? */ | |
| ct_srb &= ~(SRB_XFRERR|SRB_REW); /* clear err, rew */ | |
| if (flg & OP_WRI) { /* write-type op? */ | |
| if (sim_tape_wrp (uptr)) { /* locked? */ | |
| ct_srb |= SRB_WLE; /* set flag, abort */ | |
| return AC; | |
| } | |
| ct_write = 1; /* set TU60 wr flag */ | |
| ct_db = AC & 0377; | |
| } | |
| else { | |
| ct_write = 0; | |
| ct_db = 0; | |
| } | |
| ct_srb &= ~SRB_BEOT; /* tape in motion */ | |
| if (fnc == SRA_REW) /* rew? set flag */ | |
| ct_srb |= SRB_REW; | |
| if ((fnc != SRA_REW) && !(flg & OP_WRI)) { /* read cmd? */ | |
| t_mtrlnt t; | |
| t_stat st; | |
| uptr->UST = flg & UST_REV; /* save direction */ | |
| if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ | |
| st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */ | |
| if (st != MTSE_TMK) /* not there? */ | |
| sim_tape_rewind (uptr); /* restore tap pos */ | |
| else old_ust = 0; /* defang next */ | |
| } | |
| if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */ | |
| if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, | |
| ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n", | |
| fnc, uptr->UST, uptr->pos); | |
| if (uptr->UST) /* skip file gap */ | |
| sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR); | |
| else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); | |
| } | |
| } | |
| else uptr->UST = 0; | |
| ct_bptr = 0; /* init buffer */ | |
| ct_blnt = 0; | |
| uptr->FNC = fnc; /* save function */ | |
| sim_activate (uptr, ct_stime); /* schedule op */ | |
| } | |
| if ((fnc == SRA_READ) || (fnc == SRA_CRC)) /* read or CRC? */ | |
| return 0; /* get "char" */ | |
| return AC; | |
| } | |
| /* Continue an in-progress operation - cassette is in motion */ | |
| int32 ct_go_cont (UNIT *uptr, int32 AC) | |
| { | |
| int32 fnc = GET_FNC (ct_sra); | |
| switch (fnc) { /* case on function */ | |
| case SRA_READ: /* read */ | |
| return ct_db; /* return data */ | |
| case SRA_WRITE: /* write */ | |
| ct_db = AC & 0377; /* save data */ | |
| break; | |
| case SRA_CRC: /* CRC */ | |
| if ((uptr->FNC & SRA_M_FNC) != SRA_CRC) /* if not CRC */ | |
| uptr->FNC = SRA_CRC; /* start CRC seq */ | |
| if (!ct_write) /* read? AC <- buf */ | |
| return ct_db; | |
| break; | |
| default: | |
| break; | |
| } | |
| return AC; | |
| } | |
| /* Unit service */ | |
| t_stat ct_svc (UNIT *uptr) | |
| { | |
| uint32 i, crc; | |
| uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC]; | |
| t_mtrlnt tbc; | |
| t_stat st, r; | |
| if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ | |
| ct_updsta (uptr); /* update status */ | |
| return (ct_stopioe? SCPE_UNATT: SCPE_OK); | |
| } | |
| if (((flgs & OP_REV) && sim_tape_bot (uptr)) || /* rev at BOT or */ | |
| ((flgs & OP_FWD) && sim_tape_eot (uptr))) { /* fwd at EOT? */ | |
| ct_srb |= SRB_BEOT; /* error */ | |
| ct_updsta (uptr); /* op done */ | |
| return SCPE_OK; | |
| } | |
| r = SCPE_OK; | |
| switch (uptr->FNC) { /* case on function */ | |
| case SRA_READ: /* read start */ | |
| st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */ | |
| if (st == MTSE_RECE) /* rec in err? */ | |
| ct_srb |= SRB_CRC; | |
| else if (st != MTSE_OK) { /* other error? */ | |
| r = ct_map_err (uptr, st); /* map error */ | |
| break; | |
| } | |
| crc = ct_crc (ct_xb, ct_blnt); /* calculate CRC */ | |
| ct_xb[ct_blnt++] = (crc >> 8) & 0377; /* append to buffer */ | |
| ct_xb[ct_blnt++] = crc & 0377; | |
| uptr->FNC |= SRA_2ND; /* next state */ | |
| sim_activate (uptr, ct_ctime); /* sched next char */ | |
| return SCPE_OK; | |
| case SRA_READ|SRA_2ND: /* read char */ | |
| if (!ct_read_char ()) /* read, overrun? */ | |
| break; | |
| ct_set_df (TRUE); /* set data flag */ | |
| sim_activate (uptr, ct_ctime); /* sched next char */ | |
| return SCPE_OK; | |
| case SRA_WRITE: /* write start */ | |
| for (i = 0; i < CT_MAXFR; i++) /* clear buffer */ | |
| ct_xb[i] = 0; | |
| uptr->FNC |= SRA_2ND; /* next state */ | |
| sim_activate (uptr, ct_ctime); /* sched next char */ | |
| return SCPE_OK; | |
| case SRA_WRITE|SRA_2ND: /* write char */ | |
| if ((ct_bptr < CT_MAXFR) && /* room in buf? */ | |
| ((uptr->pos + ct_bptr) < uptr->capac)) /* room on tape? */ | |
| ct_xb[ct_bptr++] = ct_db; /* store char */ | |
| ct_set_df (TRUE); /* set data flag */ | |
| sim_activate (uptr, ct_ctime); /* sched next char */ | |
| return SCPE_OK; | |
| case SRA_CRC: /* CRC */ | |
| if (ct_write) { /* write? */ | |
| if ((st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr)))/* write, err? */ | |
| r = ct_map_err (uptr, st); /* map error */ | |
| break; /* write done */ | |
| } | |
| ct_read_char (); /* get second CRC */ | |
| ct_set_df (FALSE); /* set df */ | |
| uptr->FNC |= SRA_2ND; /* next state */ | |
| sim_activate (uptr, ct_ctime); | |
| return SCPE_OK; | |
| case SRA_CRC|SRA_2ND: /* second read CRC */ | |
| if (ct_bptr != ct_blnt) { /* partial read? */ | |
| crc = ct_crc (ct_xb, ct_bptr); /* actual CRC */ | |
| if (crc != 0) /* must be zero */ | |
| ct_srb |= SRB_CRC; | |
| } | |
| break; /* read done */ | |
| case SRA_WFG: /* write file gap */ | |
| if ((st = sim_tape_wrtmk (uptr))) /* write tmk, err? */ | |
| r = ct_map_err (uptr, st); /* map error */ | |
| break; | |
| case SRA_REW: /* rewind */ | |
| sim_tape_rewind (uptr); | |
| ct_srb |= SRB_BEOT; /* set BOT */ | |
| break; | |
| case SRA_SRB: /* space rev blk */ | |
| if ((st = sim_tape_sprecr (uptr, &tbc))) /* space rev, err? */ | |
| r = ct_map_err (uptr, st); /* map error */ | |
| break; | |
| case SRA_SRF: /* space rev file */ | |
| while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; | |
| r = ct_map_err (uptr, st); /* map error */ | |
| break; | |
| case SRA_SFF: /* space fwd file */ | |
| while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; | |
| r = ct_map_err (uptr, st); /* map error */ | |
| break; | |
| default: /* never get here! */ | |
| return SCPE_IERR; | |
| } /* end case */ | |
| ct_updsta (uptr); /* update status */ | |
| if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, | |
| ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n", | |
| uptr->FNC, ct_sra, ct_srb, uptr->pos); | |
| return r; | |
| } | |
| /* Update controller status */ | |
| uint32 ct_updsta (UNIT *uptr) | |
| { | |
| int32 srb; | |
| if (uptr == NULL) { /* unit specified? */ | |
| uptr = ct_busy (); /* use busy unit */ | |
| if ((uptr == NULL) && (ct_sra & SRA_ENAB)) /* none busy? */ | |
| uptr = ct_dev.units + GET_UNIT (ct_sra); /* use sel unit */ | |
| } | |
| else if (ct_srb & SRB_EOF) /* save gap */ | |
| uptr->UST |= UST_GAP; | |
| if (uptr) { /* any unit? */ | |
| ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY); /* clear dyn flags */ | |
| if ((uptr->flags & UNIT_ATT) == 0) /* unattached? */ | |
| ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */ | |
| if (!sim_is_active (uptr)) { /* not busy? */ | |
| ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW; /* ready, ~rew */ | |
| } | |
| if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW)) /* locked or rew? */ | |
| ct_srb |= SRB_WLK; /* set locked */ | |
| } | |
| if (ct_sra & SRA_ENAB) /* can TA see TU60? */ | |
| srb = ct_srb; | |
| else srb = 0; /* no */ | |
| if ((ct_sra & SRA_IE) && /* int enabled? */ | |
| (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))) /* any flag? */ | |
| int_req |= INT_CT; /* set int req */ | |
| else int_req &= ~INT_CT; /* no, clr int req */ | |
| return srb; | |
| } | |
| /* Set data flag */ | |
| void ct_set_df (t_bool timchk) | |
| { | |
| if (ct_df && timchk) /* flag still set? */ | |
| ct_srb |= SRB_TIM; | |
| ct_df = 1; /* set data flag */ | |
| if (ct_sra & SRA_IE) /* if ie, int req */ | |
| int_req |= INT_CT; | |
| return; | |
| } | |
| /* Read character */ | |
| t_bool ct_read_char (void) | |
| { | |
| if (ct_bptr < ct_blnt) { /* more chars? */ | |
| ct_db = ct_xb[ct_bptr++]; | |
| return TRUE; | |
| } | |
| ct_db = 0; | |
| ct_srb |= SRB_CRC; /* overrun */ | |
| return FALSE; | |
| } | |
| /* Test if controller busy */ | |
| UNIT *ct_busy (void) | |
| { | |
| uint32 u; | |
| UNIT *uptr; | |
| for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ | |
| uptr = ct_dev.units + u; | |
| if (sim_is_active (uptr)) | |
| return uptr; | |
| } | |
| return NULL; | |
| } | |
| /* Calculate CRC on buffer */ | |
| uint32 ct_crc (uint8 *buf, uint32 cnt) | |
| { | |
| uint32 crc, i, j; | |
| crc = 0; | |
| for (i = 0; i < cnt; i++) { | |
| crc = crc ^ (((uint32) buf[i]) << 8); | |
| for (j = 0; j < 8; j++) { | |
| if (crc & 1) | |
| crc = (crc >> 1) ^ 0xA001; | |
| else crc = crc >> 1; | |
| } | |
| } | |
| return crc; | |
| } | |
| /* Map error status */ | |
| t_stat ct_map_err (UNIT *uptr, t_stat st) | |
| { | |
| switch (st) { | |
| case MTSE_FMT: /* illegal fmt */ | |
| case MTSE_UNATT: /* unattached */ | |
| ct_srb |= SRB_CRC; | |
| case MTSE_OK: /* no error */ | |
| return SCPE_IERR; /* never get here! */ | |
| case MTSE_TMK: /* end of file */ | |
| ct_srb |= SRB_EOF; | |
| break; | |
| case MTSE_IOERR: /* IO error */ | |
| ct_srb |= SRB_CRC; /* set crc err */ | |
| if (ct_stopioe) | |
| return SCPE_IOERR; | |
| break; | |
| case MTSE_INVRL: /* invalid rec lnt */ | |
| ct_srb |= SRB_CRC; /* set crc err */ | |
| return SCPE_MTRLNT; | |
| case MTSE_RECE: /* record in error */ | |
| case MTSE_EOM: /* end of medium */ | |
| ct_srb |= SRB_CRC; /* set crc err */ | |
| break; | |
| case MTSE_BOT: /* reverse into BOT */ | |
| ct_srb |= SRB_BEOT; /* set BOT */ | |
| break; | |
| case MTSE_WRP: /* write protect */ | |
| ct_srb |= SRB_WLE; /* set wlk err */ | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Reset routine */ | |
| t_stat ct_reset (DEVICE *dptr) | |
| { | |
| uint32 u; | |
| UNIT *uptr; | |
| ct_sra = 0; | |
| ct_srb = 0; | |
| ct_df = 0; | |
| ct_db = 0; | |
| ct_write = 0; | |
| ct_bptr = 0; | |
| ct_blnt = 0; | |
| int_req = int_req & ~INT_CT; /* clear interrupt */ | |
| for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ | |
| uptr = ct_dev.units + u; | |
| sim_cancel (uptr); /* cancel activity */ | |
| sim_tape_reset (uptr); /* reset tape */ | |
| } | |
| if (ct_xb == NULL) | |
| ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8)); | |
| if (ct_xb == NULL) | |
| return SCPE_MEM; | |
| return SCPE_OK; | |
| } | |
| /* Attach routine */ | |
| t_stat ct_attach (UNIT *uptr, char *cptr) | |
| { | |
| t_stat r; | |
| r = sim_tape_attach (uptr, cptr); | |
| if (r != SCPE_OK) | |
| return r; | |
| ct_updsta (NULL); | |
| uptr->UST = 0; | |
| return r; | |
| } | |
| /* Detach routine */ | |
| t_stat ct_detach (UNIT* uptr) | |
| { | |
| t_stat r; | |
| if (!(uptr->flags & UNIT_ATT)) /* check attached */ | |
| return SCPE_OK; | |
| r = sim_tape_detach (uptr); | |
| ct_updsta (NULL); | |
| uptr->UST = 0; | |
| return r; | |
| } | |
| /* Bootstrap routine */ | |
| #define BOOT_START 04000 | |
| #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) | |
| static const uint16 boot_rom[] = { | |
| 01237, /* BOOT, TAD M50 /change CRC to REW */ | |
| 01206, /* CRCCHK, TAD L260 /crc op */ | |
| 06704, /* KLSA /load op */ | |
| 06706, /* KGOA /start */ | |
| 06703, /* KSBF /ready? */ | |
| 05204, /* RDCOD, JMP .-1 /loop */ | |
| 07264, /* L260, CML STA RAL /L = 1, AC = halt */ | |
| 06702, /* KSEN /error? */ | |
| 07610, /* SKP CLA /halt on any error */ | |
| 03211, /* DCA . /except REW or FFG */ | |
| 03636, /* DCA I PTR /TAD I PTR mustn't change L */ | |
| 01205, /* TAD RDCOD /read op */ | |
| 06704, /* KLSA /load op */ | |
| 06706, /* KGOA /start */ | |
| 06701, /* LOOP, KSDF /data ready? */ | |
| 05216, /* JMP .-1 /loop */ | |
| 07002, /* BSW /to upper 6b */ | |
| 07430, /* SZL /second byte? */ | |
| 01636, /* TAD I PTR /yes */ | |
| 07022, /* CML BSW /swap back */ | |
| 03636, /* DCA I PTR /store in mem */ | |
| 07420, /* SNL /done with both bytes? */ | |
| 02236, /* ISZ PTR /yes, bump mem ptr */ | |
| 02235, /* ISZ KNT /done with record? */ | |
| 05215, /* JMP LOOP /next byte */ | |
| 07346, /* STA CLL RTL */ | |
| 07002, /* BSW /AC = 7757 */ | |
| 03235, /* STA KNT /now read 200 byte record */ | |
| 05201, /* JMP CRCCHK /go check CRC */ | |
| 07737, /* KNT, 7737 /1's compl of byte count */ | |
| 03557, /* PTR, 3557 /load point */ | |
| 07730, /* M50, 7730 /CLA SPA SZL */ | |
| }; | |
| t_stat ct_boot (int32 unitno, DEVICE *dptr) | |
| { | |
| int32 i; | |
| extern int32 saved_PC; | |
| extern uint16 M[]; | |
| if ((ct_dib.dev != DEV_CT) || unitno) /* only std devno */ | |
| return STOP_NOTSTD; | |
| for (i = 0; i < BOOT_LEN; i++) | |
| M[BOOT_START + i] = boot_rom[i]; | |
| saved_PC = BOOT_START; | |
| return SCPE_OK; | |
| } |