Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* hp2100_dr.c: HP 2100 12606B/12610B fixed head disk/drum 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 | dr 12606B 2770/2771 fixed head disk\r | |
27 | 12610B 2773/2774/2775 drum\r | |
28 | \r | |
29 | 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified)\r | |
30 | 07-Oct-04 JDB Fixed enable/disable from either device\r | |
31 | Fixed sector return in status word\r | |
32 | Provided protected tracks and "Writing Enabled" status bit\r | |
33 | Fixed DMA last word write, incomplete sector fill value\r | |
34 | Added "parity error" status return on writes for 12606\r | |
35 | Added track origin test for 12606\r | |
36 | Added SCP test for 12606\r | |
37 | Fixed 12610 SFC operation\r | |
38 | Added "Sector Flag" status bit\r | |
39 | Added "Read Inhibit" status bit for 12606\r | |
40 | Fixed current-sector determination\r | |
41 | Added PROTECTED, UNPROTECTED, TRACKPROT modifiers\r | |
42 | 26-Aug-04 RMS Fixed CLC to stop operation (from Dave Bryan)\r | |
43 | 26-Apr-04 RMS Fixed SFS x,C and SFC x,C\r | |
44 | Revised boot rom to use IBL algorithm\r | |
45 | Implemented DMA SRQ (follows FLG)\r | |
46 | 27-Jul-03 RMS Fixed drum sizes\r | |
47 | Fixed variable capacity interaction with SAVE/RESTORE\r | |
48 | 10-Nov-02 RMS Added BOOT command\r | |
49 | \r | |
50 | These head-per-track devices are buffered in memory, to minimize overhead.\r | |
51 | \r | |
52 | The drum data channel does not have a command flip-flop. Its control\r | |
53 | flip-flop is not wired into the interrupt chain; accordingly, the\r | |
54 | simulator uses command rather than control for the data channel. Its\r | |
55 | flag does not respond to SFS, SFC, or STF.\r | |
56 | \r | |
57 | The drum control channel does not have any of the traditional flip-flops.\r | |
58 | \r | |
59 | The 12606 interface implements two diagnostic tests. An SFS CC instruction\r | |
60 | will skip if the disk has passed the track origin (sector 0) since the last\r | |
61 | CLF CC instruction, and an SFC CC instruction will skip if the Sector Clock\r | |
62 | Phase (SCP) flip-flop is clear, indicating that the current sector is\r | |
63 | accessible. The 12610 interface does not support these tests; the SKF signal\r | |
64 | is not driven, so neither SFC CC nor SFS CC will skip.\r | |
65 | \r | |
66 | The interface implements a track protect mechanism via a switch and a set of\r | |
67 | on-card diodes. The switch sets the protected/unprotected status, and the\r | |
68 | particular diodes installed indicate the range of tracks (a power of 2) that\r | |
69 | are read-only in the protected mode.\r | |
70 | \r | |
71 | Somewhat unusually, writing to a protected track completes normally, but the\r | |
72 | data isn't actually written, as the write current is inhibited. There is no\r | |
73 | "failure" status indication. Instead, a program must note the lack of\r | |
74 | "Writing Enabled" status before the write is attempted.\r | |
75 | \r | |
76 | Specifications (2770/2771):\r | |
77 | - 90 sectors per logical track\r | |
78 | - 45 sectors per revolution\r | |
79 | - 64 words per sector\r | |
80 | - 2880 words per revolution\r | |
81 | - 3450 RPM = 17.4 ms/revolution\r | |
82 | - data timing = 6.0 us/word, 375 us/sector\r | |
83 | - inst timing = 4 inst/word, 11520 inst/revolution\r | |
84 | \r | |
85 | Specifications 2773/2774/2775:\r | |
86 | - 32 sectors per logical track\r | |
87 | - 32 sectors per revolution\r | |
88 | - 64 words per sector\r | |
89 | - 2048 words per revolution\r | |
90 | - 3450 RPM = 17.4 ms/revolution\r | |
91 | - data timing = 8.5 us/word, 550 us/sector\r | |
92 | - inst timing = 6 inst/word, 12288 inst/revolution\r | |
93 | \r | |
94 | References:\r | |
95 | - 12606B Disc Memory Interface Kit Operating and Service Manual\r | |
96 | (12606-90012, Mar-1970)\r | |
97 | - 12610B Drum Memory Interface Kit Operating and Service Manual\r | |
98 | (12610-9001, Feb-1970)\r | |
99 | */\r | |
100 | \r | |
101 | #include "hp2100_defs.h"\r | |
102 | #include <math.h>\r | |
103 | \r | |
104 | /* Constants */\r | |
105 | \r | |
106 | #define DR_NUMWD 64 /* words/sector */\r | |
107 | #define DR_FNUMSC 90 /* fhd sec/track */\r | |
108 | #define DR_DNUMSC 32 /* drum sec/track */\r | |
109 | #define DR_NUMSC ((drc_unit.flags & UNIT_DR)? DR_DNUMSC: DR_FNUMSC)\r | |
110 | #define DR_SIZE (512 * DR_DNUMSC * DR_NUMWD) /* initial size */\r | |
111 | #define DR_FTIME 4 /* fhd per-word time */\r | |
112 | #define DR_DTIME 6 /* drum per-word time */\r | |
113 | #define DR_OVRHEAD 5 /* overhead words at track start */\r | |
114 | #define UNIT_V_PROT (UNIT_V_UF + 0) /* track protect */\r | |
115 | #define UNIT_V_SZ (UNIT_V_UF + 1) /* disk vs drum */\r | |
116 | #define UNIT_M_SZ 017 /* size */\r | |
117 | #define UNIT_PROT (1 << UNIT_V_PROT)\r | |
118 | #define UNIT_SZ (UNIT_M_SZ << UNIT_V_SZ)\r | |
119 | #define UNIT_DR (1 << UNIT_V_SZ) /* low order bit */\r | |
120 | #define SZ_180K 000 /* disks */\r | |
121 | #define SZ_360K 002\r | |
122 | #define SZ_720K 004\r | |
123 | #define SZ_1024K 001 /* drums: default size */\r | |
124 | #define SZ_1536K 003\r | |
125 | #define SZ_384K 005\r | |
126 | #define SZ_512K 007\r | |
127 | #define SZ_640K 011\r | |
128 | #define SZ_768K 013\r | |
129 | #define SZ_896K 015\r | |
130 | #define DR_GETSZ(x) (((x) >> UNIT_V_SZ) & UNIT_M_SZ)\r | |
131 | \r | |
132 | /* Command word */\r | |
133 | \r | |
134 | #define CW_WR 0100000 /* write vs read */\r | |
135 | #define CW_V_FTRK 7 /* fhd track */\r | |
136 | #define CW_M_FTRK 0177\r | |
137 | #define CW_V_DTRK 5 /* drum track */\r | |
138 | #define CW_M_DTRK 01777\r | |
139 | #define MAX_TRK (((drc_unit.flags & UNIT_DR)? CW_M_DTRK: CW_M_FTRK) + 1)\r | |
140 | #define CW_GETTRK(x) ((drc_unit.flags & UNIT_DR)? \\r | |
141 | (((x) >> CW_V_DTRK) & CW_M_DTRK): \\r | |
142 | (((x) >> CW_V_FTRK) & CW_M_FTRK))\r | |
143 | #define CW_PUTTRK(x) ((drc_unit.flags & UNIT_DR)? \\r | |
144 | (((x) & CW_M_DTRK) << CW_V_DTRK): \\r | |
145 | (((x) & CW_M_FTRK) << CW_V_FTRK))\r | |
146 | #define CW_V_FSEC 0 /* fhd sector */\r | |
147 | #define CW_M_FSEC 0177\r | |
148 | #define CW_V_DSEC 0 /* drum sector */\r | |
149 | #define CW_M_DSEC 037\r | |
150 | #define CW_GETSEC(x) ((drc_unit.flags & UNIT_DR)? \\r | |
151 | (((x) >> CW_V_DSEC) & CW_M_DSEC): \\r | |
152 | (((x) >> CW_V_FSEC) & CW_M_FSEC))\r | |
153 | #define CW_PUTSEC(x) ((drc_unit.flags & UNIT_DR)? \\r | |
154 | (((x) & CW_M_DSEC) << CW_V_DSEC): \\r | |
155 | (((x) & CW_M_FSEC) << CW_V_FSEC))\r | |
156 | \r | |
157 | /* Status register, ^ = dynamic */\r | |
158 | \r | |
159 | #define DRS_V_NS 8 /* ^next sector */\r | |
160 | #define DRS_M_NS 0177\r | |
161 | #define DRS_SEC 0100000 /* ^sector flag */\r | |
162 | #define DRS_RDY 0000200 /* ^ready */\r | |
163 | #define DRS_RIF 0000100 /* ^read inhibit */\r | |
164 | #define DRS_SAC 0000040 /* sector coincidence */\r | |
165 | #define DRS_ABO 0000010 /* abort */\r | |
166 | #define DRS_WEN 0000004 /* ^write enabled */\r | |
167 | #define DRS_PER 0000002 /* parity error */\r | |
168 | #define DRS_BSY 0000001 /* ^busy */\r | |
169 | \r | |
170 | #define CALC_SCP(x) (((int32) fmod ((x) / (double) dr_time, \\r | |
171 | (double) (DR_NUMWD))) >= (DR_NUMWD - 3))\r | |
172 | \r | |
173 | extern UNIT cpu_unit;\r | |
174 | extern uint16 *M;\r | |
175 | extern uint32 PC;\r | |
176 | extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2];\r | |
177 | \r | |
178 | int32 drc_cw = 0; /* fnc, addr */\r | |
179 | int32 drc_sta = 0; /* status */\r | |
180 | int32 drc_run = 0; /* run flip-flop */\r | |
181 | int32 drd_ibuf = 0; /* input buffer */\r | |
182 | int32 drd_obuf = 0; /* output buffer */\r | |
183 | int32 drd_ptr = 0; /* sector pointer */\r | |
184 | int32 drc_pcount = 1; /* number of prot tracks */\r | |
185 | int32 dr_stopioe = 1; /* stop on error */\r | |
186 | int32 dr_time = DR_DTIME; /* time per word */\r | |
187 | \r | |
188 | static int32 sz_tab[16] = {\r | |
189 | 184320, 1048576, 368640, 1572864, 737280, 393216, 0, 524288,\r | |
190 | 0, 655360, 0, 786432, 0, 917504, 0, 0 };\r | |
191 | \r | |
192 | DEVICE drd_dev, drc_dev;\r | |
193 | int32 drdio (int32 inst, int32 IR, int32 dat);\r | |
194 | int32 drcio (int32 inst, int32 IR, int32 dat);\r | |
195 | t_stat drc_svc (UNIT *uptr);\r | |
196 | t_stat drc_reset (DEVICE *dptr);\r | |
197 | t_stat drc_attach (UNIT *uptr, char *cptr);\r | |
198 | t_stat drc_boot (int32 unitno, DEVICE *dptr);\r | |
199 | int32 dr_incda (int32 trk, int32 sec, int32 ptr);\r | |
200 | int32 dr_seccntr (double simtime);\r | |
201 | t_stat dr_set_prot (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
202 | t_stat dr_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
203 | \r | |
204 | /* DRD data structures\r | |
205 | \r | |
206 | drd_dev device descriptor\r | |
207 | drd_unit unit descriptor\r | |
208 | drd_reg register list\r | |
209 | */\r | |
210 | \r | |
211 | DIB dr_dib[] = {\r | |
212 | { DRD, 0, 0, 0, 0, 0, &drdio },\r | |
213 | { DRC, 0, 0, 0, 0, 0, &drcio }\r | |
214 | };\r | |
215 | \r | |
216 | #define drd_dib dr_dib[0]\r | |
217 | #define drc_dib dr_dib[1]\r | |
218 | \r | |
219 | UNIT drd_unit[] = {\r | |
220 | { UDATA (NULL, 0, 0) },\r | |
221 | { UDATA (NULL, UNIT_DIS, 0) }\r | |
222 | };\r | |
223 | \r | |
224 | #define TMR_ORG 0 /* origin timer */\r | |
225 | #define TMR_INH 1 /* inhibit timer */\r | |
226 | \r | |
227 | REG drd_reg[] = {\r | |
228 | { ORDATA (IBUF, drd_ibuf, 16) },\r | |
229 | { ORDATA (OBUF, drd_obuf, 16) },\r | |
230 | { FLDATA (CMD, drd_dib.cmd, 0) },\r | |
231 | { FLDATA (CTL, drd_dib.ctl, 0) },\r | |
232 | { FLDATA (FLG, drd_dib.flg, 0) },\r | |
233 | { FLDATA (FBF, drd_dib.fbf, 0) },\r | |
234 | { FLDATA (SRQ, drd_dib.srq, 0) },\r | |
235 | { ORDATA (BPTR, drd_ptr, 6) },\r | |
236 | { ORDATA (DEVNO, drd_dib.devno, 6), REG_HRO },\r | |
237 | { NULL }\r | |
238 | };\r | |
239 | \r | |
240 | MTAB drd_mod[] = {\r | |
241 | { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",\r | |
242 | &hp_setdev, &hp_showdev, &drd_dev },\r | |
243 | { 0 }\r | |
244 | };\r | |
245 | \r | |
246 | DEVICE drd_dev = {\r | |
247 | "DRD", drd_unit, drd_reg, drd_mod,\r | |
248 | 2, 0, 0, 0, 0, 0,\r | |
249 | NULL, NULL, &drc_reset,\r | |
250 | NULL, NULL, NULL,\r | |
251 | &drd_dib, DEV_DISABLE\r | |
252 | };\r | |
253 | \r | |
254 | /* DRC data structures\r | |
255 | \r | |
256 | drc_dev device descriptor\r | |
257 | drc_unit unit descriptor\r | |
258 | drc_mod unit modifiers\r | |
259 | drc_reg register list\r | |
260 | */\r | |
261 | \r | |
262 | UNIT drc_unit = {\r | |
263 | UDATA (&drc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+\r | |
264 | UNIT_MUSTBUF+UNIT_DR+UNIT_BINK, DR_SIZE)\r | |
265 | };\r | |
266 | \r | |
267 | REG drc_reg[] = {\r | |
268 | { DRDATA (PCNT, drc_pcount, 10), REG_HIDDEN | PV_LEFT },\r | |
269 | { ORDATA (CW, drc_cw, 16) },\r | |
270 | { ORDATA (STA, drc_sta, 16) },\r | |
271 | { FLDATA (RUN, drc_run, 0) },\r | |
272 | { FLDATA (CMD, drc_dib.cmd, 0) },\r | |
273 | { FLDATA (CTL, drc_dib.ctl, 0) },\r | |
274 | { FLDATA (FLG, drc_dib.flg, 0) },\r | |
275 | { FLDATA (FBF, drc_dib.fbf, 0) },\r | |
276 | { FLDATA (SRQ, drc_dib.srq, 0) },\r | |
277 | { DRDATA (TIME, dr_time, 24), REG_NZ + PV_LEFT },\r | |
278 | { FLDATA (STOP_IOE, dr_stopioe, 0) },\r | |
279 | { ORDATA (DEVNO, drc_dib.devno, 6), REG_HRO },\r | |
280 | { DRDATA (CAPAC, drc_unit.capac, 24), REG_HRO },\r | |
281 | { NULL }\r | |
282 | };\r | |
283 | \r | |
284 | MTAB drc_mod[] = {\r | |
285 | { UNIT_DR, 0, "disk", NULL, NULL },\r | |
286 | { UNIT_DR, UNIT_DR, "drum", NULL, NULL },\r | |
287 | { UNIT_SZ, (SZ_180K << UNIT_V_SZ), NULL, "180K", &dr_set_size },\r | |
288 | { UNIT_SZ, (SZ_360K << UNIT_V_SZ), NULL, "360K", &dr_set_size },\r | |
289 | { UNIT_SZ, (SZ_720K << UNIT_V_SZ), NULL, "720K", &dr_set_size },\r | |
290 | { UNIT_SZ, (SZ_384K << UNIT_V_SZ), NULL, "384K", &dr_set_size },\r | |
291 | { UNIT_SZ, (SZ_512K << UNIT_V_SZ), NULL, "512K", &dr_set_size },\r | |
292 | { UNIT_SZ, (SZ_640K << UNIT_V_SZ), NULL, "640K", &dr_set_size },\r | |
293 | { UNIT_SZ, (SZ_768K << UNIT_V_SZ), NULL, "768K", &dr_set_size },\r | |
294 | { UNIT_SZ, (SZ_896K << UNIT_V_SZ), NULL, "896K", &dr_set_size },\r | |
295 | { UNIT_SZ, (SZ_1024K << UNIT_V_SZ), NULL, "1024K", &dr_set_size },\r | |
296 | { UNIT_SZ, (SZ_1536K << UNIT_V_SZ), NULL, "1536K", &dr_set_size },\r | |
297 | { UNIT_PROT, UNIT_PROT, "protected", "PROTECTED", NULL },\r | |
298 | { UNIT_PROT, 0, "unprotected", "UNPROTECTED", NULL },\r | |
299 | { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "tracks protected", "TRACKPROT",\r | |
300 | &dr_set_prot, NULL, &drc_reg[0] },\r | |
301 | { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO",\r | |
302 | &hp_setdev, &hp_showdev, &drd_dev },\r | |
303 | { 0 }\r | |
304 | };\r | |
305 | \r | |
306 | DEVICE drc_dev = {\r | |
307 | "DRC", &drc_unit, drc_reg, drc_mod,\r | |
308 | 1, 8, 21, 1, 8, 16,\r | |
309 | NULL, NULL, &drc_reset,\r | |
310 | &drc_boot, &drc_attach, NULL,\r | |
311 | &drc_dib, DEV_DISABLE\r | |
312 | };\r | |
313 | \r | |
314 | /* IO instructions */\r | |
315 | \r | |
316 | int32 drdio (int32 inst, int32 IR, int32 dat)\r | |
317 | {\r | |
318 | int32 devd, t;\r | |
319 | \r | |
320 | devd = IR & I_DEVMASK; /* get device no */\r | |
321 | switch (inst) { /* case on opcode */\r | |
322 | \r | |
323 | case ioOTX: /* output */\r | |
324 | drd_obuf = dat;\r | |
325 | break;\r | |
326 | \r | |
327 | case ioMIX: /* merge */\r | |
328 | dat = dat | drd_ibuf;\r | |
329 | break;\r | |
330 | \r | |
331 | case ioLIX: /* load */\r | |
332 | dat = drd_ibuf;\r | |
333 | break;\r | |
334 | \r | |
335 | case ioCRS: /* control reset (action unverif) */\r | |
336 | case ioCTL: /* control clear/set */\r | |
337 | if (IR & I_AB) { /* CLC */\r | |
338 | clrCMD (devd); /* clr "ctl" */\r | |
339 | clrFSR (devd); /* clr flg */\r | |
340 | if (!drc_run) sim_cancel (&drc_unit); /* cancel curr op */\r | |
341 | drc_sta = drc_sta & ~DRS_SAC; /* clear SAC flag */\r | |
342 | }\r | |
343 | else if (!CMD (devd)) { /* STC, not set? */\r | |
344 | setCMD (devd); /* set "ctl" */\r | |
345 | if (drc_cw & CW_WR) { setFSR (devd); } /* prime DMA */\r | |
346 | drc_sta = 0; /* clr status */\r | |
347 | drd_ptr = 0; /* clear sec ptr */\r | |
348 | sim_cancel (&drc_unit); /* cancel curr op */\r | |
349 | t = CW_GETSEC (drc_cw) - dr_seccntr (sim_gtime());\r | |
350 | if (t <= 0) t = t + DR_NUMSC;\r | |
351 | sim_activate (&drc_unit, t * DR_NUMWD * dr_time);\r | |
352 | }\r | |
353 | break;\r | |
354 | \r | |
355 | default:\r | |
356 | break;\r | |
357 | }\r | |
358 | \r | |
359 | if (IR & I_HC) { clrFSR (devd); } /* H/C option */\r | |
360 | return dat;\r | |
361 | }\r | |
362 | \r | |
363 | int32 drcio (int32 inst, int32 IR, int32 dat)\r | |
364 | {\r | |
365 | int32 sec;\r | |
366 | \r | |
367 | switch (inst) { /* case on opcode */\r | |
368 | \r | |
369 | case ioFLG: /* flag clear/set */\r | |
370 | if ((IR & I_HC) && !(drc_unit.flags & UNIT_DR)) { /* CLF disk */\r | |
371 | sec = dr_seccntr (sim_gtime ()); /* current sector */\r | |
372 | sim_cancel (&drd_unit[TMR_ORG]); /* sched origin tmr */\r | |
373 | sim_activate (&drd_unit[TMR_ORG],\r | |
374 | (DR_FNUMSC - sec) * DR_NUMWD * dr_time);\r | |
375 | }\r | |
376 | break;\r | |
377 | \r | |
378 | case ioSFC: /* skip flag clear */\r | |
379 | if (drc_unit.flags & UNIT_DR) break; /* 12610 never skips */\r | |
380 | if (!(CALC_SCP (sim_gtime()))) /* nearing end of sector? */\r | |
381 | PC = (PC + 1) & VAMASK; /* skip if SCP clear */\r | |
382 | break;\r | |
383 | \r | |
384 | case ioSFS: /* skip flag set */\r | |
385 | if (drc_unit.flags & UNIT_DR) break; /* 12610 never skips */\r | |
386 | if (!sim_is_active (&drd_unit[TMR_ORG])) /* passed origin? */\r | |
387 | PC = (PC + 1) & VAMASK; /* skip if origin seen */\r | |
388 | break;\r | |
389 | \r | |
390 | case ioOTX: /* output */\r | |
391 | if (!(drc_unit.flags & UNIT_DR)) { /* disk? */\r | |
392 | sim_cancel (&drd_unit[TMR_INH]); /* schedule inhibit timer */\r | |
393 | sim_activate (&drd_unit[TMR_INH], DR_FTIME * DR_NUMWD);\r | |
394 | }\r | |
395 | drc_cw = dat; /* get control word */\r | |
396 | break;\r | |
397 | \r | |
398 | case ioLIX: /* load */\r | |
399 | dat = 0;\r | |
400 | case ioMIX: /* merge */\r | |
401 | dat = dat | drc_sta; /* static bits */\r | |
402 | if (!(drc_unit.flags & UNIT_PROT) || /* not protected? */\r | |
403 | (CW_GETTRK(drc_cw) >= drc_pcount)) /* or not in range? */\r | |
404 | dat = dat | DRS_WEN; /* set wrt enb status */\r | |
405 | if (drc_unit.flags & UNIT_ATT) { /* attached? */\r | |
406 | dat = dat | (dr_seccntr (sim_gtime()) << DRS_V_NS) | DRS_RDY;\r | |
407 | if (sim_is_active (&drc_unit)) /* op in progress? */\r | |
408 | dat = dat | DRS_BSY;\r | |
409 | if (CALC_SCP (sim_gtime())) /* SCP ff set? */\r | |
410 | dat = dat | DRS_SEC; /* set sector flag */\r | |
411 | if (sim_is_active (&drd_unit[TMR_INH]) && /* inhibit timer on? */\r | |
412 | !(drc_cw & CW_WR))\r | |
413 | dat = dat | DRS_RIF; /* set read inh flag */\r | |
414 | }\r | |
415 | break;\r | |
416 | \r | |
417 | default:\r | |
418 | break;\r | |
419 | }\r | |
420 | \r | |
421 | return dat;\r | |
422 | }\r | |
423 | \r | |
424 | /* Unit service */\r | |
425 | \r | |
426 | t_stat drc_svc (UNIT *uptr)\r | |
427 | {\r | |
428 | int32 devd, trk, sec;\r | |
429 | uint32 da;\r | |
430 | uint16 *bptr = (uint16 *) uptr->filebuf;\r | |
431 | \r | |
432 | if ((uptr->flags & UNIT_ATT) == 0) {\r | |
433 | drc_sta = DRS_ABO;\r | |
434 | return IORETURN (dr_stopioe, SCPE_UNATT);\r | |
435 | }\r | |
436 | \r | |
437 | devd = drd_dib.devno; /* get dch devno */\r | |
438 | trk = CW_GETTRK (drc_cw);\r | |
439 | sec = CW_GETSEC (drc_cw);\r | |
440 | da = ((trk * DR_NUMSC) + sec) * DR_NUMWD;\r | |
441 | drc_sta = drc_sta | DRS_SAC;\r | |
442 | drc_run = 1; /* set run ff */\r | |
443 | \r | |
444 | if (drc_cw & CW_WR) { /* write? */\r | |
445 | if ((da < uptr->capac) && (sec < DR_NUMSC)) {\r | |
446 | bptr[da + drd_ptr] = drd_obuf;\r | |
447 | if (((uint32) (da + drd_ptr)) >= uptr->hwmark)\r | |
448 | uptr->hwmark = da + drd_ptr + 1;\r | |
449 | }\r | |
450 | drd_ptr = dr_incda (trk, sec, drd_ptr); /* inc disk addr */\r | |
451 | if (CMD (devd)) { /* dch active? */\r | |
452 | setFSR (devd); /* set dch flg */\r | |
453 | sim_activate (uptr, dr_time); /* sched next word */\r | |
454 | }\r | |
455 | else { /* done */\r | |
456 | if (drd_ptr) /* need to fill? */\r | |
457 | for ( ; drd_ptr < DR_NUMWD; drd_ptr++)\r | |
458 | bptr[da + drd_ptr] = drd_obuf; /* fill with last word */\r | |
459 | if (!(drc_unit.flags & UNIT_DR)) /* disk? */\r | |
460 | drc_sta = drc_sta | DRS_PER; /* parity bit sets on write */\r | |
461 | drc_run = 0; /* clear run ff */\r | |
462 | }\r | |
463 | } /* end write */\r | |
464 | else { /* read */\r | |
465 | if (CMD (devd)) { /* dch active? */\r | |
466 | if ((da >= uptr->capac) || (sec >= DR_NUMSC)) drd_ibuf = 0;\r | |
467 | else drd_ibuf = bptr[da + drd_ptr];\r | |
468 | drd_ptr = dr_incda (trk, sec, drd_ptr);\r | |
469 | setFSR (devd); /* set dch flg */\r | |
470 | sim_activate (uptr, dr_time); /* sched next word */\r | |
471 | }\r | |
472 | else drc_run = 0; /* clear run ff */\r | |
473 | }\r | |
474 | return SCPE_OK;\r | |
475 | }\r | |
476 | \r | |
477 | /* Increment current disk address */\r | |
478 | \r | |
479 | int32 dr_incda (int32 trk, int32 sec, int32 ptr)\r | |
480 | {\r | |
481 | ptr = ptr + 1; /* inc pointer */\r | |
482 | if (ptr >= DR_NUMWD) { /* end sector? */\r | |
483 | ptr = 0; /* new sector */\r | |
484 | sec = sec + 1; /* adv sector */\r | |
485 | if (sec >= DR_NUMSC) { /* end track? */\r | |
486 | sec = 0; /* new track */\r | |
487 | trk = trk + 1; /* adv track */\r | |
488 | if (trk >= MAX_TRK) trk = 0; /* wraps at max */\r | |
489 | }\r | |
490 | drc_cw = (drc_cw & CW_WR) | CW_PUTTRK (trk) | CW_PUTSEC (sec);\r | |
491 | }\r | |
492 | return ptr;\r | |
493 | }\r | |
494 | \r | |
495 | /* Read the sector counter\r | |
496 | \r | |
497 | The hardware sector counter contains the number of the next sector that will\r | |
498 | pass under the heads (so it is one ahead of the current sector). For the\r | |
499 | duration of the last sector of the track, the sector counter contains 90 for\r | |
500 | the 12606 and 0 for the 12610. The sector counter resets to 0 at track\r | |
501 | origin and increments at the start of the first sector. Therefore, the\r | |
502 | counter value ranges from 0-90 for the 12606 and 0-31 for the 12610. The 0\r | |
503 | state is quite short in the 12606 and long in the 12610, relative to the\r | |
504 | other sector counter states.\r | |
505 | \r | |
506 | The simulated sector counter is calculated from the simulation time, based on\r | |
507 | the time per word and the number of words per track.\r | |
508 | */\r | |
509 | \r | |
510 | int32 dr_seccntr (double simtime)\r | |
511 | {\r | |
512 | int32 curword;\r | |
513 | \r | |
514 | curword = (int32) fmod (simtime / (double) dr_time,\r | |
515 | (double) (DR_NUMWD * DR_NUMSC + DR_OVRHEAD));\r | |
516 | if (curword <= DR_OVRHEAD) return 0;\r | |
517 | else return ((curword - DR_OVRHEAD) / DR_NUMWD +\r | |
518 | ((drc_unit.flags & UNIT_DR)? 0: 1));\r | |
519 | }\r | |
520 | \r | |
521 | /* Reset routine */\r | |
522 | \r | |
523 | t_stat drc_reset (DEVICE *dptr)\r | |
524 | {\r | |
525 | hp_enbdis_pair (dptr, /* make pair cons */\r | |
526 | (dptr == &drd_dev)? &drc_dev: &drd_dev);\r | |
527 | drc_sta = drc_cw = drd_ptr = 0;\r | |
528 | drc_dib.cmd = drd_dib.cmd = 0; /* clear cmd */\r | |
529 | drc_dib.ctl = drd_dib.ctl = 0; /* clear ctl */\r | |
530 | drc_dib.fbf = drd_dib.fbf = 0; /* clear fbf */\r | |
531 | drc_dib.flg = drd_dib.flg = 0; /* clear flg */\r | |
532 | drc_dib.srq = drd_dib.srq = 0; /* srq follows flg */\r | |
533 | sim_cancel (&drc_unit);\r | |
534 | sim_cancel (&drd_unit[TMR_ORG]);\r | |
535 | sim_cancel (&drd_unit[TMR_INH]);\r | |
536 | return SCPE_OK;\r | |
537 | }\r | |
538 | \r | |
539 | /* Attach routine */\r | |
540 | \r | |
541 | t_stat drc_attach (UNIT *uptr, char *cptr)\r | |
542 | {\r | |
543 | int32 sz = sz_tab[DR_GETSZ (uptr->flags)];\r | |
544 | \r | |
545 | if (sz == 0) return SCPE_IERR;\r | |
546 | uptr->capac = sz;\r | |
547 | return attach_unit (uptr, cptr);\r | |
548 | }\r | |
549 | \r | |
550 | /* Set protected track count */\r | |
551 | \r | |
552 | t_stat dr_set_prot (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
553 | {\r | |
554 | int32 count;\r | |
555 | t_stat status;\r | |
556 | \r | |
557 | if (cptr == NULL) return SCPE_ARG;\r | |
558 | count = (int32) get_uint (cptr, 10, 768, &status);\r | |
559 | if (status != SCPE_OK) return status;\r | |
560 | else switch (count) {\r | |
561 | case 1:\r | |
562 | case 2:\r | |
563 | case 4:\r | |
564 | case 8:\r | |
565 | case 16:\r | |
566 | case 32:\r | |
567 | case 64:\r | |
568 | case 128:\r | |
569 | drc_pcount = count;\r | |
570 | break;\r | |
571 | case 256:\r | |
572 | case 512:\r | |
573 | case 768:\r | |
574 | if (drc_unit.flags & UNIT_DR) drc_pcount = count;\r | |
575 | else return SCPE_ARG;\r | |
576 | break;\r | |
577 | default:\r | |
578 | return SCPE_ARG;\r | |
579 | }\r | |
580 | return SCPE_OK;\r | |
581 | }\r | |
582 | \r | |
583 | /* Set size routine */\r | |
584 | \r | |
585 | t_stat dr_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
586 | {\r | |
587 | int32 sz;\r | |
588 | int32 szindex;\r | |
589 | \r | |
590 | if (val < 0) return SCPE_IERR;\r | |
591 | if ((sz = sz_tab[szindex = DR_GETSZ (val)]) == 0) return SCPE_IERR;\r | |
592 | if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r | |
593 | uptr->capac = sz;\r | |
594 | if (szindex & UNIT_DR) dr_time = DR_DTIME; /* drum */\r | |
595 | else {\r | |
596 | dr_time = DR_FTIME; /* disk */\r | |
597 | if (drc_pcount > 128) drc_pcount = 128; /* max prot track count */\r | |
598 | }\r | |
599 | return SCPE_OK;\r | |
600 | }\r | |
601 | \r | |
602 | /* Fixed head disk/drum bootstrap routine (disc subset of disc/paper tape loader) */\r | |
603 | \r | |
604 | #define BOOT_BASE 056\r | |
605 | #define BOOT_START 060\r | |
606 | \r | |
607 | static const uint16 dr_rom[IBL_LNT - BOOT_BASE] = {\r | |
608 | 0020010, /*DMA 20000+DC */\r | |
609 | 0000000, /* 0 */\r | |
610 | 0107700, /* CLC 0,C */\r | |
611 | 0063756, /* LDA DMA ; DMA ctrl */\r | |
612 | 0102606, /* OTA 6 */\r | |
613 | 0002700, /* CLA,CCE */\r | |
614 | 0102611, /* OTA CC ; trk = sec = 0 */\r | |
615 | 0001500, /* ERA ; A = 100000 */\r | |
616 | 0102602, /* OTA 2 ; DMA in, addr */\r | |
617 | 0063777, /* LDA M64 */\r | |
618 | 0102702, /* STC 2 */\r | |
619 | 0102602, /* OTA 2 ; DMA wc = -64 */\r | |
620 | 0103706, /* STC 6,C ; start DMA */\r | |
621 | 0067776, /* LDB JSF ; get JMP . */\r | |
622 | 0074077, /* STB 77 ; in base page */\r | |
623 | 0102710, /* STC DC ; start disc */\r | |
624 | 0024077, /*JSF JMP 77 ; go wait */\r | |
625 | 0177700 /*M64 -100 */\r | |
626 | };\r | |
627 | \r | |
628 | t_stat drc_boot (int32 unitno, DEVICE *dptr)\r | |
629 | {\r | |
630 | int32 i, dev, ad;\r | |
631 | uint16 wd;\r | |
632 | \r | |
633 | if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */\r | |
634 | dev = drd_dib.devno; /* get data chan dev */\r | |
635 | ad = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */\r | |
636 | for (i = BOOT_BASE; i < IBL_LNT; i++) { /* copy bootstrap */\r | |
637 | wd = dr_rom[i - BOOT_BASE]; /* get word */\r | |
638 | if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */\r | |
639 | ((wd & I_DEVMASK) >= 010) && /* dev >= 10? */\r | |
640 | (I_GETIOOP (wd) != ioHLT)) /* not a HALT? */\r | |
641 | M[ad + i] = (wd + (dev - 010)) & DMASK;\r | |
642 | else M[ad + i] = wd;\r | |
643 | }\r | |
644 | PC = ad + BOOT_START;\r | |
645 | return SCPE_OK;\r | |
646 | }\r |