First Commit of my working state
[simh.git] / PDP8 / pdp8_rf.c
CommitLineData
196ba1fc
PH
1/* pdp8_rf.c: RF08 fixed head disk simulator\r
2\r
3 Copyright (c) 1993-2006, Robert M Supnik\r
4\r
5 Permission is hereby granted, free of charge, to any person obtaining a\r
6 copy of this software and associated documentation files (the "Software"),\r
7 to deal in the Software without restriction, including without limitation\r
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
9 and/or sell copies of the Software, and to permit persons to whom the\r
10 Software is furnished to do so, subject to the following conditions:\r
11\r
12 The above copyright notice and this permission notice shall be included in\r
13 all copies or substantial portions of the Software.\r
14\r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21\r
22 Except as contained in this notice, the name of Robert M Supnik shall not be\r
23 used in advertising or otherwise to promote the sale, use or other dealings\r
24 in this Software without prior written authorization from Robert M Supnik.\r
25\r
26 rf RF08 fixed head disk\r
27\r
28 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein)\r
29 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman)\r
30 04-Jan-04 RMS Changed sim_fsize calling sequence\r
31 26-Oct-03 RMS Cleaned up buffer copy code\r
32 26-Jul-03 RMS Fixed bug in set size routine\r
33 14-Mar-03 RMS Fixed variable platter interaction with save/restore\r
34 03-Mar-03 RMS Fixed autosizing\r
35 02-Feb-03 RMS Added variable platter and autosizing support\r
36 04-Oct-02 RMS Added DIB, device number support\r
37 28-Nov-01 RMS Added RL8A support\r
38 25-Apr-01 RMS Added device enable/disable support\r
39 19-Mar-01 RMS Added disk monitor bootstrap, fixed IOT decoding\r
40 15-Feb-01 RMS Fixed 3 cycle data break sequence\r
41 14-Apr-99 RMS Changed t_addr to unsigned\r
42 30-Mar-98 RMS Fixed bug in RF bootstrap\r
43\r
44 The RF08 is a head-per-track disk. It uses the three cycle data break\r
45 facility. To minimize overhead, the entire RF08 is buffered in memory.\r
46\r
47 Two timing parameters are provided:\r
48\r
49 rf_time Interword timing, must be non-zero\r
50 rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise,\r
51 DMA occurs in a burst\r
52*/\r
53\r
54#include "pdp8_defs.h"\r
55#include <math.h>\r
56\r
57#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */\r
58#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */\r
59#define UNIT_M_PLAT 03\r
60#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1)\r
61#define UNIT_AUTO (1 << UNIT_V_AUTO)\r
62#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT)\r
63\r
64/* Constants */\r
65\r
66#define RF_NUMWD 2048 /* words/track */\r
67#define RF_NUMTR 128 /* tracks/disk */\r
68#define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */\r
69#define RF_NUMDK 4 /* disks/controller */\r
70#define RF_WC 07750 /* word count */\r
71#define RF_MA 07751 /* mem address */\r
72#define RF_WMASK (RF_NUMWD - 1) /* word mask */\r
73\r
74/* Parameters in the unit descriptor */\r
75\r
76#define FUNC u4 /* function */\r
77#define RF_READ 2 /* read */\r
78#define RF_WRITE 4 /* write */\r
79\r
80/* Status register */\r
81\r
82#define RFS_PCA 04000 /* photocell status */\r
83#define RFS_DRE 02000 /* data req enable */\r
84#define RFS_WLS 01000 /* write lock status */\r
85#define RFS_EIE 00400 /* error int enable */\r
86#define RFS_PIE 00200 /* photocell int enb */\r
87#define RFS_CIE 00100 /* done int enable */\r
88#define RFS_MEX 00070 /* memory extension */\r
89#define RFS_DRL 00004 /* data late error */\r
90#define RFS_NXD 00002 /* non-existent disk */\r
91#define RFS_PER 00001 /* parity error */\r
92#define RFS_ERR (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER)\r
93#define RFS_V_MEX 3\r
94\r
95#define GET_MEX(x) (((x) & RFS_MEX) << (12 - RFS_V_MEX))\r
96#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \\r
97 ((double) RF_NUMWD)))\r
98#define UPDATE_PCELL if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \\r
99 else rf_sta = rf_sta & ~RFS_PCA\r
100#define RF_INT_UPDATE if ((rf_done && (rf_sta & RFS_CIE)) || \\r
101 ((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \\r
102 ((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \\r
103 int_req = int_req | INT_RF; \\r
104 else int_req = int_req & ~INT_RF\r
105\r
106extern uint16 M[];\r
107extern int32 int_req, stop_inst;\r
108extern UNIT cpu_unit;\r
109\r
110int32 rf_sta = 0; /* status register */\r
111int32 rf_da = 0; /* disk address */\r
112int32 rf_done = 0; /* done flag */\r
113int32 rf_wlk = 0; /* write lock */\r
114int32 rf_time = 10; /* inter-word time */\r
115int32 rf_burst = 1; /* burst mode flag */\r
116int32 rf_stopioe = 1; /* stop on error */\r
117\r
118DEVICE rf_dev;\r
119int32 rf60 (int32 IR, int32 AC);\r
120int32 rf61 (int32 IR, int32 AC);\r
121int32 rf62 (int32 IR, int32 AC);\r
122int32 rf64 (int32 IR, int32 AC);\r
123t_stat rf_svc (UNIT *uptr);\r
124t_stat pcell_svc (UNIT *uptr);\r
125t_stat rf_reset (DEVICE *dptr);\r
126t_stat rf_boot (int32 unitno, DEVICE *dptr);\r
127t_stat rf_attach (UNIT *uptr, char *cptr);\r
128t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r
129\r
130/* RF08 data structures\r
131\r
132 rf_dev RF device descriptor\r
133 rf_unit RF unit descriptor\r
134 pcell_unit photocell timing unit (orphan)\r
135 rf_reg RF register list\r
136*/\r
137\r
138DIB rf_dib = { DEV_RF, 5, { &rf60, &rf61, &rf62, NULL, &rf64 } };\r
139\r
140UNIT rf_unit = {\r
141 UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+\r
142 UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE)\r
143 };\r
144\r
145UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) };\r
146\r
147REG rf_reg[] = {\r
148 { ORDATA (STA, rf_sta, 12) },\r
149 { ORDATA (DA, rf_da, 20) },\r
150 { ORDATA (WC, M[RF_WC], 12), REG_FIT },\r
151 { ORDATA (MA, M[RF_MA], 12), REG_FIT },\r
152 { FLDATA (DONE, rf_done, 0) },\r
153 { FLDATA (INT, int_req, INT_V_RF) },\r
154 { ORDATA (WLK, rf_wlk, 32) },\r
155 { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT },\r
156 { FLDATA (BURST, rf_burst, 0) },\r
157 { FLDATA (STOP_IOE, rf_stopioe, 0) },\r
158 { DRDATA (CAPAC, rf_unit.capac, 21), REG_HRO },\r
159 { ORDATA (DEVNUM, rf_dib.dev, 6), REG_HRO },\r
160 { NULL }\r
161 };\r
162\r
163MTAB rf_mod[] = {\r
164 { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size },\r
165 { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size },\r
166 { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size },\r
167 { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size },\r
168 { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL },\r
169 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",\r
170 &set_dev, &show_dev, NULL },\r
171 { 0 }\r
172 };\r
173\r
174DEVICE rf_dev = {\r
175 "RF", &rf_unit, rf_reg, rf_mod,\r
176 1, 8, 20, 1, 8, 12,\r
177 NULL, NULL, &rf_reset,\r
178 &rf_boot, &rf_attach, NULL,\r
179 &rf_dib, DEV_DISABLE | DEV_DIS\r
180 };\r
181\r
182/* IOT routines */\r
183\r
184int32 rf60 (int32 IR, int32 AC)\r
185{\r
186int32 t;\r
187int32 pulse = IR & 07;\r
188\r
189UPDATE_PCELL; /* update photocell */\r
190if (pulse & 1) { /* DCMA */\r
191 rf_da = rf_da & ~07777; /* clear DAR<8:19> */\r
192 rf_done = 0; /* clear done */\r
193 rf_sta = rf_sta & ~RFS_ERR; /* clear errors */\r
194 RF_INT_UPDATE; /* update int req */\r
195 }\r
196if (pulse & 6) { /* DMAR, DMAW */\r
197 rf_da = rf_da | AC; /* DAR<8:19> |= AC */\r
198 rf_unit.FUNC = pulse & ~1; /* save function */\r
199 t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */\r
200 if (t < 0) t = t + RF_NUMWD; /* wrap around? */\r
201 sim_activate (&rf_unit, t * rf_time); /* schedule op */\r
202 AC = 0; /* clear AC */\r
203 }\r
204return AC;\r
205}\r
206\r
207int32 rf61 (int32 IR, int32 AC)\r
208{\r
209int32 pulse = IR & 07;\r
210\r
211UPDATE_PCELL; /* update photocell */\r
212switch (pulse) { /* decode IR<9:11> */\r
213\r
214 case 1: /* DCIM */\r
215 rf_sta = rf_sta & 07007; /* clear STA<3:8> */\r
216 int_req = int_req & ~INT_RF; /* clear int req */\r
217 sim_cancel (&pcell_unit); /* cancel photocell */\r
218 return AC;\r
219\r
220 case 2: /* DSAC */\r
221 return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP: 0;\r
222\r
223 case 5: /* DIML */\r
224 rf_sta = (rf_sta & 07007) | (AC & 0770); /* STA<3:8> <- AC */\r
225 if (rf_sta & RFS_PIE) /* photocell int? */\r
226 sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) *\r
227 rf_time);\r
228 else sim_cancel (&pcell_unit);\r
229 RF_INT_UPDATE; /* update int req */\r
230 return 0; /* clear AC */\r
231\r
232 case 6: /* DIMA */\r
233 return rf_sta; /* AC <- STA<0:11> */\r
234 }\r
235\r
236return AC;\r
237}\r
238\r
239int32 rf62 (int32 IR, int32 AC)\r
240{\r
241int32 pulse = IR & 07;\r
242\r
243UPDATE_PCELL; /* update photocell */\r
244if (pulse & 1) { /* DFSE */\r
245 if (rf_sta & RFS_ERR) AC = AC | IOT_SKP;\r
246 }\r
247if (pulse & 2) { /* DFSC */\r
248 if (pulse & 4) AC = AC & ~07777; /* for DMAC */\r
249 else if (rf_done) AC = AC | IOT_SKP;\r
250 }\r
251if (pulse & 4) AC = AC | (rf_da & 07777); /* DMAC */\r
252return AC;\r
253}\r
254\r
255int32 rf64 (int32 IR, int32 AC)\r
256{\r
257int32 pulse = IR & 07;\r
258\r
259UPDATE_PCELL; /* update photocell */\r
260switch (pulse) { /* decode IR<9:11> */\r
261\r
262 case 1: /* DCXA */\r
263 rf_da = rf_da & 07777; /* clear DAR<0:7> */\r
264 break;\r
265\r
266 case 3: /* DXAL */\r
267 rf_da = rf_da & 07777; /* clear DAR<0:7> */\r
268 case 2: /* DXAL w/o clear */\r
269 rf_da = rf_da | ((AC & 0377) << 12); /* DAR<0:7> |= AC */\r
270 AC = 0; /* clear AC */\r
271 break;\r
272\r
273 case 5: /* DXAC */\r
274 AC = 0; /* clear AC */\r
275 case 4: /* DXAC w/o clear */\r
276 AC = AC | ((rf_da >> 12) & 0377); /* AC |= DAR<0:7> */\r
277 break;\r
278\r
279 default:\r
280 AC = (stop_inst << IOT_V_REASON) + AC;\r
281 break;\r
282 } /* end switch */\r
283\r
284if ((uint32) rf_da >= rf_unit.capac) rf_sta = rf_sta | RFS_NXD;\r
285else rf_sta = rf_sta & ~RFS_NXD;\r
286RF_INT_UPDATE;\r
287return AC;\r
288}\r
289\r
290/* Unit service\r
291\r
292 Note that for reads and writes, memory addresses wrap around in the\r
293 current field. This code assumes the entire disk is buffered.\r
294*/\r
295\r
296t_stat rf_svc (UNIT *uptr)\r
297{\r
298int32 pa, t, mex;\r
299int16 *fbuf = uptr->filebuf;\r
300\r
301UPDATE_PCELL; /* update photocell */\r
302if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */\r
303 rf_sta = rf_sta | RFS_NXD;\r
304 rf_done = 1;\r
305 RF_INT_UPDATE; /* update int req */\r
306 return IORETURN (rf_stopioe, SCPE_UNATT);\r
307 }\r
308\r
309mex = GET_MEX (rf_sta);\r
310do {\r
311 if ((uint32) rf_da >= rf_unit.capac) { /* disk overflow? */\r
312 rf_sta = rf_sta | RFS_NXD;\r
313 break;\r
314 }\r
315 M[RF_WC] = (M[RF_WC] + 1) & 07777; /* incr word count */\r
316 M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */\r
317 pa = mex | M[RF_MA]; /* add extension */\r
318 if (uptr->FUNC == RF_READ) { /* read? */\r
319 if (MEM_ADDR_OK (pa)) /* if !nxm */\r
320 M[pa] = fbuf[rf_da]; /* read word */\r
321 }\r
322 else { /* write */\r
323 t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07);\r
324 if ((rf_wlk >> t) & 1) /* write locked? */\r
325 rf_sta = rf_sta | RFS_WLS;\r
326 else { /* not locked */\r
327 fbuf[rf_da] = M[pa]; /* write word */\r
328 if (((uint32) rf_da) >= uptr->hwmark) uptr->hwmark = rf_da + 1;\r
329 }\r
330 }\r
331 rf_da = (rf_da + 1) & 03777777; /* incr disk addr */\r
332 } while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */\r
333\r
334if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */\r
335 sim_activate (&rf_unit, rf_time); /* sched next */\r
336else {\r
337 rf_done = 1; /* done */\r
338 RF_INT_UPDATE; /* update int req */\r
339 }\r
340return SCPE_OK;\r
341}\r
342\r
343/* Photocell unit service */\r
344\r
345t_stat pcell_svc (UNIT *uptr)\r
346{\r
347rf_sta = rf_sta | RFS_PCA; /* set photocell */\r
348if (rf_sta & RFS_PIE) { /* int enable? */\r
349 sim_activate (&pcell_unit, RF_NUMWD * rf_time);\r
350 int_req = int_req | INT_RF;\r
351 }\r
352return SCPE_OK;\r
353}\r
354\r
355/* Reset routine */\r
356\r
357t_stat rf_reset (DEVICE *dptr)\r
358{\r
359rf_sta = rf_da = 0;\r
360rf_done = 1;\r
361int_req = int_req & ~INT_RF; /* clear interrupt */\r
362sim_cancel (&rf_unit);\r
363sim_cancel (&pcell_unit);\r
364return SCPE_OK;\r
365}\r
366\r
367/* Bootstrap routine */\r
368\r
369#define OS8_START 07750\r
370#define OS8_LEN (sizeof (os8_rom) / sizeof (int16))\r
371#define DM4_START 00200\r
372#define DM4_LEN (sizeof (dm4_rom) / sizeof (int16))\r
373\r
374static const uint16 os8_rom[] = {\r
375 07600, /* 7750, CLA CLL ; also word count */\r
376 06603, /* 7751, DMAR ; also address */\r
377 06622, /* 7752, DFSC ; done? */\r
378 05352, /* 7753, JMP .-1 ; no */\r
379 05752 /* 7754, JMP @.-2 ; enter boot */\r
380 };\r
381\r
382static const uint16 dm4_rom[] = {\r
383 00200, 07600, /* 0200, CLA CLL */\r
384 00201, 06603, /* 0201, DMAR ; read */\r
385 00202, 06622, /* 0202, DFSC ; done? */\r
386 00203, 05202, /* 0203, JMP .-1 ; no */\r
387 00204, 05600, /* 0204, JMP @.-4 ; enter boot */\r
388 07750, 07576, /* 7750, 7576 ; word count */\r
389 07751, 07576 /* 7751, 7576 ; address */\r
390 };\r
391\r
392t_stat rf_boot (int32 unitno, DEVICE *dptr)\r
393{\r
394int32 i;\r
395extern int32 sim_switches, saved_PC;\r
396\r
397if (rf_dib.dev != DEV_RF) return STOP_NOTSTD; /* only std devno */\r
398if (sim_switches & SWMASK ('D')) {\r
399 for (i = 0; i < DM4_LEN; i = i + 2)\r
400 M[dm4_rom[i]] = dm4_rom[i + 1];\r
401 saved_PC = DM4_START;\r
402 }\r
403else {\r
404 for (i = 0; i < OS8_LEN; i++)\r
405 M[OS8_START + i] = os8_rom[i];\r
406 saved_PC = OS8_START;\r
407 }\r
408return SCPE_OK;\r
409}\r
410\r
411/* Attach routine */\r
412\r
413t_stat rf_attach (UNIT *uptr, char *cptr)\r
414{\r
415uint32 sz, p;\r
416uint32 ds_bytes = RF_DKSIZE * sizeof (int16);\r
417\r
418if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) {\r
419 p = (sz + ds_bytes - 1) / ds_bytes;\r
420 if (p >= RF_NUMDK) p = RF_NUMDK - 1;\r
421 uptr->flags = (uptr->flags & ~UNIT_PLAT) |\r
422 (p << UNIT_V_PLAT);\r
423 }\r
424uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE;\r
425return attach_unit (uptr, cptr);\r
426}\r
427\r
428/* Change disk size */\r
429\r
430t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r
431{\r
432if (val < 0) return SCPE_IERR;\r
433if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r
434uptr->capac = UNIT_GETP (val) * RF_DKSIZE;\r
435uptr->flags = uptr->flags & ~UNIT_AUTO;\r
436return SCPE_OK;\r
437}\r