First Commit of my working state
[simh.git] / PDP18B / pdp18b_rb.c
CommitLineData
196ba1fc
PH
1/* pdp18b_rb.c: RB09 fixed head disk simulator\r
2\r
3 Copyright (c) 2003-2005, 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 rb RB09 fixed head disk\r
27\r
28 14-Jan-04 RMS Revised IO device call interface\r
29 26-Oct-03 RMS Cleaned up buffer copy code\r
30\r
31 The RB09 is a head-per-track disk. It uses the single cycle data break\r
32 facility. To minimize overhead, the entire RB09 is buffered in memory.\r
33\r
34 Two timing parameters are provided:\r
35\r
36 rb_time Interword timing. Must be non-zero.\r
37 rb_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 "pdp18b_defs.h"\r
42#include <math.h>\r
43\r
44/* Constants */\r
45\r
46#define RB_NUMWD 64 /* words/sector */\r
47#define RB_NUMSC 80 /* sectors/track */\r
48#define RB_NUMTR 200 /* tracks/disk */\r
49#define RB_WLKTR 10 /* tracks/wlock switch */\r
50#define RB_SIZE (RB_NUMTR * RB_NUMSC * RB_NUMWD) /* words/drive */\r
51\r
52/* Function/status register */\r
53\r
54#define RBS_ERR 0400000 /* error */\r
55#define RBS_PAR 0200000 /* parity error */\r
56#define RBS_ILA 0100000 /* ill addr error */\r
57#define RBS_TIM 0040000 /* timing transfer */\r
58#define RBS_NRY 0020000 /* not ready error */\r
59#define RBS_DON 0010000 /* done */\r
60#define RBS_IE 0004000 /* int enable */\r
61#define RBS_BSY 0002000 /* busy */\r
62#define RBS_WR 0001000 /* read/write */\r
63#define RBS_XOR (RBS_IE|RBS_BSY|RBS_WR) /* set by XOR */\r
64#define RBS_MBZ 0000777 /* always clear */\r
65#define RBS_EFLGS (RBS_PAR|RBS_ILA|RBS_TIM|RBS_NRY) /* error flags */\r
66\r
67/* BCD disk address */\r
68\r
69#define RBA_V_TR 8\r
70#define RBA_M_TR 0x1FF\r
71#define RBA_V_SC 0\r
72#define RBA_M_SC 0xFF\r
73#define RBA_GETTR(x) (((x) >> RBA_V_TR) & RBA_M_TR)\r
74#define RBA_GETSC(x) (((x) >> RBA_V_SC) & RBA_M_SC)\r
75\r
76#define GET_POS(x) ((int) fmod (sim_gtime () / ((double) (x)), \\r
77 ((double) (RB_NUMSC * RB_NUMWD))))\r
78\r
79extern int32 M[];\r
80extern int32 int_hwre[API_HLVL+1];\r
81extern UNIT cpu_unit;\r
82\r
83int32 rb_sta = 0; /* status register */\r
84int32 rb_da = 0; /* disk address */\r
85int32 rb_ma = 0; /* current addr */\r
86int32 rb_wc = 0; /* word count */\r
87int32 rb_wlk = 0; /* write lock */\r
88int32 rb_time = 10; /* inter-word time */\r
89int32 rb_burst = 1; /* burst mode flag */\r
90int32 rb_stopioe = 1; /* stop on error */\r
91\r
92DEVICE rb_dev;\r
93int32 rb71 (int32 dev, int32 pulse, int32 AC);\r
94t_stat rb_svc (UNIT *uptr);\r
95t_stat rb_reset (DEVICE *dptr);\r
96int32 rb_updsta (int32 new);\r
97int32 rb_make_da (int32 dat);\r
98int32 rb_make_bcd (int32 dat);\r
99int32 rb_set_da (int32 dat, int32 old);\r
100int32 rb_set_bcd (int32 dat);\r
101\r
102/* RB data structures\r
103\r
104 rb_dev RF device descriptor\r
105 rb_unit RF unit descriptor\r
106 rb_reg RF register list\r
107*/\r
108\r
109DIB rb_dib = { DEV_RB, 1, NULL, { &rb71 } };\r
110\r
111UNIT rb_unit = {\r
112 UDATA (&rb_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,\r
113 RB_SIZE)\r
114 };\r
115\r
116REG rb_reg[] = {\r
117 { ORDATA (STA, rb_sta, 18) },\r
118 { ORDATA (DA, rb_da, 20) },\r
119 { ORDATA (WC, rb_wc, 16) },\r
120 { ORDATA (MA, rb_ma, ADDRSIZE) },\r
121 { FLDATA (INT, int_hwre[API_RB], INT_V_RB) },\r
122 { ORDATA (WLK, rb_wlk, RB_NUMTR / RB_WLKTR) },\r
123 { DRDATA (TIME, rb_time, 24), PV_LEFT + REG_NZ },\r
124 { FLDATA (BURST, rb_burst, 0) },\r
125 { FLDATA (STOP_IOE, rb_stopioe, 0) },\r
126 { ORDATA (DEVNO, rb_dib.dev, 6), REG_HRO },\r
127 { NULL }\r
128 };\r
129\r
130MTAB rb_mod[] = {\r
131 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno },\r
132 { 0 }\r
133 };\r
134\r
135DEVICE rb_dev = {\r
136 "RB", &rb_unit, rb_reg, rb_mod,\r
137 1, 8, 21, 1, 8, 18,\r
138 NULL, NULL, &rb_reset,\r
139 NULL, NULL, NULL,\r
140 &rb_dib, DEV_DIS | DEV_DISABLE\r
141 };\r
142\r
143/* IOT routines */\r
144\r
145int32 rb71 (int32 dev, int32 pulse, int32 AC)\r
146{\r
147int32 tow, t, sb = pulse & 060;\r
148\r
149if (pulse & 001) {\r
150 if (sb == 000) rb_sta = rb_sta & /* DBCF */\r
151 ~(RBS_ERR | RBS_EFLGS | RBS_DON);\r
152 if ((sb == 020) && (rb_sta & (RBS_ERR | RBS_DON)))\r
153 AC = AC | IOT_SKP; /* DBSF */\r
154 if (sb == 040) rb_sta = 0; /* DBCS */\r
155 }\r
156if (pulse & 002) {\r
157 if (sb == 000) AC = AC | rb_make_da (rb_da); /* DBRD */\r
158 if (sb == 020) AC = AC | rb_sta; /* DBRS */\r
159 if (sb == 040) rb_ma = AC & AMASK; /* DBLM */\r
160 }\r
161if (pulse & 004) {\r
162 if (sb == 000) rb_da = rb_set_da (AC, rb_da); /* DBLD */\r
163 if (sb == 020) rb_wc = AC & 0177777; /* DBLW */\r
164 if (sb == 040) { /* DBLS */\r
165 rb_sta = (rb_sta & RBS_XOR) ^ (AC & ~RBS_MBZ);\r
166 if (rb_sta & RBS_BSY) { /* busy set? */\r
167 if (!sim_is_active (&rb_unit)) { /* schedule */\r
168 tow = rb_da % (RB_NUMSC * RB_NUMWD);\r
169 t = tow - GET_POS (rb_time);\r
170 if (t < 0) t = t + (RB_NUMSC * RB_NUMWD);\r
171 sim_activate (&rb_unit, t * rb_time);\r
172 }\r
173 }\r
174 else sim_cancel (&rb_unit); /* no, stop */\r
175 }\r
176 }\r
177rb_updsta (0); /* update status */\r
178return AC;\r
179}\r
180\r
181int32 rb_make_da (int32 da)\r
182{\r
183int32 t = da / (RB_NUMSC * RB_NUMWD); /* bin track */\r
184int32 s = (da % (RB_NUMSC * RB_NUMWD)) / RB_NUMWD; /* bin sector */\r
185int32 bcd_t = rb_make_bcd (t); /* bcd track */\r
186int32 bcd_s = rb_make_bcd (s); /* bcd sector */\r
187return (bcd_t << RBA_V_TR) | (bcd_s << RBA_V_SC);\r
188}\r
189\r
190int32 rb_set_da (int32 bcda, int32 old_da)\r
191{\r
192int32 bcd_t = RBA_GETTR (bcda); /* bcd track */\r
193int32 bcd_s = RBA_GETSC (bcda); /* bcd sector */\r
194int32 t = rb_set_bcd (bcd_t); /* bin track */\r
195int32 s = rb_set_bcd (bcd_s); /* bin sector */\r
196\r
197if ((t >= RB_NUMTR) || (t < 0) || /* invalid? */\r
198 (s >= RB_NUMSC) || (s < 0)) {\r
199 rb_updsta (RBS_ILA); /* error */\r
200 return old_da; /* don't change */\r
201 }\r
202else return (((t * RB_NUMSC) + s) * RB_NUMWD); /* new da */\r
203}\r
204\r
205int32 rb_make_bcd (int32 bin)\r
206{\r
207int32 d, i, r;\r
208\r
209for (r = i = 0; bin != 0; bin = bin / 10) { /* while nz */\r
210 d = bin % 10; /* dec digit */\r
211 r = r | (d << i); /* insert bcd */\r
212 i = i + 4;\r
213 }\r
214return r;\r
215}\r
216\r
217int32 rb_set_bcd (int32 bcd)\r
218{\r
219int32 d, i, r;\r
220\r
221for (r = 0, i = 1; bcd != 0; bcd = bcd >> 4) { /* while nz */\r
222 d = bcd & 0xF; /* bcd digit */\r
223 if (d >= 10) return -1; /* invalid? */\r
224 r = r + (d * i); /* insert bin */\r
225 i = i * 10;\r
226 }\r
227return r;\r
228} \r
229\r
230/* Unit service - disk is buffered in memory */\r
231\r
232t_stat rb_svc (UNIT *uptr)\r
233{\r
234int32 t, sw;\r
235int32 *fbuf = uptr->filebuf;\r
236\r
237if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */\r
238 rb_updsta (RBS_NRY | RBS_DON); /* set nxd, done */\r
239 return IORETURN (rb_stopioe, SCPE_UNATT);\r
240 }\r
241\r
242do {\r
243 if (rb_sta & RBS_WR) { /* write? */\r
244 t = rb_da / (RB_NUMSC * RB_NUMWD); /* track */\r
245 sw = t / RB_WLKTR; /* switch */\r
246 if ((rb_wlk >> sw) & 1) { /* write locked? */\r
247 rb_updsta (RBS_ILA | RBS_DON);\r
248 break;\r
249 }\r
250 else { /* not locked */\r
251 fbuf[rb_da] = M[rb_ma]; /* write word */\r
252 if (((t_addr) rb_da) >= uptr->hwmark) uptr->hwmark = rb_da + 1;\r
253 }\r
254 }\r
255 else if (MEM_ADDR_OK (rb_ma)) /* read, valid addr? */\r
256 M[rb_ma] = fbuf[rb_da]; /* read word */\r
257 rb_wc = (rb_wc + 1) & 0177777; /* incr word count */\r
258 rb_ma = (rb_ma + 1) & AMASK; /* incr mem addr */\r
259 rb_da = rb_da + 1; /* incr disk addr */\r
260 if (rb_da > RB_SIZE) rb_da = 0; /* disk wraparound? */\r
261 } while ((rb_wc != 0) && (rb_burst != 0)); /* brk if wc, no brst */\r
262\r
263if ((rb_wc != 0) && ((rb_sta & RBS_ERR) == 0)) /* more to do? */\r
264 sim_activate (&rb_unit, rb_time); /* sched next */\r
265else rb_updsta (RBS_DON); /* set done */\r
266return SCPE_OK;\r
267}\r
268\r
269/* Update status */\r
270\r
271int32 rb_updsta (int32 new)\r
272{\r
273rb_sta = (rb_sta | new) & ~(RBS_ERR | RBS_MBZ); /* clear err, mbz */\r
274if (rb_sta & RBS_EFLGS) rb_sta = rb_sta | RBS_ERR; /* error? */\r
275if (rb_sta & RBS_DON) rb_sta = rb_sta & ~RBS_BSY; /* done? clear busy */\r
276if ((rb_sta & (RBS_ERR | RBS_DON)) && (rb_sta & RBS_IE))\r
277 SET_INT (RB); /* set or clr intr */\r
278else CLR_INT (RB);\r
279return rb_sta;\r
280}\r
281\r
282/* Reset routine */\r
283\r
284t_stat rb_reset (DEVICE *dptr)\r
285{\r
286rb_sta = rb_da = 0;\r
287rb_wc = rb_ma = 0;\r
288rb_updsta (0);\r
289sim_cancel (&rb_unit);\r
290return SCPE_OK;\r
291}\r