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