First Commit of my working state
[simh.git] / Ibm1130 / ibm1130_disk.c
1 /* ibm1130_disk.c: IBM 1130 disk IO simulator
2
3 NOTE - there is a problem with this code. The Device Status Word (DSW) is
4 computed from current conditions when requested by an XIO load status
5 command; the value of DSW available to the simulator's examine & save
6 commands may NOT be accurate. This should probably be fixed.
7
8 Based on the SIMH package written by Robert M Supnik
9
10 * (C) Copyright 2002, Brian Knittel.
11 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
12 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
13 * usual yada-yada. Please keep this notice and the copyright in any distributions
14 * or modifications.
15 *
16 * Revision History
17 * 05-dec-06 Added cgiwritable mode
18 *
19 * 19-Dec-05 We no longer issue an operation complete interrupt if an INITR, INITW
20 * or CONTROL operation is attemped on a drive that is not online. DATA_ERROR
21 * is now only indicated in the DSW when
22 *
23 * 02-Nov-04 Addes -s option to boot to leave switches alone.
24 * 15-jun-03 moved actual read on XIO read to end of time interval,
25 * as the APL boot card required 2 instructions to run between the
26 * time read was initiated and the time the data was read (a jump and a wait)
27 *
28 * 01-sep-02 corrected treatment of -m and -r flags in dsk_attach
29 * in cgi mode, so that file is opened readonly but emulated
30 * disk is writable.
31 *
32 * This is not a supported product, but I welcome bug reports and fixes.
33 * Mail to simh@ibm1130.org
34 */
35
36 #include "ibm1130_defs.h"
37 #include "memory.h"
38
39 #define TRACE_DMS_IO /* define to enable debug of DMS phase IO */
40
41 #ifdef TRACE_DMS_IO
42 extern int32 sim_switches;
43 extern int32 sim_quiet;
44 static int trace_dms = 0;
45 static void tracesector (int iswrite, int nwords, int addr, int sector);
46 static t_stat where_cmd (int flag, char *ptr);
47 static t_stat phdebug_cmd (int flag, char *ptr);
48 static t_stat fdump_cmd (int flags, char *cptr);
49 static void enable_dms_tracing (int newsetting);
50 #endif
51
52 /* Constants */
53
54 #define DSK_NUMWD 321 /* words/sector */
55 #define DSK_NUMSC 4 /* sectors/surface */
56 #define DSK_NUMSF 2 /* surfaces/cylinder */
57 #define DSK_NUMCY 203 /* cylinders/drive */
58 #define DSK_NUMTR (DSK_NUMCY * DSK_NUMSF) /* tracks/drive */
59 #define DSK_NUMDR 5 /* drives/controller */
60 #define DSK_SIZE (DSK_NUMCY * DSK_NUMSF * DSK_NUMSC * DSK_NUMWD) /* words/drive */
61
62 #define UNIT_V_RONLY (UNIT_V_UF + 0) /* hwre write lock */
63 #define UNIT_V_OPERR (UNIT_V_UF + 1) /* operation error flag */
64 #define UNIT_V_HARDERR (UNIT_V_UF + 2) /* hard error flag (reset on power down) */
65 #define UNIT_RONLY (1u << UNIT_V_RONLY)
66 #define UNIT_OPERR (1u << UNIT_V_OPERR)
67 #define UNIT_HARDERR (1u << UNIT_V_HARDERR)
68
69 #define MEM_MAPPED(uptr) (uptr->flags & UNIT_BUF) /* disk buffered in memory */
70
71 #define IO_NONE 0 /* last operation, used to ensure fseek between read and write */
72 #define IO_READ 1
73 #define IO_WRITE 2
74
75 #define DSK_DSW_DATA_ERROR 0x8000 /* device status word bits */
76 #define DSK_DSW_OP_COMPLETE 0x4000
77 #define DSK_DSW_NOT_READY 0x2000
78 #define DSK_DSW_DISK_BUSY 0x1000
79 #define DSK_DSW_CARRIAGE_HOME 0x0800
80 #define DSK_DSW_SECTOR_MASK 0x0003
81
82 /* device status words */
83 static int16 dsk_dsw[DSK_NUMDR] = {DSK_DSW_NOT_READY, DSK_DSW_NOT_READY, DSK_DSW_NOT_READY, DSK_DSW_NOT_READY, DSK_DSW_NOT_READY};
84 static int16 dsk_sec[DSK_NUMDR] = {0}; /* next-sector-up */
85 static char dsk_lastio[DSK_NUMDR]; /* last stdio operation: IO_READ or IO_WRITE */
86 int32 dsk_swait = 50; /* seek time -- see how short a delay we can get away with */
87 int32 dsk_rwait = 50; /* rotate time */
88 static t_bool raw_disk_debug = FALSE;
89
90 static t_stat dsk_svc (UNIT *uptr);
91 static t_stat dsk_reset (DEVICE *dptr);
92 static t_stat dsk_attach (UNIT *uptr, char *cptr);
93 static t_stat dsk_detach (UNIT *uptr);
94 static t_stat dsk_boot (int unitno, DEVICE *dptr);
95
96 static void diskfail (UNIT *uptr, int dswflag, int unitflag, t_bool do_interrupt);
97
98 /* DSK data structures
99
100 dsk_dev disk device descriptor
101 dsk_unit unit descriptor
102 dsk_reg register list
103 */
104
105 UNIT dsk_unit[] = {
106 { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) },
107 { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) },
108 { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) },
109 { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) },
110 { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }
111 };
112
113 #define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)
114
115 /* Parameters in the unit descriptor */
116
117 #define CYL u3 /* current cylinder */
118 #define FUNC u4 /* current function */
119
120 REG dsk_reg[] = {
121 { HRDATA (DSKDSW0, dsk_dsw[0], 16) },
122 { HRDATA (DSKDSW1, dsk_dsw[1], 16) },
123 { HRDATA (DSKDSW2, dsk_dsw[2], 16) },
124 { HRDATA (DSKDSW3, dsk_dsw[3], 16) },
125 { HRDATA (DSKDSW4, dsk_dsw[4], 16) },
126 { DRDATA (STIME, dsk_swait, 24), PV_LEFT },
127 { DRDATA (RTIME, dsk_rwait, 24), PV_LEFT },
128 { NULL } };
129
130 MTAB dsk_mod[] = {
131 { UNIT_RONLY, 0, "write enabled", "ENABLED", NULL },
132 { UNIT_RONLY, UNIT_RONLY, "write locked", "LOCKED", NULL },
133 { 0 } };
134
135 DEVICE dsk_dev = {
136 "DSK", dsk_unit, dsk_reg, dsk_mod,
137 DSK_NUMDR, 16, 16, 1, 16, 16,
138 NULL, NULL, &dsk_reset,
139 dsk_boot, dsk_attach, dsk_detach};
140
141 static int32 dsk_ilswbit[DSK_NUMDR] = { /* interrupt level status word bits for the drives */
142 ILSW_2_1131_DISK,
143 ILSW_2_2310_DRV_1,
144 ILSW_2_2310_DRV_2,
145 ILSW_2_2310_DRV_3,
146 ILSW_2_2310_DRV_4,
147 };
148
149 static int32 dsk_ilswlevel[DSK_NUMDR] =
150 {
151 2, /* interrupt levels for the drives */
152 2, 2, 2, 2
153 };
154
155 typedef enum {DSK_FUNC_IDLE, DSK_FUNC_READ, DSK_FUNC_VERIFY, DSK_FUNC_WRITE, DSK_FUNC_SEEK, DSK_FUNC_FAILED} DSK_FUNC;
156
157 static struct tag_dsk_action { /* stores data needed for pending IO activity */
158 int32 io_address;
159 uint32 io_filepos;
160 int io_nwords;
161 int io_sector;
162 } dsk_action[DSK_NUMDR];
163
164 /* xio_disk - XIO command interpreter for the disk drives */
165 /*
166 * device status word:
167 *
168 * 0 data error, occurs when:
169 * 1. A modulo 4 error is detected during a read, read-check, or write operation.
170 * 2. The disk storage is in a read or write mode at the leading edge of a sector pulse.
171 * 3. A seek-incomplete signal is received from the 2311.
172 * 4. A write select error has occurred in the disk storage drive.
173 * 5. The power unsafe latch is set in the attachment.
174 * Conditions 1, 2, and 3 are turned off by a sense device command with modifier bit 15
175 * set to 1. Conditions 4 and 5 are turned off by powering the drive off and back on.
176 * 1 operation complete
177 * 2 not ready, occurs when disk not ready or busy or disabled or off-line or
178 * power unsafe latch set. Also included in the disk not ready is the write select error,
179 * which can be a result of power unsafe or write select.
180 * 3 disk busy
181 * 4 carriage home (on cyl 0)
182 * 15-16: number of next sector spinning into position.
183 */
184
185 extern void void_backtrace (int afrom, int ato);
186
187 void xio_disk (int32 iocc_addr, int32 func, int32 modify, int drv)
188 {
189 int i, rev, nsteps, newcyl, sec, nwords;
190 uint32 newpos; /* changed from t_addr to uint32 in anticipation of simh 64-bit development */
191 char msg[80];
192 UNIT *uptr = dsk_unit+drv;
193 int16 buf[DSK_NUMWD];
194
195 if (! BETWEEN(drv, 0, DSK_NUMDR-1)) { /* hmmm, invalid drive */
196 if (func != XIO_SENSE_DEV) { /* tried to use it, too */
197 /* just do nothing, as if the controller isn't there. NAMCRA at N0116300 tests for drives by attempting reads
198 sprintf(msg, "Op %x on invalid drive number %d", func, drv);
199 xio_error(msg);
200 */
201 }
202 return;
203 }
204
205 CLRBIT(uptr->flags, UNIT_OPERR); /* clear pending error flag from previous op, if any */
206
207 switch (func) {
208 case XIO_INITR:
209 if (! IS_ONLINE(uptr)) { /* disk is offline */
210 diskfail(uptr, 0, 0, FALSE);
211 break;
212 }
213
214 sim_cancel(uptr); /* cancel any pending ops */
215 dsk_dsw[drv] |= DSK_DSW_DISK_BUSY; /* and mark the disk as busy */
216
217 nwords = M[iocc_addr++ & mem_mask]; /* get word count w/o upsetting SAR/SBR */
218
219 if (nwords == 0) /* this is bad -- on real 1130, this locks up disk controller ! */
220 break;
221
222 if (! BETWEEN(nwords, 1, DSK_NUMWD)) { /* count bad */
223 SETBIT(uptr->flags, UNIT_OPERR); /* set data error DSW bit when op complete */
224 nwords = DSK_NUMWD; /* limit xfer to proper sector size */
225 }
226
227 sec = modify & 0x07; /* get sector on cylinder */
228
229 if ((modify & 0x0080) == 0) { /* it's a real read if it's not a read check */
230 /* ah. We have a problem. The APL boot card counts on there being time for at least one
231 * more instruction to execute between the XIO read and the time the data starts loading
232 * into core. So, we have to defer the actual read operation a bit. Might as well wait
233 * until it's time to issue the operation complete interrupt. This means saving the
234 * IO information, then performing the actual read in dsk_svc.
235 */
236
237 newpos = (uptr->CYL*DSK_NUMSC*DSK_NUMSF + sec)*2*DSK_NUMWD;
238
239 dsk_action[drv].io_address = iocc_addr;
240 dsk_action[drv].io_nwords = nwords;
241 dsk_action[drv].io_sector = sec;
242 dsk_action[drv].io_filepos = newpos;
243
244 uptr->FUNC = DSK_FUNC_READ;
245 }
246 else {
247 trace_io("* DSK%d verify %d.%d (%x)", drv, uptr->CYL, sec, uptr->CYL*8 + sec);
248
249 if (raw_disk_debug)
250 printf("* DSK%d verify %d.%d (%x)", drv, uptr->CYL, sec, uptr->CYL*8 + sec);
251
252 uptr->FUNC = DSK_FUNC_VERIFY;
253 }
254
255 sim_activate(uptr, dsk_rwait);
256 break;
257
258 case XIO_INITW:
259 if (! IS_ONLINE(uptr)) { /* disk is offline */
260 diskfail(uptr, 0, 0, FALSE);
261 break;
262 }
263
264 if (uptr->flags & UNIT_RONLY) { /* oops, write to RO disk? permanent error until disk is powered off/on */
265 diskfail(uptr, DSK_DSW_DATA_ERROR, UNIT_HARDERR, FALSE);
266 break;
267 }
268
269 sim_cancel(uptr); /* cancel any pending ops */
270 dsk_dsw[drv] |= DSK_DSW_DISK_BUSY; /* and mark drive as busy */
271
272 nwords = M[iocc_addr++ & mem_mask]; /* get word count w/o upsetting SAR/SBR */
273
274 if (nwords == 0) /* this is bad -- locks up disk controller ! */
275 break;
276
277 if (! BETWEEN(nwords, 1, DSK_NUMWD)) { /* count bad */
278 SETBIT(uptr->flags, UNIT_OPERR); /* set data error DSW bit when op complete */
279 nwords = DSK_NUMWD; /* limit xfer to proper sector size */
280 }
281
282 sec = modify & 0x07; /* get sector on cylinder */
283 newpos = (uptr->CYL*DSK_NUMSC*DSK_NUMSF + sec)*2*DSK_NUMWD;
284
285 trace_io("* DSK%d wrote %d words from M[%04x-%04x] to %d.%d (%x, %x)", drv, nwords, iocc_addr & mem_mask, (iocc_addr + nwords - 1) & mem_mask, uptr->CYL, sec, uptr->CYL*8 + sec, newpos);
286
287 if (raw_disk_debug)
288 printf("* DSK%d XIO @ %04x wrote %d words from M[%04x-%04x] to %d.%d (%x, %x)\n", drv, prev_IAR, nwords, iocc_addr & mem_mask, (iocc_addr + nwords - 1) & mem_mask, uptr->CYL, sec, uptr->CYL*8 + sec, newpos);
289
290 #ifdef TRACE_DMS_IO
291 if (trace_dms)
292 tracesector(1, nwords, iocc_addr & mem_mask, uptr->CYL*8 + sec);
293 #endif
294 for (i = 0; i < nwords; i++)
295 buf[i] = M[iocc_addr++ & mem_mask];
296
297 for (; i < DSK_NUMWD; i++) /* rest of sector gets zeroed */
298 buf[i] = 0;
299
300 i = uptr->CYL*8 + sec;
301 if (buf[0] != i)
302 printf("*DSK writing bad sector#\n");
303
304 if (MEM_MAPPED(uptr)) {
305 memcpy((char *) uptr->filebuf + newpos, buf, 2*DSK_NUMWD);
306 uptr->hwmark = newpos + 2*DSK_NUMWD;
307 }
308 else {
309 if (uptr->pos != newpos || dsk_lastio[drv] != IO_WRITE) {
310 fseek(uptr->fileref, newpos, SEEK_SET);
311 dsk_lastio[drv] = IO_WRITE;
312 }
313
314 fxwrite(buf, 2, DSK_NUMWD, uptr->fileref);
315 uptr->pos = newpos + 2*DSK_NUMWD;
316 }
317
318 uptr->FUNC = DSK_FUNC_WRITE;
319 sim_activate(uptr, dsk_rwait);
320 break;
321
322 case XIO_CONTROL: /* step fwd/rev */
323 if (! IS_ONLINE(uptr)) {
324 diskfail(uptr, 0, 0, FALSE);
325 break;
326 }
327
328 sim_cancel(uptr);
329
330 rev = modify & 4;
331 nsteps = iocc_addr & 0x00FF;
332 if (nsteps == 0) /* 0 steps does not cause op complete interrupt */
333 break;
334
335 newcyl = uptr->CYL + (rev ? (-nsteps) : nsteps);
336 if (newcyl < 0)
337 newcyl = 0;
338 else if (newcyl >= DSK_NUMCY)
339 newcyl = DSK_NUMCY-1;
340
341 uptr->FUNC = DSK_FUNC_SEEK;
342 uptr->CYL = newcyl;
343 sim_activate(uptr, dsk_swait); /* schedule interrupt */
344
345 dsk_dsw[drv] |= DSK_DSW_DISK_BUSY;
346 trace_io("* DSK%d at cyl %d", drv, newcyl);
347 break;
348
349 case XIO_SENSE_DEV:
350 CLRBIT(dsk_dsw[drv], DSK_DSW_CARRIAGE_HOME|DSK_DSW_NOT_READY);
351
352 if ((uptr->flags & UNIT_HARDERR) || (dsk_dsw[drv] & DSK_DSW_DISK_BUSY) || ! IS_ONLINE(uptr))
353 SETBIT(dsk_dsw[drv], DSK_DSW_NOT_READY);
354 else if (uptr->CYL <= 0) {
355 SETBIT(dsk_dsw[drv], DSK_DSW_CARRIAGE_HOME);
356 uptr->CYL = 0;
357 }
358
359 dsk_sec[drv] = (int16) ((dsk_sec[drv] + 1) % 4); /* advance the "next sector" count every time */
360 ACC = dsk_dsw[drv] | dsk_sec[drv];
361
362 if (modify & 0x01) { /* reset interrupts */
363 CLRBIT(dsk_dsw[drv], DSK_DSW_OP_COMPLETE|DSK_DSW_DATA_ERROR);
364 CLRBIT(ILSW[dsk_ilswlevel[drv]], dsk_ilswbit[drv]);
365 }
366 break;
367
368 default:
369 sprintf(msg, "Invalid disk XIO function %x", func);
370 xio_error(msg);
371 }
372 }
373
374 /* diskfail - schedule an operation complete that sets the error bit */
375
376 static void diskfail (UNIT *uptr, int dswflag, int unitflag, t_bool do_interrupt)
377 {
378 int drv = uptr - dsk_unit;
379
380 sim_cancel(uptr); /* cancel any pending ops */
381 SETBIT(dsk_dsw[drv], dswflag); /* set any specified DSW bits */
382 SETBIT(uptr->flags, unitflag); /* set any specified unit flag bits */
383 uptr->FUNC = DSK_FUNC_FAILED; /* tell svc routine why it failed */
384
385 if (do_interrupt)
386 sim_activate(uptr, 1); /* schedule an immediate op complete interrupt */
387 }
388
389 t_stat dsk_svc (UNIT *uptr)
390 {
391 int drv = uptr - dsk_unit, i, nwords, sec;
392 int16 buf[DSK_NUMWD];
393 uint32 newpos; /* changed from t_addr to uint32 in anticipation of simh 64-bit development */
394 int32 iocc_addr;
395
396 if (uptr->FUNC == DSK_FUNC_IDLE) /* service function called with no activity? not good, but ignore */
397 return SCPE_OK;
398
399 CLRBIT(dsk_dsw[drv], DSK_DSW_DISK_BUSY); /* activate operation complete interrupt */
400 SETBIT(dsk_dsw[drv], DSK_DSW_OP_COMPLETE);
401
402 if (uptr->flags & (UNIT_OPERR|UNIT_HARDERR)) { /* word count error or data error */
403 SETBIT(dsk_dsw[drv], DSK_DSW_DATA_ERROR);
404 CLRBIT(uptr->flags, UNIT_OPERR); /* soft error is one time occurrence; don't clear hard error */
405 }
406 /* schedule interrupt */
407 SETBIT(ILSW[dsk_ilswlevel[drv]], dsk_ilswbit[drv]);
408
409 switch (uptr->FUNC) { /* take care of business */
410 case DSK_FUNC_IDLE:
411 case DSK_FUNC_VERIFY:
412 case DSK_FUNC_WRITE:
413 case DSK_FUNC_SEEK:
414 case DSK_FUNC_FAILED:
415 break;
416
417 case DSK_FUNC_READ: /* actually read the data into core */
418 iocc_addr = dsk_action[drv].io_address; /* recover saved parameters */
419 nwords = dsk_action[drv].io_nwords;
420 newpos = dsk_action[drv].io_filepos;
421 sec = dsk_action[drv].io_sector;
422
423 if (MEM_MAPPED(uptr)) {
424 memcpy(buf, (char *) uptr->filebuf + newpos, 2*DSK_NUMWD);
425 }
426 else {
427 if (uptr->pos != newpos || dsk_lastio[drv] != IO_READ) {
428 fseek(uptr->fileref, newpos, SEEK_SET);
429 dsk_lastio[drv] = IO_READ;
430 uptr->pos = newpos;
431 }
432 fxread(buf, 2, DSK_NUMWD, uptr->fileref); /* read whole sector so we're in position for next read */
433 uptr->pos = newpos + 2*DSK_NUMWD;
434 }
435
436 void_backtrace(iocc_addr, iocc_addr + nwords - 1); /* mark prev instruction as altered */
437
438 trace_io("* DSK%d read %d words from %d.%d (%x, %x) to M[%04x-%04x]", drv, nwords, uptr->CYL, sec, uptr->CYL*8 + sec, newpos, iocc_addr & mem_mask,
439 (iocc_addr + nwords - 1) & mem_mask);
440
441 /* this will help debug the monitor by letting me watch phase loading */
442 if (raw_disk_debug)
443 printf("* DSK%d XIO @ %04x read %d words from %d.%d (%x, %x) to M[%04x-%04x]\n", drv, prev_IAR, nwords, uptr->CYL, sec, uptr->CYL*8 + sec, newpos, iocc_addr & mem_mask,
444 (iocc_addr + nwords - 1) & mem_mask);
445
446 i = uptr->CYL*8 + sec;
447 if (buf[0] != i)
448 printf("*DSK read bad sector #\n");
449
450 for (i = 0; i < nwords; i++)
451 M[(iocc_addr+i) & mem_mask] = buf[i];
452
453 #ifdef TRACE_DMS_IO
454 if (trace_dms)
455 tracesector(0, nwords, iocc_addr & mem_mask, uptr->CYL*8 + sec);
456 #endif
457 break;
458
459 default:
460 fprintf(stderr, "Unexpected FUNC %x in dsk_svc(%d)\n", uptr->FUNC, drv);
461 break;
462
463 }
464
465 uptr->FUNC = DSK_FUNC_IDLE; /* we're done with this operation */
466
467 return SCPE_OK;
468 }
469
470 t_stat dsk_reset (DEVICE *dptr)
471 {
472 int drv;
473 UNIT *uptr;
474
475 #ifdef TRACE_DMS_IO
476 /* add the WHERE command. It finds the phase that was loaded at given address and indicates */
477 /* the offset in the phase */
478 register_cmd("WHERE", &where_cmd, 0, "w{here} address find phase and offset of an address\n");
479 register_cmd("PHDEBUG", &phdebug_cmd, 0, "ph{debug} off|phlo phhi break on phase load\n");
480 register_cmd("FDUMP", &fdump_cmd, 0, NULL);
481 #endif
482
483 for (drv = 0, uptr = dsk_dev.units; drv < DSK_NUMDR; drv++, uptr++) {
484 sim_cancel(uptr);
485
486 CLRBIT(ILSW[2], dsk_ilswbit[drv]);
487 CLRBIT(uptr->flags, UNIT_OPERR|UNIT_HARDERR);
488
489 uptr->CYL = 0;
490 uptr->FUNC = DSK_FUNC_IDLE;
491 dsk_dsw[drv] = (int16) ((uptr->flags & UNIT_ATT) ? DSK_DSW_CARRIAGE_HOME : 0);
492 }
493
494 calc_ints();
495
496 return SCPE_OK;
497 }
498
499 static t_stat dsk_attach (UNIT *uptr, char *cptr)
500 {
501 int drv = uptr - dsk_unit;
502 t_stat rval;
503
504 sim_cancel(uptr); /* cancel current IO */
505 dsk_lastio[drv] = IO_NONE;
506
507 if (uptr->flags & UNIT_ATT) /* dismount current disk */
508 if ((rval = dsk_detach(uptr)) != SCPE_OK)
509 return rval;
510
511 uptr->CYL = 0; /* reset the device */
512 uptr->FUNC = DSK_FUNC_IDLE;
513 dsk_dsw[drv] = DSK_DSW_CARRIAGE_HOME;
514
515 CLRBIT(uptr->flags, UNIT_RO|UNIT_ROABLE|UNIT_BUFABLE|UNIT_BUF|UNIT_RONLY|UNIT_OPERR|UNIT_HARDERR);
516 CLRBIT(ILSW[2], dsk_ilswbit[drv]);
517 calc_ints();
518
519 if (sim_switches & SWMASK('M')) /* if memory mode (e.g. for CGI), buffer the file */
520 SETBIT(uptr->flags, UNIT_BUFABLE|UNIT_MUSTBUF);
521
522 if (sim_switches & SWMASK('R')) /* read lock mode */
523 SETBIT(uptr->flags, UNIT_RO|UNIT_ROABLE|UNIT_RONLY);
524
525 if (cgi && (sim_switches & SWMASK('M')) && ! cgiwritable) { /* if cgi and memory mode, but writable option not specified */
526 sim_switches |= SWMASK('R'); /* have attach_unit open file in readonly mode */
527 SETBIT(uptr->flags, UNIT_ROABLE); /* but don't set the UNIT_RONLY flag so DMS can write to the buffered image */
528 }
529
530 if ((rval = attach_unit(uptr, quotefix(cptr))) != SCPE_OK) { /* mount new disk */
531 SETBIT(dsk_dsw[drv], DSK_DSW_NOT_READY);
532 return rval;
533 }
534
535 if (drv == 0) {
536 disk_ready(TRUE);
537 disk_unlocked(FALSE);
538 }
539
540 enable_dms_tracing(sim_switches & SWMASK('D'));
541 raw_disk_debug = sim_switches & SWMASK('G');
542
543 return SCPE_OK;
544 }
545
546 static t_stat dsk_detach (UNIT *uptr)
547 {
548 t_stat rval;
549 int drv = uptr - dsk_unit;
550
551 sim_cancel(uptr);
552
553 if ((rval = detach_unit(uptr)) != SCPE_OK)
554 return rval;
555
556 CLRBIT(ILSW[2], dsk_ilswbit[drv]);
557 CLRBIT(uptr->flags, UNIT_OPERR|UNIT_HARDERR);
558 calc_ints();
559
560 uptr->CYL = 0;
561 uptr->FUNC = DSK_FUNC_IDLE;
562 dsk_dsw[drv] = DSK_DSW_NOT_READY;
563
564 if (drv == 0) {
565 disk_unlocked(TRUE);
566 disk_ready(FALSE);
567 }
568
569 return SCPE_OK;
570 }
571
572 /* boot routine - if they type BOOT DSK, load the standard boot card. */
573
574 static t_stat dsk_boot (int unitno, DEVICE *dptr)
575 {
576 t_stat rval;
577
578 if ((rval = reset_all(0)) != SCPE_OK)
579 return rval;
580
581 return load_cr_boot(unitno, sim_switches);
582 }
583
584 #ifdef TRACE_DMS_IO
585
586 static struct {
587 int phid;
588 char *name;
589 } phase[] = {
590 # include "dmsr2v12phases.h"
591 0xFFFF, ""
592 };
593
594 #pragma pack(2)
595 #define MAXSLET ((3*320)/4)
596 struct tag_slet {
597 int16 phid;
598 int16 addr;
599 int16 nwords;
600 int16 sector;
601 } slet[MAXSLET] = {
602 # include "dmsr2v12slet.h" /* without RPG, use this info until overwritten by actual data from disk */
603 };
604
605 #pragma pack()
606
607 #define MAXMSEG 100
608 struct tag_mseg {
609 char *name;
610 int addr, offset, len, phid;
611 } mseg[MAXMSEG];
612 int nseg = 0;
613
614 static void enable_dms_tracing (int newsetting)
615 {
616 nseg = 0; /* clear the segment map */
617
618 if ((newsetting && trace_dms) || ! (newsetting || trace_dms))
619 return;
620
621 trace_dms = newsetting;
622 if (! sim_quiet)
623 printf("DMS disk tracing is now %sabled\n", trace_dms ? "en" : "dis");
624 }
625
626 char * saywhere (int addr)
627 {
628 int i;
629 static char buf[150];
630
631 for (i = 0; i < nseg; i++) {
632 if (addr >= mseg[i].addr && addr < (mseg[i].addr+mseg[i].len)) {
633 sprintf(buf, "/%04x = /%04x + /%x in ", addr, mseg[i].addr - mseg[i].offset, addr-mseg[i].addr + mseg[i].offset);
634 if (mseg[i].phid > 0)
635 sprintf(buf+strlen(buf), "phase %02x (%s)", mseg[i].phid, mseg[i].name);
636 else
637 sprintf(buf+strlen(buf), "%s", mseg[i].name);
638
639 return buf;
640 }
641 }
642 return NULL;
643 }
644
645 static int phdebug_lo = -1, phdebug_hi = -1;
646
647 static t_stat phdebug_cmd (int flag, char *ptr)
648 {
649 int val1, val2;
650
651 if (strcmpi(ptr, "off") == 0)
652 phdebug_lo = phdebug_hi = -1;
653 else {
654 switch(sscanf(ptr, "%x%x", &val1, &val2)) {
655 case 1:
656 phdebug_lo = phdebug_hi = val1;
657 enable_dms_tracing(TRUE);
658 break;
659
660 case 2:
661 phdebug_lo = val1;
662 phdebug_hi = val2;
663 enable_dms_tracing(TRUE);
664 break;
665
666 default:
667 printf("Usage: phdebug off | phdebug phfrom [phto]\n");
668 break;
669 }
670 }
671 return SCPE_OK;
672 }
673
674 static t_stat where_cmd (int flag, char *ptr)
675 {
676 int addr;
677 char *where;
678
679 if (! trace_dms) {
680 printf("Tracing is disabled. To enable, attach disk with -d switch\n");
681 return SCPE_OK;
682 }
683
684 if (sscanf(ptr, "%x", &addr) != 1)
685 return SCPE_ARG;
686
687 if ((where = saywhere(addr)) == NULL)
688 printf("/%04x not found\n", addr);
689 else
690 printf("%s\n", where);
691
692 return SCPE_OK;
693 }
694
695 /* savesector - save info on a sector just read. THIS IS NOT YET TESTED */
696
697 static void addseg (int i)
698 {
699 if (! trace_dms)
700 return;
701
702 if (nseg >= MAXMSEG) {
703 printf("(Memory map full, disabling tracing)\n");
704 trace_dms = 0;
705 nseg = -1;
706 return;
707 }
708 memcpy(mseg+i+1, mseg+i, (nseg-i)*sizeof(mseg[0]));
709 nseg++;
710 }
711
712 static void delseg (int i)
713 {
714 if (! trace_dms)
715 return;
716
717 if (nseg > 0) {
718 nseg--;
719 memcpy(mseg+i, mseg+i+1, (nseg-i)*sizeof(mseg[0]));
720 }
721 }
722
723 static void savesector (int addr, int offset, int len, int phid, char *name)
724 {
725 int i;
726
727 if (! trace_dms)
728 return;
729
730 addr++; /* first word is sector address, so account for that */
731 len--;
732
733 for (i = 0; i < nseg; i++) {
734 if (addr >= (mseg[i].addr+mseg[i].len)) /* entirely after this entry */
735 continue;
736
737 if (mseg[i].addr < addr) { /* old one starts before this. split it */
738 addseg(i);
739 mseg[i].len = addr-mseg[i].addr;
740 i++;
741 mseg[i].addr = addr;
742 mseg[i].len -= mseg[i-1].len;
743 }
744
745 break;
746 }
747
748 addseg(i); /* add new segment. Old one ends up after this */
749
750 if (i >= MAXMSEG)
751 return;
752
753 mseg[i].addr = addr;
754 mseg[i].offset = offset;
755 mseg[i].phid = phid;
756 mseg[i].len = len;
757 mseg[i].name = name;
758
759 i++; /* delete any segments completely covered */
760
761 while (i < nseg && (mseg[i].addr+mseg[i].len) <= (addr+len))
762 delseg(i);
763
764 if (i < nseg && mseg[i].addr < (addr+len)) { /* old one extends past this. Retain the end */
765 mseg[i].len = (mseg[i].addr+mseg[i].len) - (addr+len);
766 mseg[i].addr = addr+len;
767 }
768 }
769
770 static void tracesector (int iswrite, int nwords, int addr, int sector)
771 {
772 int i, phid = 0, offset = 0;
773 char *name = NULL;
774
775 if (nwords < 3 || ! trace_dms)
776 return;
777
778 switch (sector) { /* explicitly known sector name */
779 case 0: name = "ID/COLD START"; break;
780 case 1: name = "DCOM"; break;
781 case 2: name = "RESIDENT IMAGE"; break;
782 case 3:
783 case 4:
784 case 5: name = "SLET"; /* save just-read or written SLET info */
785 memmove(&slet[(320/4)*(sector-3)], &M[addr+1], nwords*2);
786 break;
787 case 6: name = "RELOAD TABLE"; break;
788 case 7: name = "PAGE HEADER"; break;
789 }
790
791 printf("* %04x: %3d /%04x %c %3d.%d ",
792 prev_IAR, nwords, addr, iswrite ? 'W' : 'R', sector/8, sector%8);
793
794 if (name == NULL) { /* look up sector in SLET */
795 for (i = 0; i < MAXSLET; i++) {
796 if (slet[i].phid == 0) /* not found */
797 goto done;
798 else if (slet[i].sector > sector) {
799 if (--i >= 0) {
800 if (sector >= slet[i].sector && sector <= (slet[i].sector + slet[i].nwords/320)) {
801 phid = slet[i].phid;
802 offset = (sector-slet[i].sector)*320;
803 break;
804 }
805 }
806 goto done;
807 }
808 if (slet[i].sector == sector) {
809 phid = slet[i].phid; /* we found the starting sector */
810 break;
811 }
812 }
813
814 if (i >= MAXSLET) /* was not found */
815 goto done;
816
817 name = "?";
818 for (i = sizeof(phase)/sizeof(phase[0]); --i >= 0; ) {
819 if (phase[i].phid == phid) { /* look up name */
820 name = phase[i].name;
821 break;
822 }
823 }
824 printf("%02x %s", phid, name);
825 }
826 else
827 printf("%s", name);
828
829 done:
830 putchar('\n');
831
832 if (phid >= phdebug_lo && phid <= phdebug_hi && offset == 0)
833 break_simulation(STOP_PHASE_BREAK); /* break on read of first sector of indicated phases */
834
835 if (name != NULL && *name != '?' && ! iswrite)
836 savesector(addr, offset, nwords, phid, name);
837 }
838
839 static t_stat fdump_cmd (int flags, char *cptr)
840 {
841 int addr = 0x7a24; /* address of next statement */
842 int sofst = 0x7a26, symaddr;
843 int cword, nwords, stype, has_stnum, strel = 1, laststno = 0;
844
845 addr = M[addr & mem_mask] & mem_mask; /* get address of first statement */
846 sofst = M[sofst & mem_mask] & mem_mask ; /* get address of symbol table */
847
848 for (;;) {
849 cword = M[addr];
850 nwords = (cword >> 2) & 0x01FF;
851 stype = (cword >> 1) & 0x7C00;
852 has_stnum = (cword & 1);
853
854 if (has_stnum) {
855 laststno++;
856 strel = 0;
857 }
858
859 printf("/%04x [%4d +%3d] %3d - %04x", addr, laststno, strel, nwords, stype);
860
861 if (has_stnum) {
862 addr++;
863 nwords--;
864 symaddr = sofst - (M[addr] & 0x7FF)*3 + 3;
865 printf(" [%04x %04x %04x]", M[symaddr], M[symaddr+1], M[symaddr+2]);
866 }
867
868 if (stype == 0x5000) { /* error record */
869 printf(" (err %d)", M[addr+1]);
870 }
871
872 if (stype == 0x0800)
873 break;
874
875 addr += nwords;
876 putchar('\n');
877
878 if (nwords == 0) {
879 printf("0 words?\n");
880 break;
881 }
882 strel++;
883 }
884
885 printf("\nEnd found at /%04x, EOFS = /%04x\n", addr, M[0x7a25 & mem_mask]);
886 return SCPE_OK;
887 }
888
889 #endif /* TRACE_DMS_IO */