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