First Commit of my working state
[simh.git] / PDP11 / pdp11_rf.c
CommitLineData
196ba1fc
PH
1/* pdp11_rf.c: RF11 fixed head disk simulator\r
2\r
3 Copyright (c) 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 rf RF11 fixed head disk\r
27\r
28 25-Dec-06 RMS Fixed bug in unit mask (found by John Dundas)\r
29 26-Jun-06 RMS Cloned from RF08 simulator\r
30\r
31 The RF11 is a head-per-track disk. To minimize overhead, the entire RF11\r
32 is buffered in memory.\r
33\r
34 Two timing parameters are provided:\r
35\r
36 rf_time Interword timing, must be non-zero\r
37 rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise,\r
38 DMA occurs in a burst\r
39*/\r
40\r
41#include "pdp11_defs.h"\r
42#include <math.h>\r
43\r
44#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */\r
45#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */\r
46#define UNIT_M_PLAT (RF_NUMDK - 1)\r
47#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1)\r
48#define UNIT_AUTO (1 << UNIT_V_AUTO)\r
49#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT)\r
50\r
51/* Constants */\r
52\r
53#define RF_NUMWD 2048 /* words/track */\r
54#define RF_NUMTR 128 /* tracks/disk */\r
55#define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */\r
56#define RF_NUMDK 8 /* disks/controller */\r
57#define RF_WMASK (RF_NUMWD - 1) /* word mask */\r
58\r
59/* Parameters in the unit descriptor */\r
60\r
61#define FUNC u4 /* function */\r
62\r
63/* Status register */\r
64\r
65#define RFCS_ERR (CSR_ERR) /* error */\r
66#define RFCS_FRZ 0040000 /* error freeze */\r
67#define RFCS_WCHK 0020000 /* write check */\r
68#define RFCS_DPAR 0010000 /* data parity (ni) */\r
69#define RFCS_NED 0004000 /* nx disk */\r
70#define RFCS_WLK 0002000 /* write lock */\r
71#define RFCS_MXFR 0001000 /* missed xfer (ni) */\r
72#define RFCS_CLR 0000400 /* clear */\r
73#define RFCS_DONE (CSR_DONE)\r
74#define RFCS_IE (CSR_IE)\r
75#define RFCS_M_MEX 0000003 /* memory extension */\r
76#define RFCS_V_MEX 4\r
77#define RFCS_MEX (RFCS_M_MEX << RFCS_V_MEX)\r
78#define RFCS_MAINT 0000010 /* maint */\r
79#define RFCS_M_FUNC 0000003 /* function */\r
80#define RFNC_NOP 0\r
81#define RFNC_WRITE 1\r
82#define RFNC_READ 2\r
83#define RFNC_WCHK 3\r
84#define RFCS_V_FUNC 1\r
85#define RFCS_FUNC (RFCS_M_FUNC << RFCS_V_FUNC)\r
86#define RFCS_GO 0000001\r
87#define RFCS_ALLERR (RFCS_FRZ|RFCS_WCHK|RFCS_DPAR|RFCS_NED|RFCS_WLK|RFCS_MXFR)\r
88#define RFCS_W (RFCS_IE|RFCS_MEX|RFCS_FUNC)\r
89\r
90/* Current memory address */\r
91\r
92#define RFCMA_RW 0177776\r
93\r
94/* Address extension */\r
95\r
96#define RFDAE_ALLERR 0176000\r
97#define RFDAE_NXM 0002000\r
98#define RFDAE_INH 0000400 /* addr inhibit */\r
99#define RFDAE_RLAT 0000200 /* req late */\r
100#define RFDAE_DAE 0000077 /* extension */\r
101#define RFDAE_R 0176677\r
102#define RFDAE_W 0000677\r
103\r
104#define GET_FUNC(x) (((x) >> RFCS_V_FUNC) & RFCS_M_FUNC)\r
105#define GET_MEX(x) (((x) & RFCS_MEX) << (16 - RFCS_V_MEX))\r
106#define GET_DEX(x) (((x) & RFDAE_DAE) << 16)\r
107#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \\r
108 ((double) RF_NUMWD)))\r
109\r
110extern uint16 *M;\r
111extern int32 int_req[IPL_HLVL];\r
112extern FILE *sim_deb;\r
113\r
114uint32 rf_cs = 0; /* status register */\r
115uint32 rf_cma = 0;\r
116uint32 rf_wc = 0;\r
117uint32 rf_da = 0; /* disk address */\r
118uint32 rf_dae = 0;\r
119uint32 rf_dbr = 0;\r
120uint32 rf_maint = 0;\r
121uint32 rf_wlk = 0; /* write lock */\r
122uint32 rf_time = 10; /* inter-word time */\r
123uint32 rf_burst = 1; /* burst mode flag */\r
124uint32 rf_stopioe = 1; /* stop on error */\r
125\r
126DEVICE rf_dev;\r
127t_stat rf_rd (int32 *data, int32 PA, int32 access);\r
128t_stat rf_wr (int32 data, int32 PA, int32 access);\r
129int32 rf_inta (void);\r
130t_stat rf_svc (UNIT *uptr);\r
131t_stat rf_reset (DEVICE *dptr);\r
132t_stat rf_boot (int32 unitno, DEVICE *dptr);\r
133t_stat rf_attach (UNIT *uptr, char *cptr);\r
134t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r
135uint32 update_rfcs (uint32 newcs, uint32 newdae);\r
136\r
137/* RF11 data structures\r
138\r
139 rf_dev RF device descriptor\r
140 rf_unit RF unit descriptor\r
141 rf_reg RF register list\r
142*/\r
143\r
144DIB rf_dib = {\r
145 IOBA_RF, IOLN_RF, &rf_rd, &rf_wr,\r
146 1, IVCL (RF), VEC_RF, NULL\r
147 };\r
148\r
149\r
150UNIT rf_unit = {\r
151 UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+\r
152 UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE)\r
153 };\r
154\r
155REG rf_reg[] = {\r
156 { ORDATA (RFCS, rf_cs, 16) },\r
157 { ORDATA (RFWC, rf_wc, 16) },\r
158 { ORDATA (RFCMA, rf_cma, 16) },\r
159 { ORDATA (RFDA, rf_da, 16) },\r
160 { ORDATA (RFDAE, rf_dae, 16) },\r
161 { ORDATA (RFDBR, rf_dbr, 16) },\r
162 { ORDATA (RFMR, rf_maint, 16) },\r
163 { ORDATA (RFWLK, rf_wlk, 32) },\r
164 { FLDATA (INT, IREQ (RF), INT_V_RF) },\r
165 { FLDATA (ERR, rf_cs, CSR_V_ERR) },\r
166 { FLDATA (DONE, rf_cs, CSR_V_DONE) },\r
167 { FLDATA (IE, rf_cs, CSR_V_IE) },\r
168 { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT },\r
169 { FLDATA (BURST, rf_burst, 0) },\r
170 { FLDATA (STOP_IOE, rf_stopioe, 0) },\r
171 { ORDATA (DEVADDR, rf_dib.ba, 32), REG_HRO },\r
172 { ORDATA (DEVVEC, rf_dib.vec, 16), REG_HRO },\r
173 { NULL }\r
174 };\r
175\r
176MTAB rf_mod[] = {\r
177 { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size },\r
178 { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size },\r
179 { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size },\r
180 { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size },\r
181 { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &rf_set_size },\r
182 { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &rf_set_size },\r
183 { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &rf_set_size },\r
184 { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &rf_set_size },\r
185 { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL },\r
186 { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS",\r
187 &set_addr, &show_addr, NULL },\r
188 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR",\r
189 &set_vec, &show_vec, NULL },\r
190 { 0 }\r
191 };\r
192\r
193DEVICE rf_dev = {\r
194 "RF", &rf_unit, rf_reg, rf_mod,\r
195 1, 8, 21, 1, 8, 16,\r
196 NULL, NULL, &rf_reset,\r
197 &rf_boot, &rf_attach, NULL,\r
198 &rf_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG\r
199 };\r
200\r
201/* I/O dispatch routine, I/O addresses 17777460 - 17777476 */\r
202\r
203t_stat rf_rd (int32 *data, int32 PA, int32 access)\r
204{\r
205switch ((PA >> 1) & 07) { /* decode PA<3:1> */\r
206\r
207 case 0: /* RFCS */\r
208 *data = update_rfcs (0, 0); /* update RFCS */\r
209 break;\r
210\r
211 case 1: /* RFWC */\r
212 *data = rf_wc;\r
213 break;\r
214\r
215 case 2: /* RFCMA */\r
216 *data = rf_cma & RFCMA_RW;\r
217 break;\r
218\r
219 case 3: /* RFDA */\r
220 *data = rf_da;\r
221 break;\r
222\r
223 case 4: /* RFDAE */\r
224 *data = rf_dae & RFDAE_R;\r
225 break;\r
226\r
227 case 5: /* RFDBR */\r
228 *data = rf_dbr;\r
229 break;\r
230\r
231 case 6: /* RFMR */\r
232 *data = rf_maint;\r
233 break;\r
234\r
235 case 7: /* RFADS */\r
236 *data = GET_POS (rf_time);\r
237 break;\r
238 } /* end switch */\r
239return SCPE_OK;\r
240}\r
241\r
242t_stat rf_wr (int32 data, int32 PA, int32 access)\r
243{\r
244int32 t, fnc;\r
245\r
246switch ((PA >> 1) & 07) { /* decode PA<3:1> */\r
247\r
248 case 0: /* RFCS */\r
249 if (access == WRITEB) data = (PA & 1)?\r
250 (rf_cs & 0377) | (data << 8): (rf_cs & ~0377) | data;\r
251 if (data & RFCS_CLR) rf_reset (&rf_dev); /* clear? */\r
252 if ((data & RFCS_IE) == 0) /* int disable? */\r
253 CLR_INT (RF); /* clr int request */\r
254 else if ((rf_cs & (RFCS_DONE + RFCS_IE)) == RFCS_DONE)\r
255 SET_INT (RF); /* set int request */\r
256 rf_cs = (rf_cs & ~RFCS_W) | (data & RFCS_W); /* merge */\r
257 if ((rf_cs & RFCS_DONE) && (data & RFCS_GO) && /* new function? */\r
258 ((fnc = GET_FUNC (rf_cs)) != RFNC_NOP)) {\r
259 rf_unit.FUNC = fnc; /* save function */\r
260 t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */\r
261 if (t < 0) t = t + RF_NUMWD; /* wrap around? */\r
262 sim_activate (&rf_unit, t * rf_time); /* schedule op */\r
263 rf_cs &= ~(RFCS_WCHK|RFCS_DPAR|RFCS_NED|RFCS_WLK|RFCS_MXFR|RFCS_DONE);\r
264 CLR_INT (RF);\r
265 if (DEBUG_PRS (rf_dev))\r
266 fprintf (sim_deb, ">>RF start: cs = %o, da = %o, ma = %o\n",\r
267 update_rfcs (0, 0), GET_DEX (rf_dae) | rf_da, GET_MEX (rf_cs) | rf_cma);\r
268 }\r
269 break;\r
270\r
271 case 1: /* RFWC */\r
272 if (access == WRITEB) data = (PA & 1)?\r
273 (rf_wc & 0377) | (data << 8): (rf_wc & ~0377) | data;\r
274 rf_wc = data;\r
275 break;\r
276\r
277 case 2: /* RFCMA */\r
278 if (access == WRITEB) data = (PA & 1)?\r
279 (rf_cma & 0377) | (data << 8): (rf_cma & ~0377) | data;\r
280 rf_cma = data & RFCMA_RW;\r
281 break;\r
282\r
283 case 3: /* RFDA */\r
284 if (access == WRITEB) data = (PA & 1)?\r
285 (rf_da & 0377) | (data << 8): (rf_da & ~0377) | data;\r
286 rf_da = data;\r
287 break;\r
288\r
289 case 4: /* RFDAE */\r
290 if (access == WRITEB) data = (PA & 1)?\r
291 (rf_dae & 0377) | (data << 8): (rf_dae & ~0377) | data;\r
292 rf_dae = (rf_dae & ~RFDAE_W) | (data & RFDAE_W);\r
293 break;\r
294\r
295 case 5: /* RFDBR */\r
296 rf_dbr = data;\r
297 break;\r
298\r
299 case 6: /* RFMR */\r
300 rf_maint = data;\r
301 break;\r
302\r
303 case 7: /* RFADS */\r
304 break; /* read only */\r
305 } /* end switch */\r
306\r
307update_rfcs (0, 0);\r
308return SCPE_OK;\r
309}\r
310\r
311/* Unit service\r
312\r
313 Note that for reads and writes, memory addresses wrap around in the\r
314 current field. This code assumes the entire disk is buffered.\r
315*/\r
316\r
317t_stat rf_svc (UNIT *uptr)\r
318{\r
319uint32 ma, da, t;\r
320uint16 dat;\r
321uint16 *fbuf = uptr->filebuf;\r
322\r
323if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */\r
324 update_rfcs (RFCS_NED|RFCS_DONE, 0); /* nx disk */\r
325 return IORETURN (rf_stopioe, SCPE_UNATT);\r
326 }\r
327\r
328ma = GET_MEX (rf_cs) | rf_cma; /* 18b mem addr */\r
329da = GET_DEX (rf_dae) | rf_da; /* 22b disk addr */\r
330do {\r
331 if (da >= rf_unit.capac) { /* disk overflow? */\r
332 update_rfcs (RFCS_NED, 0);\r
333 break;\r
334 }\r
335 if (uptr->FUNC == RFNC_READ) { /* read? */\r
336 dat = fbuf[da]; /* get disk data */\r
337 rf_dbr = dat;\r
338 if (Map_WriteW (ma, 2, &dat)) { /* store mem, nxm? */\r
339 update_rfcs (0, RFDAE_NXM);\r
340 break;\r
341 }\r
342 }\r
343 else if (uptr->FUNC == RFNC_WCHK) { /* write check? */\r
344 rf_dbr = fbuf[da]; /* get disk data */\r
345 if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */\r
346 update_rfcs (0, RFDAE_NXM);\r
347 break;\r
348 }\r
349 if (rf_dbr != dat) { /* miscompare? */\r
350 update_rfcs (RFCS_WCHK, 0);\r
351 break;\r
352 }\r
353 }\r
354 else { /* write */\r
355 t = (da >> 15) & 037;\r
356 if ((rf_wlk >> t) & 1) { /* write locked? */\r
357 update_rfcs (RFCS_WLK, 0);\r
358 break;\r
359 }\r
360 else { /* not locked */\r
361 if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */\r
362 update_rfcs (0, RFDAE_NXM);\r
363 break;\r
364 }\r
365 fbuf[da] = dat; /* write word */\r
366 rf_dbr = dat;\r
367 if (da >= uptr->hwmark) uptr->hwmark = da + 1;\r
368 }\r
369 }\r
370 da = (da + 1) & 017777777; /* incr disk addr */\r
371 if ((rf_dae & RFDAE_INH) == 0) /* inhibit clear? */\r
372 ma = (ma + 2) & UNIMASK; /* incr mem addr */\r
373 rf_wc = (rf_wc + 1) & DMASK; /* incr word count */\r
374 } while ((rf_wc != 0) && (rf_burst != 0)); /* brk if wc, no brst */\r
375\r
376rf_da = da & DMASK; /* split da */\r
377rf_dae = (rf_dae & ~RFDAE_DAE) | ((rf_da >> 16) && RFDAE_DAE);\r
378rf_cma = ma & DMASK; /* split ma */\r
379rf_cs = (rf_cs & ~RFCS_MEX) | ((ma >> (16 - RFCS_V_MEX)) & RFCS_MEX); \r
380if ((rf_wc != 0) && ((rf_cs & RFCS_ERR) == 0)) /* more to do? */\r
381 sim_activate (&rf_unit, rf_time); /* sched next */\r
382else {\r
383 update_rfcs (RFCS_DONE, 0);\r
384 if (DEBUG_PRS (rf_dev))\r
385 fprintf (sim_deb, ">>RF done: cs = %o, dae = %o, da = %o, ma = %o, wc = %o\n",\r
386 rf_cs, rf_dae, rf_da, rf_cma, rf_wc);\r
387 }\r
388return SCPE_OK;\r
389}\r
390\r
391/* Update CS register */\r
392\r
393uint32 update_rfcs (uint32 newcs, uint32 newdae)\r
394{\r
395uint32 oldcs = rf_cs;\r
396uint32 da = GET_DEX (rf_dae) | rf_da;\r
397\r
398rf_dae |= newdae; /* update DAE */\r
399rf_cs |= newcs; /* update CS */\r
400if (da >= rf_unit.capac) /* update CS<ned> */\r
401 rf_cs |= RFCS_NED;\r
402else rf_cs &= ~RFCS_NED;\r
403if (rf_dae & RFDAE_ALLERR) /* update CS<frz> */\r
404 rf_cs |= RFCS_FRZ;\r
405else rf_cs &= ~RFCS_FRZ;\r
406if (rf_cs & RFCS_ALLERR) /* update CS<err> */\r
407 rf_cs |= RFCS_ERR;\r
408else rf_cs &= ~RFCS_ERR;\r
409if ((rf_cs & RFCS_IE) && /* IE and */\r
410 (rf_cs & RFCS_DONE) &&!(oldcs & RFCS_DONE)) /* done 0->1? */\r
411 SET_INT (RF);\r
412return rf_cs;\r
413}\r
414\r
415/* Reset routine */\r
416\r
417t_stat rf_reset (DEVICE *dptr)\r
418{\r
419rf_cs = RFCS_DONE;\r
420rf_da = rf_dae = 0;\r
421rf_dbr = 0;\r
422rf_cma = 0;\r
423rf_wc = 0;\r
424rf_maint = 0;\r
425CLR_INT (RF);\r
426sim_cancel (&rf_unit);\r
427return SCPE_OK;\r
428}\r
429\r
430/* Bootstrap routine */\r
431\r
432/* Device bootstrap */\r
433\r
434#define BOOT_START 02000 /* start */\r
435#define BOOT_ENTRY (BOOT_START + 002) /* entry */\r
436#define BOOT_CSR (BOOT_START + 032) /* CSR */\r
437#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16))\r
438\r
439static const uint16 boot_rom[] = {\r
440 0043113, /* "FD" */\r
441 0012706, BOOT_START, /* MOV #boot_start, SP */\r
442 0012701, 0177472, /* MOV #RFDAE+2, R1 ; csr block */\r
443 0005041, /* CLR -(R1) ; clear dae */\r
444 0005041, /* CLR -(R1), ; clear da */\r
445 0005041, /* CLR -(R1), ; clear cma */\r
446 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */\r
447 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */\r
448 0005002, /* CLR R2 */\r
449 0005003, /* CLR R3 */\r
450 0012704, BOOT_START+020, /* MOV #START+20, R4 */\r
451 0005005, /* CLR R5 */\r
452 0105711, /* TSTB (R1) */\r
453 0100376, /* BPL .-2 */\r
454 0105011, /* CLRB (R1) */\r
455 0005007 /* CLR PC */\r
456 };\r
457\r
458t_stat rf_boot (int32 unitno, DEVICE *dptr)\r
459{\r
460int32 i;\r
461extern int32 saved_PC;\r
462\r
463for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];\r
464M[BOOT_CSR >> 1] = (rf_dib.ba & DMASK) + 012;\r
465saved_PC = BOOT_ENTRY;\r
466return SCPE_OK;\r
467}\r
468\r
469/* Attach routine */\r
470\r
471t_stat rf_attach (UNIT *uptr, char *cptr)\r
472{\r
473uint32 sz, p;\r
474uint32 ds_bytes = RF_DKSIZE * sizeof (int16);\r
475\r
476if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {\r
477 p = (sz + ds_bytes - 1) / ds_bytes;\r
478 if (p >= RF_NUMDK) p = RF_NUMDK - 1;\r
479 uptr->flags = (uptr->flags & ~UNIT_PLAT) |\r
480 (p << UNIT_V_PLAT);\r
481 }\r
482uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE;\r
483return attach_unit (uptr, cptr);\r
484}\r
485\r
486/* Change disk size */\r
487\r
488t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r
489{\r
490if (val < 0) return SCPE_IERR;\r
491if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r
492uptr->capac = UNIT_GETP (val) * RF_DKSIZE;\r
493uptr->flags = uptr->flags & ~UNIT_AUTO;\r
494return SCPE_OK;\r
495}\r