First Commit of my working state
[simh.git] / PDP11 / pdp11_tm.c
CommitLineData
196ba1fc
PH
1/* pdp11_tm.c: PDP-11 magnetic tape simulator\r
2\r
3 Copyright (c) 1993-2006, Robert M Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25\r
26 tm TM11/TU10 magtape\r
27\r
28 16-Feb-06 RMS Added tape capacity checking\r
29 31-Oct-05 RMS Fixed address width for large files\r
30 16-Aug-05 RMS Fixed C++ declaration and cast problems\r
31 07-Jul-05 RMS Removed extraneous externs\r
32 18-Mar-05 RMS Added attached test to detach routine\r
33 07-Dec-04 RMS Added read-only file support\r
34 30-Sep-04 RMS Revised Unibus interface\r
35 25-Jan-04 RMS Revised for device debug support\r
36 29-Dec-03 RMS Added 18b Qbus support\r
37 25-Apr-03 RMS Revised for extended file support\r
38 28-Mar-03 RMS Added multiformat support\r
39 28-Feb-03 RMS Revised for magtape library, added logging\r
40 30-Oct-02 RMS Revised BOT handling, added error record handling\r
41 30-Sep-02 RMS Added variable address support to bootstrap\r
42 Added vector change/display support\r
43 Changed mapping mnemonics\r
44 New data structures\r
45 Updated error handling\r
46 28-Aug-02 RMS Added end of medium support\r
47 30-May-02 RMS Widened POS to 32b\r
48 22-Apr-02 RMS Fixed max record length, first block bootstrap\r
49 (found by Jonathan Engdahl)\r
50 26-Jan-02 RMS Revised bootstrap to conform to M9312\r
51 06-Jan-02 RMS Revised enable/disable support\r
52 30-Nov-01 RMS Added read only unit, extended SET/SHOW support\r
53 24-Nov-01 RMS Converted UST, POS, FLG to arrays\r
54 09-Nov-01 RMS Added bus map support\r
55 18-Oct-01 RMS Added stub diagnostic register (found by Thord Nilson)\r
56 07-Sep-01 RMS Revised device disable and interrupt mechanisms\r
57 26-Apr-01 RMS Added device enable/disable support\r
58 18-Apr-01 RMS Changed to rewind tape before boot\r
59 14-Apr-99 RMS Changed t_addr to unsigned\r
60 04-Oct-98 RMS V2.4 magtape format\r
61 10-May-98 RMS Fixed bug with non-zero unit operation (from Steven Schultz)\r
62 09-May-98 RMS Fixed problems in bootstrap (from Steven Schultz)\r
63 10-Apr-98 RMS Added 2nd block bootstrap (from John Holden,\r
64 University of Sydney)\r
65 31-Jul-97 RMS Added bootstrap (from Ethan Dicks, Ohio State)\r
66 22-Jan-97 RMS V2.3 magtape format\r
67 18-Jan-97 RMS Fixed double interrupt, error flag bugs\r
68 29-Jun-96 RMS Added unit disable support\r
69\r
70 Magnetic tapes are represented as a series of variable 8b records\r
71 of the form:\r
72\r
73 32b record length in bytes - exact number\r
74 byte 0\r
75 byte 1\r
76 :\r
77 byte n-2\r
78 byte n-1\r
79 32b record length in bytes - exact number\r
80\r
81 If the byte count is odd, the record is padded with an extra byte\r
82 of junk. File marks are represented by a single record length of 0.\r
83 End of tape is two consecutive end of file marks.\r
84*/\r
85\r
86#include "pdp11_defs.h"\r
87#include "sim_tape.h"\r
88\r
89#define TM_NUMDR 8 /* #drives */\r
90#define USTAT u3 /* unit status */\r
91\r
92/* Command - tm_cmd */\r
93\r
94#define MTC_ERR (1 << CSR_V_ERR) /* error */\r
95#define MTC_V_DEN 13 /* density */\r
96#define MTC_M_DEN 03\r
97#define MTC_DEN (MTC_M_DEN << MTC_V_DEN)\r
98#define MTC_INIT 0010000 /* init */\r
99#define MTC_LPAR 0004000 /* parity select */\r
100#define MTC_V_UNIT 8 /* unit */\r
101#define MTC_M_UNIT 07\r
102#define MTC_UNIT (MTC_M_UNIT << MTC_V_UNIT)\r
103#define MTC_DONE (1 << CSR_V_DONE) /* done */\r
104#define MTC_IE (1 << CSR_V_IE) /* interrupt enable */\r
105#define MTC_V_EMA 4 /* ext mem address */\r
106#define MTC_M_EMA 03\r
107#define MTC_EMA (MTC_M_EMA << MTC_V_EMA)\r
108#define MTC_V_FNC 1 /* function */\r
109#define MTC_M_FNC 07\r
110#define MTC_UNLOAD 00\r
111#define MTC_READ 01\r
112#define MTC_WRITE 02\r
113#define MTC_WREOF 03\r
114#define MTC_SPACEF 04\r
115#define MTC_SPACER 05\r
116#define MTC_WREXT 06\r
117#define MTC_REWIND 07\r
118#define MTC_FNC (MTC_M_FNC << MTC_V_FNC)\r
119#define MTC_GO (1 << CSR_V_GO) /* go */\r
120#define MTC_RW (MTC_DEN | MTC_LPAR | MTC_UNIT | MTC_IE | \\r
121 MTC_EMA | MTC_FNC)\r
122#define GET_EMA(x) (((x) & MTC_EMA) << (16 - MTC_V_EMA))\r
123#define GET_UNIT(x) (((x) >> MTC_V_UNIT) & MTC_M_UNIT)\r
124#define GET_FNC(x) (((x) >> MTC_V_FNC) & MTC_M_FNC)\r
125\r
126/* Status - stored in tm_sta or (*) uptr->USTAT or (+) calculated */\r
127\r
128#define STA_ILL 0100000 /* illegal */\r
129#define STA_EOF 0040000 /* *end of file */\r
130#define STA_CRC 0020000 /* CRC error */\r
131#define STA_PAR 0010000 /* parity error */\r
132#define STA_DLT 0004000 /* data late */\r
133#define STA_EOT 0002000 /* +end of tape */\r
134#define STA_RLE 0001000 /* rec lnt error */\r
135#define STA_BAD 0000400 /* bad tape error */\r
136#define STA_NXM 0000200 /* non-existent mem */\r
137#define STA_ONL 0000100 /* *online */\r
138#define STA_BOT 0000040 /* *start of tape */\r
139#define STA_7TK 0000020 /* 7 track */\r
140#define STA_SDN 0000010 /* settle down */\r
141#define STA_WLK 0000004 /* *write locked */\r
142#define STA_REW 0000002 /* *rewinding */\r
143#define STA_TUR 0000001 /* +unit ready */\r
144\r
145#define STA_CLR (STA_7TK | STA_SDN) /* always clear */\r
146#define STA_DYN (STA_EOF | STA_EOT | STA_ONL | STA_BOT | \\r
147 STA_WLK | STA_REW | STA_TUR) /* dynamic */\r
148#define STA_EFLGS (STA_ILL | STA_EOF | STA_CRC | STA_PAR | \\r
149 STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM)\r
150 /* set error */\r
151\r
152/* Read lines - tm_rdl */\r
153\r
154#define RDL_CLK 0100000 /* 10 Khz clock */\r
155\r
156extern uint16 *M; /* memory */\r
157extern int32 int_req[IPL_HLVL];\r
158extern FILE *sim_deb;\r
159\r
160uint8 *tmxb = NULL; /* xfer buffer */\r
161int32 tm_sta = 0; /* status register */\r
162int32 tm_cmd = 0; /* command register */\r
163int32 tm_ca = 0; /* current address */\r
164int32 tm_bc = 0; /* byte count */\r
165int32 tm_db = 0; /* data buffer */\r
166int32 tm_rdl = 0; /* read lines */\r
167int32 tm_time = 10; /* record latency */\r
168int32 tm_stopioe = 1; /* stop on error */\r
169\r
170DEVICE tm_dev;\r
171t_stat tm_rd (int32 *data, int32 PA, int32 access);\r
172t_stat tm_wr (int32 data, int32 PA, int32 access);\r
173t_stat tm_svc (UNIT *uptr);\r
174t_stat tm_reset (DEVICE *dptr);\r
175t_stat tm_attach (UNIT *uptr, char *cptr);\r
176t_stat tm_detach (UNIT *uptr);\r
177t_stat tm_boot (int32 unitno, DEVICE *dptr);\r
178void tm_go (UNIT *uptr);\r
179int32 tm_updcsta (UNIT *uptr);\r
180void tm_set_done (void);\r
181t_stat tm_map_err (UNIT *uptr, t_stat st);\r
182t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc);\r
183\r
184/* MT data structures\r
185\r
186 tm_dev MT device descriptor\r
187 tm_unit MT unit list\r
188 tm_reg MT register list\r
189 tm_mod MT modifier list\r
190*/\r
191\r
192DIB tm_dib = {\r
193 IOBA_TM, IOLN_TM, &tm_rd, &tm_wr,\r
194 1, IVCL (TM), VEC_TM, { NULL }\r
195 };\r
196\r
197UNIT tm_unit[] = {\r
198 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
199 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
200 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
201 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
202 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
203 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
204 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) },\r
205 { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }\r
206 };\r
207\r
208REG tm_reg[] = {\r
209 { ORDATA (MTS, tm_sta, 16) },\r
210 { ORDATA (MTC, tm_cmd, 16) },\r
211 { ORDATA (MTBRC, tm_bc, 16) },\r
212 { ORDATA (MTCMA, tm_ca, 16) },\r
213 { ORDATA (MTD, tm_db, 8) },\r
214 { ORDATA (MTRD, tm_rdl, 16) },\r
215 { FLDATA (INT, IREQ (TM), INT_V_TM) },\r
216 { FLDATA (ERR, tm_cmd, CSR_V_ERR) },\r
217 { FLDATA (DONE, tm_cmd, CSR_V_DONE) },\r
218 { FLDATA (IE, tm_cmd, CSR_V_IE) },\r
219 { FLDATA (STOP_IOE, tm_stopioe, 0) },\r
220 { DRDATA (TIME, tm_time, 24), PV_LEFT },\r
221 { URDATA (UST, tm_unit[0].USTAT, 8, 16, 0, TM_NUMDR, 0) },\r
222 { URDATA (POS, tm_unit[0].pos, 10, T_ADDR_W, 0,\r
223 TM_NUMDR, PV_LEFT | REG_RO) },\r
224 { ORDATA (DEVADDR, tm_dib.ba, 32), REG_HRO },\r
225 { ORDATA (DEVVEC, tm_dib.vec, 16), REG_HRO },\r
226 { NULL }\r
227 };\r
228\r
229MTAB tm_mod[] = {\r
230 { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &tm_vlock },\r
231 { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &tm_vlock }, \r
232 { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",\r
233 &sim_tape_set_fmt, &sim_tape_show_fmt, NULL },\r
234 { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY",\r
235 &sim_tape_set_capac, &sim_tape_show_capac, NULL },\r
236 { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS",\r
237 &set_addr, &show_addr, NULL },\r
238 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",\r
239 &set_vec, &show_vec, NULL },\r
240 { 0 }\r
241 };\r
242\r
243DEVICE tm_dev = {\r
244 "TM", tm_unit, tm_reg, tm_mod,\r
245 TM_NUMDR, 10, T_ADDR_W, 1, 8, 8,\r
246 NULL, NULL, &tm_reset,\r
247 &tm_boot, &tm_attach, &tm_detach,\r
248 &tm_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG\r
249 };\r
250\r
251/* I/O dispatch routines, I/O addresses 17772520 - 17772532\r
252\r
253 17772520 MTS read only, constructed from tm_sta\r
254 plus current drive status flags\r
255 17772522 MTC read/write\r
256 17772524 MTBRC read/write\r
257 17772526 MTCMA read/write\r
258 17772530 MTD read/write\r
259 17772532 MTRD read only\r
260*/\r
261\r
262t_stat tm_rd (int32 *data, int32 PA, int32 access)\r
263{\r
264UNIT *uptr;\r
265\r
266uptr = tm_dev.units + GET_UNIT (tm_cmd); /* get unit */\r
267switch ((PA >> 1) & 07) { /* decode PA<3:1> */\r
268\r
269 case 0: /* MTS */\r
270 *data = tm_updcsta (uptr); /* update status */\r
271 break;\r
272\r
273 case 1: /* MTC */\r
274 tm_updcsta (uptr); /* update status */\r
275 *data = tm_cmd; /* return command */\r
276 break;\r
277\r
278 case 2: /* MTBRC */\r
279 *data = tm_bc; /* return byte count */\r
280 break;\r
281\r
282 case 3: /* MTCMA */\r
283 *data = tm_ca; /* return mem addr */\r
284 break;\r
285\r
286 case 4: /* MTD */\r
287 *data = tm_db; /* return data buffer */\r
288 break;\r
289\r
290 case 5: /* MTRD */\r
291 tm_rdl = tm_rdl ^ RDL_CLK; /* "clock" ticks */\r
292 *data = tm_rdl;\r
293 break;\r
294\r
295 default: /* unimplemented */\r
296 *data = 0;\r
297 break;\r
298 }\r
299\r
300return SCPE_OK;\r
301}\r
302\r
303t_stat tm_wr (int32 data, int32 PA, int32 access)\r
304{\r
305UNIT *uptr;\r
306\r
307switch ((PA >> 1) & 07) { /* decode PA<3:1> */\r
308\r
309 case 0: /* MTS: read only */\r
310 break;\r
311\r
312 case 1: /* MTC */\r
313 uptr = tm_dev.units + GET_UNIT (tm_cmd); /* select unit */\r
314 if ((tm_cmd & MTC_DONE) == 0) tm_sta = tm_sta | STA_ILL;\r
315 else {\r
316 if (access == WRITEB) data = (PA & 1)?\r
317 (tm_cmd & 0377) | (data << 8):\r
318 (tm_cmd & ~0377) | data;\r
319 if (data & MTC_INIT) { /* init? */\r
320 tm_reset (&tm_dev); /* reset device */\r
321 return SCPE_OK;\r
322 }\r
323 if ((data & MTC_IE) == 0) /* int disable? */\r
324 CLR_INT (TM); /* clr int request */\r
325 else if ((tm_cmd & (MTC_ERR + MTC_DONE)) && !(tm_cmd & MTC_IE))\r
326 SET_INT (TM); /* set int request */\r
327 tm_cmd = (tm_cmd & ~MTC_RW) | (data & MTC_RW);\r
328 uptr = tm_dev.units + GET_UNIT (tm_cmd); /* new unit */\r
329 if (data & MTC_GO) tm_go (uptr); /* new function? */\r
330 }\r
331 tm_updcsta (uptr); /* update status */\r
332 break;\r
333\r
334 case 2: /* MTBRC */\r
335 if (access == WRITEB) data = (PA & 1)?\r
336 (tm_bc & 0377) | (data << 8): (tm_bc & ~0377) | data;\r
337 tm_bc = data;\r
338 break;\r
339\r
340 case 3: /* MTCMA */\r
341 if (access == WRITEB) data = (PA & 1)?\r
342 (tm_ca & 0377) | (data << 8): (tm_ca & ~0377) | data;\r
343 tm_ca = data;\r
344 break;\r
345\r
346 case 4: /* MTD */\r
347 if ((access == WRITEB) && (PA & 1)) return SCPE_OK;\r
348 tm_db = data & 0377;\r
349 break;\r
350 } /* end switch */\r
351\r
352return SCPE_OK;\r
353}\r
354\r
355/* New magtape command */\r
356\r
357void tm_go (UNIT *uptr)\r
358{\r
359int32 f;\r
360\r
361f = GET_FNC (tm_cmd); /* get function */\r
362if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */\r
363 sim_is_active (uptr) || /* busy? */\r
364 (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) &&\r
365 sim_tape_wrp (uptr))) { /* write locked? */\r
366 tm_sta = tm_sta | STA_ILL; /* illegal */\r
367 tm_set_done (); /* set done */\r
368 return;\r
369 }\r
370uptr->USTAT = uptr->USTAT & (STA_WLK | STA_ONL); /* clear status */\r
371tm_sta = 0; /* clear errors */\r
372if (f == MTC_UNLOAD) { /* unload? */\r
373 uptr->USTAT = (uptr->USTAT | STA_REW) & ~STA_ONL;\r
374 detach_unit (uptr); /* set offline */\r
375 }\r
376else if (f == MTC_REWIND) /* rewind */\r
377 uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */\r
378/* else /* uncomment this else if rewind/unload don't set done */\r
379tm_cmd = tm_cmd & ~MTC_DONE; /* clear done */\r
380CLR_INT (TM); /* clear int */\r
381sim_activate (uptr, tm_time); /* start io */\r
382return;\r
383}\r
384\r
385/* Unit service\r
386\r
387 If rewind done, reposition to start of tape, set status\r
388 else, do operation, set done, interrupt\r
389*/\r
390\r
391t_stat tm_svc (UNIT *uptr)\r
392{\r
393int32 f, t, u;\r
394uint32 xma;\r
395t_mtrlnt tbc, cbc;\r
396t_stat st, r = SCPE_OK;\r
397\r
398u = (int32) (uptr - tm_dev.units); /* get unit number */\r
399f = GET_FNC (tm_cmd); /* get command */\r
400xma = GET_EMA (tm_cmd) | tm_ca; /* get mem addr */\r
401cbc = 0200000 - tm_bc; /* get bc */\r
402\r
403if (uptr->USTAT & STA_REW) { /* rewind? */\r
404 sim_tape_rewind (uptr); /* update position */\r
405 if (uptr->flags & UNIT_ATT) /* still on line? */\r
406 uptr->USTAT = STA_ONL | STA_BOT |\r
407 (sim_tape_wrp (uptr)? STA_WLK: 0);\r
408 else uptr->USTAT = 0;\r
409 if (u == GET_UNIT (tm_cmd)) { /* selected? */\r
410 tm_set_done (); /* set done */\r
411 tm_updcsta (uptr); /* update status */\r
412 }\r
413 return SCPE_OK;\r
414 }\r
415\r
416if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */\r
417 uptr->USTAT = 0; /* unit off line */\r
418 tm_sta = tm_sta | STA_ILL; /* illegal operation */\r
419 tm_set_done (); /* set done */\r
420 tm_updcsta (uptr); /* update status */\r
421 return IORETURN (tm_stopioe, SCPE_UNATT);\r
422 }\r
423\r
424if (DEBUG_PRS (tm_dev)) fprintf (sim_deb,\r
425 ">>TM: op=%o, ma=%o, bc=%o, pos=%d\n", f, xma, tm_bc, uptr->pos);\r
426switch (f) { /* case on function */\r
427\r
428 case MTC_READ: /* read */\r
429 st = sim_tape_rdrecf (uptr, tmxb, &tbc, MT_MAXFR); /* read rec */\r
430 if (st == MTSE_RECE) tm_sta = tm_sta | STA_PAR; /* rec in error? */\r
431 else if (st != MTSE_OK) { /* other error? */\r
432 r = tm_map_err (uptr, st); /* map error */\r
433 break;\r
434 }\r
435 if (tbc > cbc) tm_sta = tm_sta | STA_RLE; /* wrong size? */\r
436 if (tbc < cbc) cbc = tbc; /* use smaller */\r
437 if (t = Map_WriteB (xma, cbc, tmxb)) { /* copy buf to mem */\r
438 tm_sta = tm_sta | STA_NXM; /* NXM, set err */\r
439 cbc = cbc - t; /* adj byte cnt */\r
440 }\r
441 xma = (xma + cbc) & 0777777; /* inc bus addr */\r
442 tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */\r
443 break;\r
444\r
445 case MTC_WRITE: /* write */\r
446 case MTC_WREXT: /* write ext gap */\r
447 if (t = Map_ReadB (xma, cbc, tmxb)) { /* copy mem to buf */\r
448 tm_sta = tm_sta | STA_NXM; /* NXM, set err */\r
449 cbc = cbc - t; /* adj byte cnt */\r
450 if (cbc == 0) break; /* no xfr? done */\r
451 }\r
452 if (st = sim_tape_wrrecf (uptr, tmxb, cbc)) /* write rec, err? */\r
453 r = tm_map_err (uptr, st); /* map error */\r
454 else {\r
455 xma = (xma + cbc) & 0777777; /* inc bus addr */\r
456 tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */\r
457 }\r
458 break;\r
459\r
460 case MTC_WREOF: /* write eof */\r
461 if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */\r
462 r = tm_map_err (uptr, st); /* map error */\r
463 break;\r
464\r
465 case MTC_SPACEF: /* space forward */\r
466 do {\r
467 tm_bc = (tm_bc + 1) & 0177777; /* incr wc */\r
468 if (st = sim_tape_sprecf (uptr, &tbc)) { /* spc rec fwd, err? */\r
469 r = tm_map_err (uptr, st); /* map error */\r
470 break;\r
471 }\r
472 } while (tm_bc != 0);\r
473 break;\r
474\r
475 case MTC_SPACER: /* space reverse */\r
476 do {\r
477 tm_bc = (tm_bc + 1) & 0177777; /* incr wc */\r
478 if (st = sim_tape_sprecr (uptr, &tbc)) { /* spc rec rev, err? */\r
479 r = tm_map_err (uptr, st); /* map error */\r
480 break;\r
481 }\r
482 } while (tm_bc != 0);\r
483 break;\r
484 } /* end case */\r
485\r
486tm_cmd = (tm_cmd & ~MTC_EMA) | ((xma >> (16 - MTC_V_EMA)) & MTC_EMA);\r
487tm_ca = xma & 0177777; /* update mem addr */\r
488tm_set_done (); /* set done */\r
489tm_updcsta (uptr); /* update status */\r
490if (DEBUG_PRS (tm_dev)) fprintf (sim_deb,\r
491 ">>TM: sta=%o, ma=%o, bc=%o, pos=%d\n",\r
492 tm_sta, tm_ca, tm_bc, uptr->pos);\r
493return r;\r
494}\r
495\r
496/* Update controller status */\r
497\r
498int32 tm_updcsta (UNIT *uptr)\r
499{\r
500tm_sta = (tm_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN);\r
501if (sim_tape_eot (uptr)) tm_sta = tm_sta | STA_EOT;\r
502if (sim_is_active (uptr)) tm_sta = tm_sta & ~STA_TUR;\r
503else tm_sta = tm_sta | STA_TUR;\r
504if (tm_sta & STA_EFLGS) tm_cmd = tm_cmd | MTC_ERR;\r
505else tm_cmd = tm_cmd & ~MTC_ERR;\r
506if ((tm_cmd & MTC_IE) == 0) CLR_INT (TM);\r
507return tm_sta;\r
508}\r
509\r
510/* Set done */\r
511\r
512void tm_set_done (void)\r
513{\r
514tm_cmd = tm_cmd | MTC_DONE;\r
515if (tm_cmd & MTC_IE) SET_INT (TM);\r
516return;\r
517}\r
518\r
519/* Map tape error status */\r
520\r
521t_stat tm_map_err (UNIT *uptr, t_stat st)\r
522{\r
523switch (st) {\r
524\r
525 case MTSE_FMT: /* illegal fmt */\r
526 case MTSE_UNATT: /* not attached */\r
527 tm_sta = tm_sta | STA_ILL;\r
528 case MTSE_OK: /* no error */\r
529 return SCPE_IERR;\r
530\r
531 case MTSE_TMK: /* tape mark */\r
532 uptr->USTAT = uptr->USTAT | STA_EOF; /* end of file */\r
533 break;\r
534\r
535 case MTSE_IOERR: /* IO error */\r
536 tm_sta = tm_sta | STA_PAR; /* parity error */\r
537 if (tm_stopioe) return SCPE_IOERR;\r
538 break;\r
539\r
540 case MTSE_INVRL: /* invalid rec lnt */\r
541 tm_sta = tm_sta | STA_PAR; /* parity error */\r
542 return SCPE_MTRLNT;\r
543\r
544 case MTSE_RECE: /* record in error */\r
545 tm_sta = tm_sta | STA_PAR; /* parity error */\r
546 break;\r
547\r
548 case MTSE_EOM: /* end of medium */\r
549 tm_sta = tm_sta | STA_BAD; /* bad tape */\r
550 break;\r
551\r
552 case MTSE_BOT: /* reverse into BOT */\r
553 uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */\r
554 break;\r
555\r
556 case MTSE_WRP: /* write protect */\r
557 tm_sta = tm_sta | STA_ILL; /* illegal operation */\r
558 break;\r
559 }\r
560\r
561return SCPE_OK;\r
562}\r
563\r
564/* Reset routine */\r
565\r
566t_stat tm_reset (DEVICE *dptr)\r
567{\r
568int32 u;\r
569UNIT *uptr;\r
570\r
571tm_cmd = MTC_DONE; /* set done */\r
572tm_bc = tm_ca = tm_db = tm_sta = tm_rdl = 0;\r
573CLR_INT (TM); /* clear interrupt */\r
574for (u = 0; u < TM_NUMDR; u++) { /* loop thru units */\r
575 uptr = tm_dev.units + u;\r
576 sim_tape_reset (uptr); /* reset tape */\r
577 sim_cancel (uptr); /* cancel activity */\r
578 if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_ONL |\r
579 (sim_tape_bot (uptr)? STA_BOT: 0) |\r
580 (sim_tape_wrp (uptr)? STA_WLK: 0);\r
581 else uptr->USTAT = 0;\r
582 }\r
583if (tmxb == NULL) tmxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8));\r
584if (tmxb == NULL) return SCPE_MEM;\r
585return SCPE_OK;\r
586}\r
587\r
588/* Attach routine */\r
589\r
590t_stat tm_attach (UNIT *uptr, char *cptr)\r
591{\r
592t_stat r;\r
593int32 u = uptr - tm_dev.units;\r
594\r
595r = sim_tape_attach (uptr, cptr);\r
596if (r != SCPE_OK) return r;\r
597uptr->USTAT = STA_ONL | STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0);\r
598if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr);\r
599return r;\r
600}\r
601\r
602/* Detach routine */\r
603\r
604t_stat tm_detach (UNIT* uptr)\r
605{\r
606int32 u = uptr - tm_dev.units;\r
607\r
608if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;\r
609if (!sim_is_active (uptr)) uptr->USTAT = 0;\r
610if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr);\r
611return sim_tape_detach (uptr);\r
612}\r
613\r
614/* Write lock/enable routine */\r
615\r
616t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc)\r
617{\r
618int32 u = uptr - tm_dev.units;\r
619\r
620if ((uptr->flags & UNIT_ATT) && \r
621 (val || sim_tape_wrp (uptr)))\r
622 uptr->USTAT = uptr->USTAT | STA_WLK;\r
623else uptr->USTAT = uptr->USTAT & ~STA_WLK;\r
624if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr);\r
625return SCPE_OK;\r
626}\r
627\r
628/* Device bootstrap\r
629\r
630 Magtape boot format changed over time. Originally, a boot tape\r
631 contained a boot loader in the first block. Eventually, the first\r
632 block was reserved for a tape label, and the second block was\r
633 expected to contain a boot loader. BSD and DEC operating systems\r
634 use the second block scheme, so it is the default.\r
635\r
636 To boot from the first block, use boot -o (old).\r
637*/\r
638\r
639#define BOOT_START 016000\r
640#define BOOT_ENTRY (BOOT_START + 2)\r
641#define BOOT_UNIT (BOOT_START + 010)\r
642#define BOOT_CSR (BOOT_START + 014)\r
643#define BOOT1_LEN (sizeof (boot1_rom) / sizeof (int16))\r
644#define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16))\r
645\r
646static const uint16 boot1_rom[] = {\r
647 0046524, /* boot_start: "TM" */\r
648 0012706, BOOT_START, /* mov #boot_start, sp */\r
649 0012700, 0000000, /* mov #unit_num, r0 */\r
650 0012701, 0172526, /* mov #172526, r1 ; mtcma */\r
651 0005011, /* clr (r1) */\r
652 0010141, /* mov r1, -(r1) ; mtbrc */\r
653 0010002, /* mov r0,r2 */\r
654 0000302, /* swab r2 */\r
655 0062702, 0060003, /* add #60003, r2 */\r
656 0010241, /* mov r2, -(r1) ; read + go */\r
657 0105711, /* tstb (r1) ; mtc */\r
658 0100376, /* bpl .-2 */\r
659 0005002, /* clr r2 */\r
660 0005003, /* clr r3 */\r
661 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */\r
662 0005005, /* clr r5 */\r
663 0005007 /* clr r7 */\r
664 };\r
665\r
666static const uint16 boot2_rom[] = {\r
667 0046524, /* boot_start: "TM" */\r
668 0012706, BOOT_START, /* mov #boot_start, sp */\r
669 0012700, 0000000, /* mov #unit_num, r0 */\r
670 0012701, 0172526, /* mov #172526, r1 ; mtcma */\r
671 0005011, /* clr (r1) */\r
672 0012741, 0177777, /* mov #-1, -(r1) ; mtbrc */\r
673 0010002, /* mov r0,r2 */\r
674 0000302, /* swab r2 */\r
675 0062702, 0060011, /* add #60011, r2 */\r
676 0010241, /* mov r2, -(r1) ; space + go */\r
677 0105711, /* tstb (r1) ; mtc */\r
678 0100376, /* bpl .-2 */\r
679 0010002, /* mov r0,r2 */\r
680 0000302, /* swab r2 */\r
681 0062702, 0060003, /* add #60003, r2 */\r
682 0010211, /* mov r2, (r1) ; read + go */\r
683 0105711, /* tstb (r1) ; mtc */\r
684 0100376, /* bpl .-2 */\r
685 0005002, /* clr r2 */\r
686 0005003, /* clr r3 */\r
687 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */\r
688 0005005, /* clr r5 */\r
689 0005007 /* clr r7 */\r
690 };\r
691\r
692t_stat tm_boot (int32 unitno, DEVICE *dptr)\r
693{\r
694int32 i;\r
695extern int32 saved_PC;\r
696extern int32 sim_switches;\r
697\r
698sim_tape_rewind (&tm_unit[unitno]);\r
699if (sim_switches & SWMASK ('O')) {\r
700 for (i = 0; i < BOOT1_LEN; i++)\r
701 M[(BOOT_START >> 1) + i] = boot1_rom[i];\r
702 }\r
703else {\r
704 for (i = 0; i < BOOT2_LEN; i++)\r
705 M[(BOOT_START >> 1) + i] = boot2_rom[i];\r
706 }\r
707M[BOOT_UNIT >> 1] = unitno;\r
708M[BOOT_CSR >> 1] = (tm_dib.ba & DMASK) + 06;\r
709saved_PC = BOOT_ENTRY;\r
710return SCPE_OK;\r
711} \r