~ubuntu-branches/ubuntu/utopic/simh/utopic

« back to all changes in this revision

Viewing changes to PDP11/pdp11_tc.c

  • Committer: Bazaar Package Importer
  • Author(s): Vince Mulhollon
  • Date: 2004-04-20 20:01:26 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040420200126-ehsuleda8xcgi51h
Tags: 3.2.0-1
New upstream 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* pdp11_tc.c: PDP-11 DECtape simulator
 
2
 
 
3
   Copyright (c) 1993-2004, Robert M Supnik
 
4
 
 
5
   Permission is hereby granted, free of charge, to any person obtaining a
 
6
   copy of this software and associated documentation files (the "Software"),
 
7
   to deal in the Software without restriction, including without limitation
 
8
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
9
   and/or sell copies of the Software, and to permit persons to whom the
 
10
   Software is furnished to do so, subject to the following conditions:
 
11
 
 
12
   The above copyright notice and this permission notice shall be included in
 
13
   all copies or substantial portions of the Software.
 
14
 
 
15
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 
18
   ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 
19
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
20
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
21
 
 
22
   Except as contained in this notice, the name of Robert M Supnik shall not
 
23
   be used in advertising or otherwise to promote the sale, use or other dealings
 
24
   in this Software without prior written authorization from Robert M Supnik.
 
25
 
 
26
   tc           TC11/TU56 DECtape
 
27
 
 
28
   25-Jan-04    RMS     Revised for device debug support
 
29
   09-Jan-04    RMS     Changed sim_fsize calling sequence, added STOP_OFFR
 
30
   29-Dec-03    RMS     Changed initial status to disabled (in Qbus system)
 
31
   18-Oct-03    RMS     Fixed reverse checksum in read all
 
32
                        Added DECtape off reel message
 
33
                        Simplified timing
 
34
   25-Apr-03    RMS     Revised for extended file support
 
35
   14-Mar-03    RMS     Fixed variable size interaction with save/restore
 
36
   29-Sep-02    RMS     Added variable address support to bootstrap
 
37
                        Added vector change/display support
 
38
                        Added 16b format support
 
39
                        New data structures
 
40
   30-May-02    RMS     Widened POS to 32b
 
41
   26-Jan-02    RMS     Revised bootstrap to conform to M9312
 
42
   06-Jan-02    RMS     Revised enable/disable support
 
43
   30-Nov-01    RMS     Added read only unit, extended SET/SHOW support
 
44
   24-Nov-01    RMS     Converted POS, STATT, LASTT to arrays
 
45
   09-Nov-01    RMS     Added bus map support
 
46
   15-Sep-01    RMS     Integrated debug logging
 
47
   27-Sep-01    RMS     Fixed interrupt after stop for RSTS/E
 
48
   07-Sep-01    RMS     Revised device disable and interrupt mechanisms
 
49
   29-Aug-01    RMS     Added casts to PDP-8 unpack routine
 
50
   17-Jul-01    RMS     Moved function prototype
 
51
   11-May-01    RMS     Fixed bug in reset
 
52
   26-Apr-01    RMS     Added device enable/disable support
 
53
   18-Apr-01    RMS     Changed to rewind tape before boot
 
54
   16-Mar-01    RMS     Fixed bug in interrupt after stop
 
55
   15-Mar-01    RMS     Added 129th word to PDP-8 format
 
56
 
 
57
   PDP-11 DECtapes are represented in memory by fixed length buffer of 32b words.
 
58
   Three file formats are supported:
 
59
 
 
60
        18b/36b                 256 words per block [256 x 18b]
 
61
        16b                     256 words per block [256 x 16b]
 
62
        12b                     129 words per block [129 x 12b]
 
63
 
 
64
   When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format.
 
65
 
 
66
   DECtape motion is measured in 3b lines.  Time between lines is 33.33us.
 
67
   Tape density is nominally 300 lines per inch.  The format of a DECtape (as
 
68
   taken from the TD8E formatter) is:
 
69
 
 
70
        reverse end zone        8192 reverse end zone codes ~ 10 feet
 
71
        reverse buffer          200 interblock codes
 
72
        block 0
 
73
         :
 
74
        block n
 
75
        forward buffer          200 interblock codes
 
76
        forward end zone        8192 forward end zone codes ~ 10 feet
 
77
 
 
78
   A block consists of five 18b header words, a tape-specific number of data
 
79
   words, and five 18b trailer words.  All systems except the PDP-8 use a
 
80
   standard block length of 256 words; the PDP-8 uses a standard block length
 
81
   of 86 words (x 18b = 129 words x 12b).
 
82
 
 
83
   Because a DECtape file only contains data, the simulator cannot support
 
84
   write timing and mark track and can only do a limited implementation
 
85
   of read all and write all.  Read all assumes that the tape has been
 
86
   conventionally written forward:
 
87
 
 
88
        header word 0           0
 
89
        header word 1           block number (for forward reads)
 
90
        header words 2,3        0
 
91
        header word 4           checksum (for reverse reads)
 
92
        :
 
93
        trailer word 4          checksum (for forward reads)
 
94
        trailer words 3,2       0
 
95
        trailer word 1          block number (for reverse reads)
 
96
        trailer word 0          0
 
97
 
 
98
   Write all writes only the data words and dumps the interblock words in the
 
99
   bit bucket.
 
100
*/
 
101
 
 
102
#include "pdp11_defs.h"
 
103
 
 
104
#define DT_NUMDR        8                               /* #drives */
 
105
#define DT_M_NUMDR      (DT_NUMDR - 1)
 
106
#define UNIT_V_WLK      (UNIT_V_UF + 0)                 /* write locked */
 
107
#define UNIT_V_8FMT     (UNIT_V_UF + 1)                 /* 12b format */
 
108
#define UNIT_V_11FMT    (UNIT_V_UF + 2)                 /* 16b format */
 
109
#define UNIT_WLK        (1 << UNIT_V_WLK)
 
110
#define UNIT_8FMT       (1 << UNIT_V_8FMT)
 
111
#define UNIT_11FMT      (1 << UNIT_V_11FMT)
 
112
#define STATE           u3                              /* unit state */
 
113
#define LASTT           u4                              /* last time update */
 
114
#define UNIT_WPRT       (UNIT_WLK | UNIT_RO)            /* write protect */
 
115
 
 
116
/* System independent DECtape constants */
 
117
 
 
118
#define DT_LPERMC       6                               /* lines per mark track */
 
119
#define DT_BLKWD        1                               /* blk no word in h/t */
 
120
#define DT_CSMWD        4                               /* checksum word in h/t */
 
121
#define DT_HTWRD        5                               /* header/trailer words */
 
122
#define DT_EZLIN        (8192 * DT_LPERMC)              /* end zone length */
 
123
#define DT_BFLIN        (200 * DT_LPERMC)               /* buffer length */
 
124
#define DT_BLKLN        (DT_BLKWD * DT_LPERMC)          /* blk no line in h/t */
 
125
#define DT_CSMLN        (DT_CSMWD * DT_LPERMC)          /* csum line in h/t */
 
126
#define DT_HTLIN        (DT_HTWRD * DT_LPERMC)          /* header/trailer lines */
 
127
 
 
128
/* 16b, 18b, 36b DECtape constants */
 
129
 
 
130
#define D18_WSIZE       6                               /* word size in lines */
 
131
#define D18_BSIZE       256                             /* block size in 18b */
 
132
#define D18_TSIZE       578                             /* tape size */
 
133
#define D18_LPERB       (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
 
134
#define D18_FWDEZ       (DT_EZLIN + (D18_LPERB * D18_TSIZE))
 
135
#define D18_CAPAC       (D18_TSIZE * D18_BSIZE)         /* tape capacity */
 
136
#define D16_FILSIZ      (D18_TSIZE * D18_BSIZE * sizeof (int16))
 
137
 
 
138
/* 12b DECtape constants */
 
139
 
 
140
#define D8_WSIZE        4                               /* word size in lines */
 
141
#define D8_BSIZE        86                              /* block size in 18b */
 
142
#define D8_TSIZE        1474                            /* tape size */
 
143
#define D8_LPERB        (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
 
144
#define D8_FWDEZ        (DT_EZLIN + (D8_LPERB * D8_TSIZE))
 
145
#define D8_CAPAC        (D8_TSIZE * D8_BSIZE)           /* tape capacity */
 
146
 
 
147
#define D8_NBSIZE       ((D8_BSIZE * D18_WSIZE) / D8_WSIZE)
 
148
#define D8_FILSIZ       (D8_NBSIZE * D8_TSIZE * sizeof (int16))
 
149
 
 
150
/* This controller */
 
151
 
 
152
#define DT_CAPAC        D18_CAPAC                       /* default */
 
153
#define DT_WSIZE        D18_WSIZE
 
154
 
 
155
/* Calculated constants, per unit */
 
156
 
 
157
#define DTU_BSIZE(u)    (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
 
158
#define DTU_TSIZE(u)    (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
 
159
#define DTU_LPERB(u)    (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
 
160
#define DTU_FWDEZ(u)    (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
 
161
#define DTU_CAPAC(u)    (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)
 
162
 
 
163
#define DT_LIN2BL(p,u)  (((p) - DT_EZLIN) / DTU_LPERB (u))
 
164
#define DT_LIN2OF(p,u)  (((p) - DT_EZLIN) % DTU_LPERB (u))
 
165
#define DT_LIN2WD(p,u)  ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE)
 
166
#define DT_BLK2LN(p,u)  (((p) * DTU_LPERB (u)) + DT_EZLIN)
 
167
#define DT_QREZ(u)      (((u)->pos) < DT_EZLIN)
 
168
#define DT_QFEZ(u)      (((u)->pos) >= ((uint32) DTU_FWDEZ (u)))
 
169
#define DT_QEZ(u)       (DT_QREZ (u) || DT_QFEZ (u))
 
170
 
 
171
/* TCST - 177340 - status register */
 
172
 
 
173
#define STA_END         0100000                         /* end zone */
 
174
#define STA_PAR         0040000                         /* parity err */
 
175
#define STA_MRK         0020000                         /* mark trk err */
 
176
#define STA_ILO         0010000                         /* illegal op */
 
177
#define STA_SEL         0004000                         /* select err */
 
178
#define STA_BLKM        0002000                         /* block miss err */
 
179
#define STA_DATM        0001000                         /* data miss err */
 
180
#define STA_NXM         0000400                         /* nx mem err */
 
181
#define STA_UPS         0000200                         /* up to speed */
 
182
#define STA_V_XD        0                               /* extended data */
 
183
#define STA_M_XD        03
 
184
#define STA_ALLERR      (STA_END | STA_PAR | STA_MRK | STA_ILO | \
 
185
                         STA_SEL | STA_BLKM | STA_DATM | STA_NXM )
 
186
#define STA_RWERR       (STA_END | STA_PAR | STA_MRK | \
 
187
                         STA_BLKM | STA_DATM | STA_NXM )
 
188
#define STA_RW          0000003
 
189
#define STA_GETXD(x)    (((x) >> STA_V_XD) & STA_M_XD)
 
190
 
 
191
/* TCCM - 177342 - command register */
 
192
 
 
193
/* #define CSR_ERR      0100000 */
 
194
#define CSR_MNT         0020000                         /* maint (unimpl) */
 
195
#define CSR_INH         0010000                         /* delay inhibit */
 
196
#define CSR_DIR         0004000                         /* reverse */
 
197
#define CSR_V_UNIT      8                               /* unit select */
 
198
#define CSR_M_UNIT      07
 
199
#define CSR_UNIT        (CSR_M_UNIT << CSR_V_UNIT)
 
200
/* #define CSR_DONE     0000200 */
 
201
/* #define CSR_IE       0000100 */
 
202
#define CSR_V_MEX       4                               /* mem extension */
 
203
#define CSR_M_MEX       03
 
204
#define CSR_MEX         (CSR_M_MEX << CSR_V_MEX)
 
205
#define CSR_V_FNC       1                               /* function */
 
206
#define CSR_M_FNC       07
 
207
#define  FNC_STOP        00                             /* stop all */
 
208
#define  FNC_SRCH        01                             /* search */
 
209
#define  FNC_READ        02                             /* read */
 
210
#define  FNC_RALL        03                             /* read all */
 
211
#define  FNC_SSEL        04                             /* stop selected */
 
212
#define  FNC_WMRK        05                             /* write */
 
213
#define  FNC_WRIT        06                             /* write all */
 
214
#define  FNC_WALL        07                             /* write timing */
 
215
/* define CSR_GO        0000001 */
 
216
#define CSR_RW          0117576                         /* read/write */
 
217
 
 
218
#define CSR_GETUNIT(x)  (((x) >> CSR_V_UNIT) & CSR_M_UNIT)
 
219
#define CSR_GETMEX(x)   (((x) >> CSR_V_MEX) & CSR_M_MEX)
 
220
#define CSR_GETFNC(x)   (((x) >> CSR_V_FNC) & CSR_M_FNC)
 
221
#define CSR_INCMEX(x)   (((x) & ~CSR_MEX) | (((x) + (1 << CSR_V_MEX)) & CSR_MEX))
 
222
 
 
223
/* TCWC - 177344 - word count */
 
224
 
 
225
/* TCBA - 177346 - bus address */
 
226
 
 
227
/* TCDT - 177350 - data */
 
228
 
 
229
/* DECtape state */
 
230
 
 
231
#define DTS_V_MOT       3                               /* motion */
 
232
#define DTS_M_MOT       07
 
233
#define  DTS_STOP        0                              /* stopped */
 
234
#define  DTS_DECF        2                              /* decel, fwd */
 
235
#define  DTS_DECR        3                              /* decel, rev */
 
236
#define  DTS_ACCF        4                              /* accel, fwd */
 
237
#define  DTS_ACCR        5                              /* accel, rev */
 
238
#define  DTS_ATSF        6                              /* @speed, fwd */
 
239
#define  DTS_ATSR        7                              /* @speed, rev */
 
240
#define DTS_DIR         01                              /* dir mask */
 
241
#define DTS_V_FNC       0                               /* function */
 
242
#define DTS_M_FNC       07
 
243
#define  DTS_OFR        FNC_WMRK                        /* "off reel" */
 
244
#define DTS_GETMOT(x)   (((x) >> DTS_V_MOT) & DTS_M_MOT)
 
245
#define DTS_GETFNC(x)   (((x) >> DTS_V_FNC) & DTS_M_FNC)
 
246
#define DTS_V_2ND       6                               /* next state */
 
247
#define DTS_V_3RD       (DTS_V_2ND + DTS_V_2ND)         /* next next */
 
248
#define DTS_STA(y,z)    (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC))
 
249
#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z)
 
250
#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \
 
251
                                ((DTS_STA (y, z)) << DTS_V_2ND)
 
252
#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \
 
253
                                ((DTS_STA (y, z)) << DTS_V_3RD)
 
254
#define DTS_NXTSTA(x)   (x >> DTS_V_2ND)
 
255
 
 
256
/* Logging */
 
257
 
 
258
#define LOG_MS          0x1
 
259
#define LOG_RW          0x2
 
260
#define LOG_BL          0x4
 
261
 
 
262
#define DT_SETDONE      tccm = tccm | CSR_DONE; \
 
263
                        if (tccm & CSR_IE) SET_INT (DTA)
 
264
#define DT_CLRDONE      tccm = tccm & ~CSR_DONE; \
 
265
                        CLR_INT (DTA)
 
266
#define ABS(x)          (((x) < 0)? (-(x)): (x))
 
267
 
 
268
extern uint16 *M;                                       /* memory */
 
269
extern int32 int_req[IPL_HLVL];
 
270
extern int32 int_vec[IPL_HLVL][32];
 
271
extern UNIT cpu_unit;
 
272
extern int32 sim_switches;
 
273
extern FILE *sim_deb;
 
274
 
 
275
int32 tcst = 0;                                         /* status */
 
276
int32 tccm = 0;                                         /* command */
 
277
int32 tcwc = 0;                                         /* word count */
 
278
int32 tcba = 0;                                         /* bus address */
 
279
int32 tcdt = 0;                                         /* data */
 
280
int32 dt_ctime = 100;                                   /* fast cmd time */
 
281
int32 dt_ltime = 12;                                    /* interline time */
 
282
int32 dt_dctime = 40000;                                /* decel time */
 
283
int32 dt_substate = 0;
 
284
int32 dt_logblk = 0;
 
285
int32 dt_stopoffr = 0;
 
286
 
 
287
DEVICE dt_dev;
 
288
t_stat dt_rd (int32 *data, int32 PA, int32 access);
 
289
t_stat dt_wr (int32 data, int32 PA, int32 access);
 
290
t_stat dt_svc (UNIT *uptr);
 
291
t_stat dt_svcdone (UNIT *uptr);
 
292
t_stat dt_reset (DEVICE *dptr);
 
293
t_stat dt_attach (UNIT *uptr, char *cptr);
 
294
t_stat dt_detach (UNIT *uptr);
 
295
t_stat dt_boot (int32 unitno, DEVICE *dptr);
 
296
void dt_deselect (int32 oldf);
 
297
void dt_newsa (int32 newf);
 
298
void dt_newfnc (UNIT *uptr, int32 newsta);
 
299
t_bool dt_setpos (UNIT *uptr);
 
300
void dt_schedez (UNIT *uptr, int32 dir);
 
301
void dt_seterr (UNIT *uptr, int32 e);
 
302
void dt_stopunit (UNIT *uptr);
 
303
int32 dt_comobv (int32 val);
 
304
int32 dt_csum (UNIT *uptr, int32 blk);
 
305
int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos);
 
306
extern int32 sim_is_running;
 
307
 
 
308
/* DT data structures
 
309
 
 
310
   dt_dev       DT device descriptor
 
311
   dt_unit      DT unit list
 
312
   dt_reg       DT register list
 
313
   dt_mod       DT modifier list
 
314
*/
 
315
 
 
316
DIB dt_dib = { IOBA_TC, IOLN_TC, &dt_rd, &dt_wr,
 
317
               1, IVCL (DTA), VEC_DTA, { NULL } };
 
318
 
 
319
UNIT dt_unit[] = {
 
320
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
321
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
322
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
323
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
324
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
325
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
326
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
327
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
328
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
329
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
330
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
331
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
332
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
333
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
334
        { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
 
335
                 UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) },
 
336
        { UDATA (&dt_svcdone, UNIT_DIS, 0) }  };
 
337
 
 
338
#define DT_TIMER        (DT_NUMDR)
 
339
 
 
340
REG dt_reg[] = {
 
341
        { ORDATA (TCST, tcst, 16) },
 
342
        { ORDATA (TCCM, tccm, 16) },
 
343
        { ORDATA (TCWC, tcwc, 16) },
 
344
        { ORDATA (TCBA, tcba, 16) },
 
345
        { ORDATA (TCDT, tcdt, 16) },
 
346
        { FLDATA (INT, IREQ (DTA), INT_V_DTA) },
 
347
        { FLDATA (ERR, tccm, CSR_V_ERR) },
 
348
        { FLDATA (DONE, tccm, CSR_V_DONE) },
 
349
        { FLDATA (IE, tccm, CSR_V_DONE) },
 
350
        { DRDATA (CTIME, dt_ctime, 31), REG_NZ },
 
351
        { DRDATA (LTIME, dt_ltime, 31), REG_NZ },
 
352
        { DRDATA (DCTIME, dt_dctime, 31), REG_NZ },
 
353
        { ORDATA (SUBSTATE, dt_substate, 1) },
 
354
        { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN },
 
355
        { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0,
 
356
                  DT_NUMDR, PV_LEFT | REG_RO) },
 
357
        { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0,
 
358
                  DT_NUMDR, REG_RO) },
 
359
        { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0,
 
360
                  DT_NUMDR, REG_HRO) },
 
361
        { FLDATA (STOP_OFFR, dt_stopoffr, 0) },
 
362
        { ORDATA (DEVADDR, dt_dib.ba, 32), REG_HRO },
 
363
        { ORDATA (DEVVEC, dt_dib.vec, 16), REG_HRO },
 
364
        { NULL }  };
 
365
 
 
366
MTAB dt_mod[] = {
 
367
        { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
 
368
        { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, 
 
369
        { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },
 
370
        { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },
 
371
        { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },
 
372
        { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
 
373
                &set_addr, &show_addr, NULL },
 
374
        { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",
 
375
                &set_vec, &show_vec, NULL },
 
376
        { 0 }  };
 
377
 
 
378
DEBTAB dt_deb[] = {
 
379
        { "MOTION", LOG_MS },
 
380
        { "DATA", LOG_RW },
 
381
        { "BLOCK", LOG_BL },
 
382
        { NULL, 0 }  };
 
383
 
 
384
DEVICE dt_dev = {
 
385
        "TC", dt_unit, dt_reg, dt_mod,
 
386
        DT_NUMDR + 1, 8, 24, 1, 8, 18,
 
387
        NULL, NULL, &dt_reset,
 
388
        &dt_boot, &dt_attach, &dt_detach,
 
389
        &dt_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0,
 
390
        dt_deb, NULL, NULL };
 
391
 
 
392
/* IO dispatch routines, I/O addresses 17777340 - 17777350 */
 
393
 
 
394
t_stat dt_rd (int32 *data, int32 PA, int32 access)
 
395
{
 
396
int32 j, unum, mot, fnc;
 
397
 
 
398
j = (PA >> 1) & 017;                                    /* get reg offset */
 
399
unum = CSR_GETUNIT (tccm);                              /* get drive */
 
400
switch (j) {
 
401
case 000:                                               /* TCST */
 
402
        mot = DTS_GETMOT (dt_unit[unum].STATE);         /* get motion */
 
403
        if (mot >= DTS_ATSF) tcst = tcst | STA_UPS;     /* set/clr speed */
 
404
        else tcst = tcst & ~STA_UPS;
 
405
        *data = tcst;
 
406
        break;
 
407
case 001:                                               /* TCCM */
 
408
        if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR;   /* set/clr error */
 
409
        else tccm = tccm & ~CSR_ERR;
 
410
        *data = tccm;
 
411
        break;
 
412
case 002:                                               /* TCWC */
 
413
        *data = tcwc;
 
414
        break;
 
415
case 003:                                               /* TCBA */
 
416
        *data = tcba;
 
417
        break;
 
418
case 004:                                               /* TCDT */
 
419
        fnc = DTS_GETFNC (dt_unit[unum].STATE);         /* get function */
 
420
        if (fnc == FNC_RALL) {                          /* read all? */
 
421
            DT_CLRDONE;  }                              /* clear done */
 
422
        *data = tcdt;
 
423
        break;  }
 
424
return SCPE_OK;
 
425
}
 
426
 
 
427
t_stat dt_wr (int32 data, int32 PA, int32 access)
 
428
{
 
429
int32 i, j, unum, old_tccm, fnc;
 
430
UNIT *uptr;
 
431
 
 
432
j = (PA >> 1) & 017;                                    /* get reg offset */
 
433
switch (j) {
 
434
case 000:                                               /* TCST */
 
435
        if ((access == WRITEB) && (PA & 1)) break;
 
436
        tcst = (tcst & ~STA_RW) | (data & STA_RW);
 
437
        break;
 
438
case 001:                                               /* TCCM */
 
439
        old_tccm = tccm;                                /* save prior */
 
440
        if (access == WRITEB) data = (PA & 1)?
 
441
            (tccm & 0377) | (data << 8): (tccm & ~0377) | data;
 
442
        if ((data & CSR_IE) == 0) CLR_INT (DTA);
 
443
        else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) ||
 
444
            (data & CSR_DONE)) SET_INT (DTA);
 
445
        tccm = (tccm & ~CSR_RW) | (data & CSR_RW);
 
446
        if ((data & CSR_GO) && (tccm & CSR_DONE)) {     /* new cmd? */
 
447
            tcst = tcst & ~STA_ALLERR;                  /* clear errors */
 
448
            tccm = tccm & ~(CSR_ERR | CSR_DONE);        /* clear done, err */
 
449
            CLR_INT (DTA);                              /* clear int */
 
450
            if ((old_tccm ^ tccm) & CSR_UNIT) dt_deselect (old_tccm);
 
451
            unum = CSR_GETUNIT (tccm);                  /* get drive */
 
452
            fnc = CSR_GETFNC (tccm);                    /* get function */
 
453
            if (fnc == FNC_STOP) {                      /* stop all? */
 
454
                sim_activate (&dt_dev.units[DT_TIMER], dt_ctime);
 
455
                for (i = 0; i < DT_NUMDR; i++)
 
456
                    dt_stopunit (dt_dev.units + i);     /* stop unit */
 
457
                break;  }
 
458
            uptr = dt_dev.units + unum;
 
459
            if (uptr->flags & UNIT_DIS)                 /* disabled? */
 
460
                dt_seterr (uptr, STA_SEL);              /* select err */
 
461
            if ((fnc == FNC_WMRK) ||                    /* write mark? */
 
462
                ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) ||
 
463
                ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)))
 
464
                dt_seterr (uptr, STA_ILO);              /* illegal op */
 
465
            if (!(tccm & CSR_ERR)) dt_newsa (tccm);  }
 
466
        else if ((tccm & CSR_ERR) == 0) {               /* clear err? */
 
467
            tcst = tcst & ~STA_RWERR;
 
468
            if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR;  }
 
469
        break;
 
470
case 002:                                               /* TCWC */
 
471
        tcwc = data;                                    /* word write only! */
 
472
        break;
 
473
case 003:                                               /* TCBA */
 
474
        tcba = data;                                    /* word write only! */
 
475
        break;          
 
476
case 004:                                               /* TCDT */
 
477
        unum = CSR_GETUNIT (tccm);                      /* get drive */
 
478
        fnc = DTS_GETFNC (dt_unit[unum].STATE);         /* get function */
 
479
        if (fnc == FNC_WALL) {                          /* write all? */
 
480
            DT_CLRDONE;  }                              /* clear done */
 
481
        tcdt = data;                                    /* word write only! */
 
482
        break;  }
 
483
return SCPE_OK;
 
484
}
 
485
 
 
486
/* Unit deselect */
 
487
 
 
488
void dt_deselect (int32 oldf)
 
489
{
 
490
int32 old_unit = CSR_GETUNIT (oldf);
 
491
UNIT *uptr = dt_dev.units + old_unit;
 
492
int32 old_mot = DTS_GETMOT (uptr->STATE);
 
493
 
 
494
if (old_mot >= DTS_ATSF)                                /* at speed? */
 
495
        dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR));
 
496
else if (old_mot >= DTS_ACCF)                           /* accelerating? */
 
497
        DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR);
 
498
return;  }
 
499
 
 
500
/* New operation
 
501
 
 
502
   1. If function = stop
 
503
        - if not already stopped or decelerating, schedule deceleration
 
504
        - schedule command completion
 
505
   2. If change in direction,
 
506
        - if not decelerating, schedule deceleration
 
507
        - set accelerating (other dir) as next state
 
508
        - set function as next next state
 
509
   3. If not accelerating or at speed,
 
510
        - schedule acceleration
 
511
        - set function as next state
 
512
   4. If not yet at speed,
 
513
        - set function as next state
 
514
   5. If at speed,
 
515
        - set function as current state, schedule function
 
516
*/
 
517
 
 
518
void dt_newsa (int32 newf)
 
519
{
 
520
int32 new_unit, prev_mot, new_fnc;
 
521
int32 prev_dir, new_dir;
 
522
UNIT *uptr;
 
523
 
 
524
new_unit = CSR_GETUNIT (newf);                          /* new, old units */
 
525
uptr = dt_dev.units + new_unit;
 
526
if ((uptr->flags & UNIT_ATT) == 0) {                    /* new unit attached? */
 
527
        dt_seterr (uptr, STA_SEL);                      /* no, error */
 
528
        return;  }
 
529
prev_mot = DTS_GETMOT (uptr->STATE);                    /* previous motion */
 
530
prev_dir = prev_mot & DTS_DIR;                          /* previous dir */
 
531
new_fnc = CSR_GETFNC (newf);                            /* new function */
 
532
new_dir = (newf & CSR_DIR) != 0;                        /* new di? */
 
533
 
 
534
if (new_fnc == FNC_SSEL) {                              /* stop unit? */
 
535
        sim_activate (&dt_dev.units[DT_TIMER], dt_ctime);       /* sched done */
 
536
        dt_stopunit (uptr);                             /* stop unit */
 
537
        return;  }
 
538
 
 
539
if (prev_mot == DTS_STOP) {                             /* start? */
 
540
        if (dt_setpos (uptr)) return;                   /* update pos */
 
541
        sim_cancel (uptr);                              /* stop current */
 
542
        sim_activate (uptr, dt_dctime - (dt_dctime >> 2));      /* sched accel */
 
543
        DTS_SETSTA (DTS_ACCF | new_dir, 0);             /* state = accel */
 
544
        DTS_SET2ND (DTS_ATSF | new_dir, new_fnc);       /* next = fnc */
 
545
        return;  }
 
546
 
 
547
if (prev_dir ^ new_dir) {                               /* dir chg? */
 
548
        dt_stopunit (uptr);                             /* stop unit */
 
549
        DTS_SET2ND (DTS_ACCF | new_dir, 0);             /* next = accel */
 
550
        DTS_SET3RD (DTS_ATSF | new_dir, new_fnc);       /* next next = fnc */
 
551
        return;  }
 
552
 
 
553
if (prev_mot < DTS_ACCF) {                              /* not accel/at speed? */
 
554
        if (dt_setpos (uptr)) return;                   /* update pos */
 
555
        sim_cancel (uptr);                              /* cancel cur */
 
556
        sim_activate (uptr, dt_dctime - (dt_dctime >> 2));      /* sched accel */
 
557
        DTS_SETSTA (DTS_ACCF | new_dir, 0);             /* state = accel */
 
558
        DTS_SET2ND (DTS_ATSF | new_dir, new_fnc);       /* next = fnc */
 
559
        return;  }
 
560
 
 
561
if (prev_mot < DTS_ATSF) {                              /* not at speed? */
 
562
        DTS_SET2ND (DTS_ATSF | new_dir, new_fnc);       /* next = fnc */
 
563
        return;  }
 
564
 
 
565
dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */
 
566
return; 
 
567
}
 
568
 
 
569
/* Schedule new DECtape function
 
570
 
 
571
   This routine is only called if
 
572
   - the selected unit is attached
 
573
   - the selected unit is at speed (forward or backward)
 
574
 
 
575
   This routine
 
576
   - updates the selected unit's position
 
577
   - updates the selected unit's state
 
578
   - schedules the new operation
 
579
*/
 
580
 
 
581
void dt_newfnc (UNIT *uptr, int32 newsta)
 
582
{
 
583
int32 fnc, dir, blk, unum, relpos, newpos;
 
584
uint32 oldpos;
 
585
 
 
586
oldpos = uptr->pos;                                     /* save old pos */
 
587
if (dt_setpos (uptr)) return;                           /* update pos */
 
588
uptr->STATE = newsta;                                   /* update state */
 
589
fnc = DTS_GETFNC (uptr->STATE);                         /* set variables */
 
590
dir = DTS_GETMOT (uptr->STATE) & DTS_DIR;
 
591
unum = uptr - dt_dev.units;
 
592
if (oldpos == uptr->pos)
 
593
        uptr->pos = uptr->pos + (dir? -1: 1);
 
594
blk = DT_LIN2BL (uptr->pos, uptr);
 
595
 
 
596
if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) {              /* wrong ez? */
 
597
        dt_seterr (uptr, STA_END);                      /* set ez flag, stop */
 
598
        return;  }
 
599
dt_substate = 0;                                        /* substate = normal */
 
600
sim_cancel (uptr);                                      /* cancel cur op */
 
601
switch (fnc) {                                          /* case function */
 
602
case DTS_OFR:                                           /* off reel */
 
603
        if (dir) newpos = -1000;                        /* rev? < start */
 
604
        else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000;       /* fwd? > end */
 
605
        break;
 
606
case FNC_SRCH:                                          /* search */
 
607
        if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)?
 
608
            DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE;
 
609
        else newpos = DT_BLK2LN ((DT_QREZ (uptr)?
 
610
            0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1);
 
611
        if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n",
 
612
            unum, (dir? "backward": "forward"));
 
613
        break;
 
614
case FNC_WRIT:                                          /* write */
 
615
case FNC_READ:                                          /* read */
 
616
        if (DT_QEZ (uptr)) {                            /* in "ok" end zone? */
 
617
            if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE;
 
618
            else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1);
 
619
            break;  }
 
620
        relpos = DT_LIN2OF (uptr->pos, uptr);           /* cur pos in blk */
 
621
        if ((relpos >= DT_HTLIN) &&                     /* in data zone? */
 
622
            (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
 
623
            dt_seterr (uptr, STA_BLKM);
 
624
            return;  }
 
625
        if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))?
 
626
            blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE;
 
627
        else newpos = DT_BLK2LN (((relpos < DT_HTLIN)?
 
628
            blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1);
 
629
        if (DEBUG_PRI (dt_dev, LOG_RW) ||
 
630
           (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))
 
631
            fprintf (sim_deb, ">>DT%d: %s block %d %s\n",
 
632
                unum, ((fnc == FNC_READ)? "read": "write"),
 
633
                blk, (dir? "backward": "forward"));
 
634
        break;
 
635
case FNC_RALL:                                          /* read all */
 
636
case FNC_WALL:                                          /* write all */
 
637
        if (DT_QEZ (uptr)) {                            /* in "ok" end zone? */
 
638
            if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE;
 
639
            else newpos = DT_EZLIN + (DT_WSIZE - 1);  }
 
640
        else {
 
641
            relpos = DT_LIN2OF (uptr->pos, uptr);               /* cur pos in blk */
 
642
            if (dir? (relpos < (DTU_LPERB (uptr) - DT_CSMLN)): /* switch in time? */
 
643
                (relpos >= DT_CSMLN)) {
 
644
                dt_seterr (uptr, STA_BLKM);
 
645
                return;  }
 
646
            if (dir) newpos = DT_BLK2LN (blk + 1, uptr) - DT_CSMLN - DT_WSIZE;
 
647
            else newpos = DT_BLK2LN (blk, uptr) + DT_CSMLN + (DT_WSIZE - 1);  }
 
648
        if (fnc == FNC_WALL) sim_activate               /* write all? */
 
649
            (&dt_dev.units[DT_TIMER], dt_ctime);        /* sched done */
 
650
        if (DEBUG_PRI (dt_dev, LOG_RW) ||
 
651
           (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk)))
 
652
            fprintf (sim_deb, ">>DT%d: read all block %d %s\n",
 
653
                unum, blk, (dir? "backward": "forward"));
 
654
        break;
 
655
default:
 
656
        dt_seterr (uptr, STA_SEL);                      /* bad state */
 
657
        return;  }
 
658
sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
 
659
return;
 
660
}
 
661
 
 
662
/* Update DECtape position
 
663
 
 
664
   DECtape motion is modeled as a constant velocity, with linear
 
665
   acceleration and deceleration.  The motion equations are as follows:
 
666
 
 
667
        t       =       time since operation started
 
668
        tmax    =       time for operation (accel, decel only)
 
669
        v       =       at speed velocity in lines (= 1/dt_ltime)
 
670
 
 
671
   Then:
 
672
        at speed dist = t * v
 
673
        accel dist = (t^2 * v) / (2 * tmax)
 
674
        decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)
 
675
 
 
676
   This routine uses the relative (integer) time, rather than the absolute
 
677
   (floating point) time, to allow save and restore of the start times.
 
678
*/
 
679
 
 
680
t_bool dt_setpos (UNIT *uptr)
 
681
{
 
682
uint32 new_time, ut, ulin, udelt;
 
683
int32 mot = DTS_GETMOT (uptr->STATE);
 
684
int32 unum, delta;
 
685
 
 
686
new_time = sim_grtime ();                               /* current time */
 
687
ut = new_time - uptr->LASTT;                            /* elapsed time */
 
688
if (ut == 0) return FALSE;                              /* no time gone? exit */
 
689
uptr->LASTT = new_time;                                 /* update last time */
 
690
switch (mot & ~DTS_DIR) {                               /* case on motion */
 
691
case DTS_STOP:                                          /* stop */
 
692
        delta = 0;
 
693
        break;
 
694
case DTS_DECF:                                          /* slowing */
 
695
        ulin = ut / (uint32) dt_ltime;
 
696
        udelt = dt_dctime / dt_ltime;
 
697
        delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
 
698
        break;
 
699
case DTS_ACCF:                                          /* accelerating */
 
700
        ulin = ut / (uint32) dt_ltime;
 
701
        udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime;
 
702
        delta = (ulin * ulin) / (2 * udelt);
 
703
        break;
 
704
case DTS_ATSF:                                          /* at speed */
 
705
        delta = ut / (uint32) dt_ltime;
 
706
        break;  }
 
707
if (mot & DTS_DIR) uptr->pos = uptr->pos - delta;       /* update pos */
 
708
else uptr->pos = uptr->pos + delta;
 
709
if (((int32) uptr->pos < 0) ||
 
710
    ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {
 
711
        detach_unit (uptr);                             /* off reel? */
 
712
        uptr->STATE = uptr->pos = 0;
 
713
        unum = uptr - dt_dev.units;
 
714
        if ((unum == CSR_GETUNIT (tccm)) && (CSR_GETFNC (tccm) != FNC_STOP))
 
715
            dt_seterr (uptr, STA_SEL);                  /* error */
 
716
        return TRUE;  }
 
717
return FALSE;
 
718
}
 
719
 
 
720
/* Command timer service after stop - set done */
 
721
 
 
722
t_stat dt_svcdone (UNIT *uptr)
 
723
{
 
724
DT_SETDONE;
 
725
return SCPE_OK;
 
726
}
 
727
 
 
728
/* Unit service
 
729
 
 
730
   Unit must be attached, detach cancels operation
 
731
*/
 
732
 
 
733
t_stat dt_svc (UNIT *uptr)
 
734
{
 
735
int32 mot = DTS_GETMOT (uptr->STATE);
 
736
int32 dir = mot & DTS_DIR;
 
737
int32 fnc = DTS_GETFNC (uptr->STATE);
 
738
int32 *fbuf = uptr->filebuf;
 
739
int32 blk, wrd, relpos, dat;
 
740
uint32 ba, ma, mma;
 
741
 
 
742
/* Motion cases
 
743
 
 
744
   Decelerating - if next state != stopped, must be accel reverse
 
745
   Accelerating - next state must be @speed, schedule function
 
746
   At speed - do functional processing
 
747
*/
 
748
 
 
749
switch (mot) {
 
750
case DTS_DECF: case DTS_DECR:                           /* decelerating */
 
751
        if (dt_setpos (uptr))                           /* upd pos; off reel? */
 
752
            return IORETURN (dt_stopoffr, STOP_DTOFF);
 
753
        uptr->STATE = DTS_NXTSTA (uptr->STATE);         /* advance state */
 
754
        if (uptr->STATE)                                /* not stopped? */
 
755
            sim_activate (uptr, dt_dctime - (dt_dctime >> 2));  /* reversing */
 
756
        return SCPE_OK;
 
757
case DTS_ACCF: case DTS_ACCR:                           /* accelerating */
 
758
        dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE));     /* adv state, sched */
 
759
        return SCPE_OK;
 
760
case DTS_ATSF: case DTS_ATSR:                           /* at speed */
 
761
        break;                                          /* check function */
 
762
default:                                                /* other */
 
763
        dt_seterr (uptr, STA_SEL);                      /* state error */
 
764
        return SCPE_OK;  }
 
765
 
 
766
/* Functional cases
 
767
 
 
768
   Search - transfer block number, schedule next block
 
769
   Off reel - detach unit (it must be deselected)
 
770
*/
 
771
 
 
772
if (dt_setpos (uptr))                                   /* upd pos; off reel? */
 
773
        return IORETURN (dt_stopoffr, STOP_DTOFF);
 
774
if (DT_QEZ (uptr)) {                                    /* in end zone? */
 
775
        dt_seterr (uptr, STA_END);                      /* end zone error */
 
776
        return SCPE_OK;  }
 
777
blk = DT_LIN2BL (uptr->pos, uptr);                      /* get block # */
 
778
 
 
779
switch (fnc) {                                          /* at speed, check fnc */
 
780
case FNC_SRCH:                                          /* search */
 
781
        tcdt = blk;                                     /* set block # */
 
782
        dt_schedez (uptr, dir);                         /* sched end zone */
 
783
        DT_SETDONE;                                     /* set done */
 
784
        break;
 
785
case DTS_OFR:                                           /* off reel */
 
786
        detach_unit (uptr);                             /* must be deselected */
 
787
        uptr->STATE = uptr->pos = 0;                    /* no visible action */
 
788
        break;
 
789
 
 
790
/* Read
 
791
 
 
792
   If wc ovf has not occurred, inc ma, wc and copy word from tape to memory
 
793
   If wc ovf, set flag
 
794
   If not end of block, schedule next word
 
795
   If end of block and not wc ovf, schedule next block
 
796
   If end of block and wc ovf, set done, schedule end zone
 
797
*/
 
798
 
 
799
case FNC_READ:                                          /* read */
 
800
        wrd = DT_LIN2WD (uptr->pos, uptr);              /* get word # */
 
801
        if (!dt_substate) {                             /* !wc ovf? */
 
802
            tcwc = tcwc & DMASK;                        /* incr MA, WC */
 
803
            tcba = tcba & DMASK;
 
804
            ma = (CSR_GETMEX (tccm) << 16) | tcba;      /* form 18b addr */
 
805
            if (!Map_Addr (ma, &mma) ||                 /* map addr */
 
806
                !ADDR_IS_MEM (mma)) {                   /* nx mem? */
 
807
                dt_seterr (uptr, STA_NXM);
 
808
                break;  }
 
809
            ba = (blk * DTU_BSIZE (uptr)) + wrd;        /* buffer ptr */
 
810
            M[mma >> 1] = tcdt = fbuf[ba] & DMASK;      /* read word */
 
811
            tcwc = (tcwc + 1) & DMASK;                  /* incr MA, WC */
 
812
            tcba = (tcba + 2) & DMASK;
 
813
            if (tcba <= 1) tccm = CSR_INCMEX (tccm);
 
814
            if (tcwc == 0) dt_substate = 1;  }
 
815
        if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1))      /* not end blk? */
 
816
            sim_activate (uptr, DT_WSIZE * dt_ltime);
 
817
        else if (dt_substate) {                         /* wc ovf? */
 
818
            dt_schedez (uptr, dir);                     /* sched end zone */
 
819
            DT_SETDONE;  }                              /* set done */
 
820
        else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);
 
821
        break;                  
 
822
 
 
823
/* Write
 
824
 
 
825
   If wc ovf has not occurred, inc ma, wc
 
826
   Copy word from memory (or 0, to fill block) to tape
 
827
   If wc ovf, set flag
 
828
   If not end of block, schedule next word
 
829
   If end of block and not wc ovf, schedule next block
 
830
   If end of block and wc ovf, set done, schedule end zone
 
831
*/
 
832
 
 
833
case FNC_WRIT:                                          /* write */
 
834
        wrd = DT_LIN2WD (uptr->pos, uptr);              /* get word # */
 
835
        if (dt_substate) tcdt = 0;                      /* wc ovf? fill */
 
836
        else {
 
837
            ma = (CSR_GETMEX (tccm) << 16) | tcba;      /* form 18b addr */
 
838
            if (!Map_Addr (ma, &mma) ||                 /* map addr */
 
839
                !ADDR_IS_MEM (mma)) {                   /* nx mem? */
 
840
                dt_seterr (uptr, STA_NXM);
 
841
                break;  }
 
842
            else tcdt = M[mma >> 1];                    /* get word */
 
843
            tcwc = (tcwc + 1) & DMASK;                  /* incr MA, WC */
 
844
            tcba = (tcba + 2) & DMASK;
 
845
            if (tcba <= 1) tccm = CSR_INCMEX (tccm);  }
 
846
        ba = (blk * DTU_BSIZE (uptr)) + wrd;            /* buffer ptr */
 
847
        fbuf[ba] = tcdt;                                /* write word */
 
848
        if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;
 
849
        if (tcwc == 0) dt_substate = 1;
 
850
        if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1))      /* not end blk? */
 
851
            sim_activate (uptr, DT_WSIZE * dt_ltime);
 
852
        else if (dt_substate) {                         /* wc ovf? */
 
853
            dt_schedez (uptr, dir);                     /* sched end zone */
 
854
            DT_SETDONE;  }
 
855
        else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime);
 
856
        break;                  
 
857
 
 
858
/* Read all - read current header or data word */
 
859
 
 
860
case FNC_RALL:
 
861
        if (tccm & CSR_DONE) {                          /* done set? */
 
862
            dt_seterr (uptr, STA_DATM);                 /* data miss */
 
863
            break;  }
 
864
        relpos = DT_LIN2OF (uptr->pos, uptr);           /* cur pos in blk */
 
865
        if ((relpos >= DT_HTLIN) &&                     /* in data zone? */
 
866
            (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
 
867
            wrd = DT_LIN2WD (uptr->pos, uptr);
 
868
            ba = (blk * DTU_BSIZE (uptr)) + wrd;        /* buffer ptr */
 
869
            dat = fbuf[ba];  }                          /* get tape word */
 
870
        else dat = dt_gethdr (uptr, blk, relpos);       /* get hdr */
 
871
        if (dir) dat = dt_comobv (dat);                 /* rev? comp obv */
 
872
        tcdt = dat & DMASK;                             /* low 16b */
 
873
        tcst = (tcst & ~STA_M_XD) | ((dat >> 16) & STA_M_XD);
 
874
        sim_activate (uptr, DT_WSIZE * dt_ltime);
 
875
        DT_SETDONE;                                     /* set done */
 
876
        break;
 
877
 
 
878
/* Write all - write current header or data word */
 
879
 
 
880
case FNC_WALL:
 
881
        if (tccm & CSR_DONE) {                          /* done set? */
 
882
            dt_seterr (uptr, STA_DATM);         /* data miss */
 
883
            break;  }
 
884
        relpos = DT_LIN2OF (uptr->pos, uptr);           /* cur pos in blk */
 
885
        if ((relpos >= DT_HTLIN) &&                     /* in data zone? */
 
886
            (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
 
887
            wrd = DT_LIN2WD (uptr->pos, uptr);
 
888
            dat = (STA_GETXD (tcst) << 16) | tcdt;      /* get data word */
 
889
            if (dir) dat = dt_comobv (dat);             /* rev? comp obv */
 
890
            ba = (blk * DTU_BSIZE (uptr)) + wrd;        /* buffer ptr */
 
891
            fbuf[ba] = dat;                             /* write word */
 
892
            if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;  }
 
893
/*      else                                            /* ignore hdr */ 
 
894
        sim_activate (uptr, DT_WSIZE * dt_ltime);
 
895
        DT_SETDONE;                                     /* set done */
 
896
        break;
 
897
default:
 
898
        dt_seterr (uptr, STA_SEL);                      /* impossible state */
 
899
        break;  }
 
900
return SCPE_OK;
 
901
}
 
902
 
 
903
/* Utility routines */
 
904
 
 
905
/* Set error flag */
 
906
 
 
907
void dt_seterr (UNIT *uptr, int32 e)
 
908
{
 
909
int32 mot = DTS_GETMOT (uptr->STATE);
 
910
 
 
911
tcst = tcst | e;                                        /* set error flag */
 
912
tccm = tccm | CSR_ERR;
 
913
if (!(tccm & CSR_DONE)) {                               /* not done? */
 
914
        DT_SETDONE;  }
 
915
if (mot >= DTS_ACCF) {                                  /* ~stopped or stopping? */
 
916
        sim_cancel (uptr);                              /* cancel activity */
 
917
        if (dt_setpos (uptr)) return;                   /* update position */
 
918
        sim_activate (uptr, dt_dctime);                 /* sched decel */
 
919
        DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0);  }  /* state = decel */
 
920
return;
 
921
}
 
922
 
 
923
/* Stop unit */
 
924
 
 
925
void dt_stopunit (UNIT *uptr)
 
926
{
 
927
int32 mot = DTS_GETMOT (uptr->STATE);
 
928
int32 dir = mot & DTS_DIR;
 
929
 
 
930
if (mot == DTS_STOP) return;                            /* already stopped? */
 
931
if ((mot & ~DTS_DIR) != DTS_DECF) {                     /* !already stopping? */
 
932
        if (dt_setpos (uptr)) return;                   /* update pos */
 
933
        sim_cancel (uptr);                              /* stop current */
 
934
        sim_activate (uptr, dt_dctime);  }              /* schedule decel */
 
935
DTS_SETSTA (DTS_DECF | dir, 0);                         /* state = decel */
 
936
return;
 
937
}
 
938
 
 
939
/* Schedule end zone */
 
940
 
 
941
void dt_schedez (UNIT *uptr, int32 dir)
 
942
{
 
943
int32 newpos;
 
944
 
 
945
if (dir) newpos = DT_EZLIN - DT_WSIZE;                  /* rev? rev ez */
 
946
else newpos = DTU_FWDEZ (uptr) + DT_WSIZE;              /* fwd? fwd ez */
 
947
sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime);
 
948
return;
 
949
}
 
950
 
 
951
/* Complement obverse routine (18b) */
 
952
 
 
953
int32 dt_comobv (int32 dat)
 
954
{
 
955
dat = dat ^ 0777777;                                    /* compl obverse */
 
956
dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) |
 
957
        ((dat >> 3) & 0700) | ((dat & 0700) << 3) |
 
958
        ((dat & 070) << 9) | ((dat & 07) << 15);
 
959
return dat;
 
960
}
 
961
 
 
962
/* Checksum routine */
 
963
 
 
964
int32 dt_csum (UNIT *uptr, int32 blk)
 
965
{
 
966
int32 *fbuf = uptr->filebuf;
 
967
int32 ba = blk * DTU_BSIZE (uptr);
 
968
int32 i, csum, wrd;
 
969
 
 
970
csum = 077;                                             /* init csum */
 
971
for (i = 0; i < DTU_BSIZE (uptr); i++) {                /* loop thru buf */
 
972
        wrd = fbuf[ba + i] ^ 0777777;                   /* get ~word */
 
973
        csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd;  }
 
974
return (csum & 077);
 
975
}
 
976
 
 
977
/* Get header word (18b) */
 
978
 
 
979
int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos)
 
980
{
 
981
int32 wrd = relpos / DT_WSIZE;
 
982
 
 
983
if (wrd == DT_BLKWD) return blk;                        /* fwd blknum */
 
984
if (wrd == DT_CSMWD) return 077;                        /* rev csum */
 
985
if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1))    /* fwd csum */
 
986
        return (dt_csum (uptr, blk) << 12);
 
987
if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1))    /* rev blkno */
 
988
        return dt_comobv (blk);
 
989
return 0;                                               /* all others */
 
990
}
 
991
 
 
992
/* Reset routine */
 
993
 
 
994
t_stat dt_reset (DEVICE *dptr)
 
995
{
 
996
int32 i, prev_mot;
 
997
UNIT *uptr;
 
998
 
 
999
for (i = 0; i < DT_NUMDR; i++) {                        /* stop all activity */
 
1000
        uptr = dt_dev.units + i;
 
1001
        if (sim_is_running) {                           /* RESET? */
 
1002
            prev_mot = DTS_GETMOT (uptr->STATE);        /* get motion */
 
1003
            if ((prev_mot & ~DTS_DIR) > DTS_DECF) {     /* accel or spd? */
 
1004
                if (dt_setpos (uptr)) continue;         /* update pos */
 
1005
                sim_cancel (uptr);
 
1006
                sim_activate (uptr, dt_dctime);         /* sched decel */
 
1007
                DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0);
 
1008
                }  }
 
1009
        else {
 
1010
            sim_cancel (uptr);                          /* sim reset */
 
1011
            uptr->STATE = 0;  
 
1012
            uptr->LASTT = sim_grtime ();  }  }
 
1013
tcst =  tcwc = tcba = tcdt = 0;                         /* clear reg */
 
1014
tccm = CSR_DONE;
 
1015
CLR_INT (DTA);                                          /* clear int req */
 
1016
return SCPE_OK;
 
1017
}
 
1018
 
 
1019
/* Device bootstrap */
 
1020
 
 
1021
#define BOOT_START      02000                           /* start */
 
1022
#define BOOT_ENTRY      (BOOT_START + 002)              /* entry */
 
1023
#define BOOT_UNIT       (BOOT_START + 010)              /* unit number */
 
1024
#define BOOT_CSR        (BOOT_START + 020)              /* CSR */
 
1025
#define BOOT_LEN        (sizeof (boot_rom) / sizeof (int16))
 
1026
 
 
1027
static const uint16 boot_rom[] = {
 
1028
        0042124,                        /* "TD" */
 
1029
        0012706, BOOT_START,            /* MOV #boot_start, SP */
 
1030
        0012700, 0000000,               /* MOV #unit, R0        ; unit number */
 
1031
        0010003,                        /* MOV R0, R3 */
 
1032
        0000303,                        /* SWAB R3 */
 
1033
        0012701, 0177342,               /* MOV #TCCM, R1        ; csr */
 
1034
        0012702, 0004003,               /* RW: MOV #4003, R2    ; rev+rnum+go */
 
1035
        0050302,                        /* BIS R3, R2 */
 
1036
        0010211,                        /* MOV R2, (R1)         ; load csr */
 
1037
        0032711, 0100200,               /* BIT #100200, (R1)    ; wait */
 
1038
        0001775,                        /* BEQ .-4 */
 
1039
        0100370,                        /* BPL RW               ; no err, cont */
 
1040
        0005761, 0177776,               /* TST -2(R1)           ; end zone? */
 
1041
        0100036,                        /* BPL ER               ; no, err */
 
1042
        0012702, 0000003,               /* MOV #3, R2           ; rnum+go */
 
1043
        0050302,                        /* BIS R3, R2 */
 
1044
        0010211,                        /* MOV R2, (R1)         ; load csr */
 
1045
        0032711, 0100200,               /* BIT #100200, (R1)    ; wait */
 
1046
        0001775,                        /* BEQ .-4 */
 
1047
        0100426,                        /* BMI ER               ; err, die */
 
1048
        0005761, 0000006,               /* TST 6(R1)            ; blk 0? */
 
1049
        0001023,                        /* BNE ER               ; no, die */
 
1050
        0012761, 0177000, 0000002,      /* MOV #-256.*2, 2(R1)  ; load wc */
 
1051
        0005061, 0000004,               /* CLR 4(R1)            ; clear ba */
 
1052
        0012702, 0000005,               /* MOV #READ+GO, R2     ; read & go */
 
1053
        0050302,                        /* BIS R3, R2 */
 
1054
        0010211,                        /* MOV R2, (R1)         ; load csr */
 
1055
        0005002,                        /* CLR R2 */
 
1056
        0005003,                        /* CLR R3 */
 
1057
        0012704, BOOT_START+020,        /* MOV #START+20, R4 */
 
1058
        0005005,                        /* CLR R5 */
 
1059
        0032711, 0100200,               /* BIT #100200, (R1)    ; wait */
 
1060
        0001775,                        /* BEQ .-4 */
 
1061
        0100401,                        /* BMI ER               ; err, die */
 
1062
        0005007,                        /* CLR PC */
 
1063
        0012711, 0000001,               /* ER: MOV #1, (R1)     ; stop all */
 
1064
        0000000                         /* HALT */
 
1065
};
 
1066
 
 
1067
t_stat dt_boot (int32 unitno, DEVICE *dptr)
 
1068
{
 
1069
int32 i;
 
1070
extern int32 saved_PC;
 
1071
 
 
1072
dt_unit[unitno].pos = DT_EZLIN;
 
1073
for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
 
1074
M[BOOT_UNIT >> 1] = unitno & DT_M_NUMDR;
 
1075
M[BOOT_CSR >> 1] = (dt_dib.ba & DMASK) + 02;
 
1076
saved_PC = BOOT_ENTRY;
 
1077
return SCPE_OK;
 
1078
}
 
1079
 
 
1080
/* Attach routine
 
1081
 
 
1082
   Determine 12b, 16b, or 18b/36b format
 
1083
   Allocate buffer
 
1084
   If 12b, read 12b format and convert to 18b in buffer
 
1085
   If 16b, read 16b format and convert to 18b in buffer
 
1086
   If 18b/36b, read data into buffer
 
1087
*/
 
1088
 
 
1089
t_stat dt_attach (UNIT *uptr, char *cptr)
 
1090
{
 
1091
uint16 pdp8b[D8_NBSIZE];
 
1092
uint16 pdp11b[D18_BSIZE];
 
1093
uint32 ba, sz, k, *fbuf;
 
1094
int32 u = uptr - dt_dev.units;
 
1095
t_stat r;
 
1096
 
 
1097
r = attach_unit (uptr, cptr);                           /* attach */
 
1098
if (r != SCPE_OK) return r;                             /* fail? */
 
1099
if ((sim_switches & SIM_SW_REST) == 0) {                /* not from rest? */
 
1100
        uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;  /* default 16b */
 
1101
        if (sim_switches & SWMASK ('R'))                /* att 12b? */
 
1102
            uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
 
1103
        else if (sim_switches & SWMASK ('T'))           /* att 18b? */
 
1104
            uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT);
 
1105
        else if (!(sim_switches & SWMASK ('S')) &&      /* autosize? */
 
1106
            ((sz = sim_fsize (uptr->fileref)) > D16_FILSIZ)) {
 
1107
            if (sz <= D8_FILSIZ)
 
1108
            uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
 
1109
            else uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT);  }  }
 
1110
uptr->capac = DTU_CAPAC (uptr);                         /* set capacity */
 
1111
uptr->filebuf = calloc (uptr->capac, sizeof (int32));
 
1112
if (uptr->filebuf == NULL) {                            /* can't alloc? */
 
1113
        detach_unit (uptr);
 
1114
        return SCPE_MEM;  }
 
1115
fbuf = uptr->filebuf;                                   /* file buffer */
 
1116
printf ("%s%d: ", sim_dname (&dt_dev), u);
 
1117
if (uptr->flags & UNIT_8FMT) printf ("12b format");
 
1118
else if (uptr->flags & UNIT_11FMT) printf ("16b format");
 
1119
else printf ("18b/36b format");
 
1120
printf (", buffering file in memory\n");
 
1121
if (uptr->flags & UNIT_8FMT) {                          /* 12b? */
 
1122
        for (ba = 0; ba < uptr->capac; ) {              /* loop thru file */
 
1123
            k = fxread (pdp8b, sizeof (int16), D8_NBSIZE, uptr->fileref);
 
1124
            if (k == 0) break;
 
1125
            for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0;
 
1126
            for (k = 0; k < D8_NBSIZE; k = k + 3) {     /* loop thru blk */
 
1127
                fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) |
 
1128
                        ((uint32) (pdp8b[k + 1] >> 6) & 077);
 
1129
                fbuf[ba + 1] = ((pdp8b[k + 1] & 077) << 12) |
 
1130
                        ((uint32) (pdp8b[k + 2] & 07777));
 
1131
                ba = ba + 2;  }                         /* end blk loop */
 
1132
            }                                           /* end file loop */
 
1133
        uptr->hwmark = ba;  }                           /* end if */
 
1134
else if (uptr->flags & UNIT_11FMT) {                    /* 16b? */
 
1135
        for (ba = 0; ba < uptr->capac; ) {              /* loop thru file */
 
1136
            k = fxread (pdp11b, sizeof (int16), D18_BSIZE, uptr->fileref);
 
1137
            if (k == 0) break;
 
1138
            for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0;
 
1139
            for (k = 0; k < D18_BSIZE; k++)
 
1140
                fbuf[ba++] = pdp11b[k];  }
 
1141
        uptr->hwmark = ba;  }                           /* end elif */
 
1142
else uptr->hwmark = fxread (uptr->filebuf, sizeof (int32),
 
1143
        uptr->capac, uptr->fileref);
 
1144
uptr->flags = uptr->flags | UNIT_BUF;                   /* set buf flag */
 
1145
uptr->pos = DT_EZLIN;                                   /* beyond leader */
 
1146
uptr->LASTT = sim_grtime ();                            /* last pos update */
 
1147
return SCPE_OK;
 
1148
}
 
1149
 
 
1150
/* Detach routine
 
1151
 
 
1152
   Cancel in progress operation
 
1153
   If 12b, convert 18b buffer to 12b and write to file
 
1154
   If 16b, convert 18b buffer to 16b and write to file
 
1155
   If 18b/36b, write buffer to file
 
1156
   Deallocate buffer
 
1157
*/
 
1158
 
 
1159
t_stat dt_detach (UNIT* uptr)
 
1160
{
 
1161
uint16 pdp8b[D8_NBSIZE];
 
1162
uint16 pdp11b[D18_BSIZE];
 
1163
uint32 ba, k, *fbuf;
 
1164
int32 u = uptr - dt_dev.units;
 
1165
 
 
1166
if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;
 
1167
if (sim_is_active (uptr)) {                             /* active? cancel op */
 
1168
        sim_cancel (uptr);
 
1169
        if ((u == CSR_GETUNIT (tccm)) && ((tccm & CSR_DONE) == 0)) {
 
1170
            tcst = tcst | STA_SEL;
 
1171
            tccm = tccm | CSR_ERR | CSR_DONE;
 
1172
            if (tccm & CSR_IE) SET_INT (DTA);  }
 
1173
        uptr->STATE = uptr->pos = 0;  }
 
1174
fbuf = uptr->filebuf;                                   /* file buffer */
 
1175
if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {   /* any data? */
 
1176
        printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u);
 
1177
        rewind (uptr->fileref);                         /* start of file */
 
1178
        if (uptr->flags & UNIT_8FMT) {                  /* 12b? */
 
1179
            for (ba = 0; ba < uptr->hwmark; ) {         /* loop thru file */
 
1180
                for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */
 
1181
                    pdp8b[k] = (fbuf[ba] >> 6) & 07777;
 
1182
                    pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) |
 
1183
                        ((fbuf[ba + 1] >> 12) & 077);
 
1184
                    pdp8b[k + 2] = fbuf[ba + 1] & 07777;
 
1185
                    ba = ba + 2;  }                     /* end loop blk */
 
1186
                fxwrite (pdp8b, sizeof (int16), D8_NBSIZE, uptr->fileref);
 
1187
                if (ferror (uptr->fileref)) break;  }   /* end loop file */
 
1188
            }                                           /* end if 12b */
 
1189
        else if (uptr->flags & UNIT_11FMT) {            /* 16b? */
 
1190
            for (ba = 0; ba < uptr->hwmark; ) {         /* loop thru file */
 
1191
                for (k = 0; k < D18_BSIZE; k++)         /* loop blk */
 
1192
                    pdp11b[k] = fbuf[ba++] & DMASK;
 
1193
                fxwrite (pdp11b, sizeof (int16), D18_BSIZE, uptr->fileref);
 
1194
                if (ferror (uptr->fileref)) break;  }   /* end loop file */
 
1195
            }                                           /* end if 16b */
 
1196
        else fxwrite (uptr->filebuf, sizeof (int32),    /* write file */
 
1197
                uptr->hwmark, uptr->fileref);
 
1198
        if (ferror (uptr->fileref)) perror ("I/O error");  }    /* end if hwmark */
 
1199
free (uptr->filebuf);                                   /* release buf */
 
1200
uptr->flags = uptr->flags & ~UNIT_BUF;                  /* clear buf flag */
 
1201
uptr->filebuf = NULL;                                   /* clear buf ptr */
 
1202
uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;  /* default fmt */
 
1203
uptr->capac = DT_CAPAC;                                 /* default size */
 
1204
return detach_unit (uptr);
 
1205
}