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 (int32 flag, char *ptr);
47 static t_stat phdebug_cmd (int32 flag, char *ptr);
48 static t_stat fdump_cmd (int32 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   (int32 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 
xio_disk(int32 iocc_addr,int32 func,int32 modify,int drv)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 
diskfail(UNIT * uptr,int dswflag,int unitflag,t_bool do_interrupt)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 
dsk_svc(UNIT * uptr)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 
dsk_reset(DEVICE * dptr)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 
dsk_attach(UNIT * uptr,char * cptr)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 
dsk_detach(UNIT * uptr)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 
dsk_boot(int32 unitno,DEVICE * dptr)574 static t_stat dsk_boot (int32 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 
enable_dms_tracing(int newsetting)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 
saywhere(int addr)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 
phdebug_cmd(int32 flag,char * ptr)647 static t_stat phdebug_cmd (int32 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 
where_cmd(int32 flag,char * ptr)674 static t_stat where_cmd (int32 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 
addseg(int i)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 
delseg(int i)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 
savesector(int addr,int offset,int len,int phid,char * name)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 
tracesector(int iswrite,int nwords,int addr,int sector)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 
fdump_cmd(int32 flags,char * cptr)839 static t_stat fdump_cmd (int32 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 */
890