1 /* pro_rd.c: RD hard drive controller
2
3 Copyright (c) 1997-2003, Tarik Isani (xhomer@isani.org)
4
5 This file is part of Xhomer.
6
7 Xhomer is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10
11 Xhomer is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Xhomer; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 /* TBD:
23 -check byte enables?
24 -read verify not implemented
25 */
26
27 #ifdef PRO
28 #include "pdp11_defs.h"
29 #include <sys/stat.h>
30 #include <unistd.h>
31
32
33 char *pro_rd_dir;
34 char *pro_rd_file;
35
36 int pro_rd_heads = 0;
37 int pro_rd_cyls = 0;
38 int pro_rd_secs = 0;
39
40 LOCAL FILE *pro_rd_fptr = NULL;
41
42 LOCAL unsigned char PRO_RD_SECBUF[512];
43
44 LOCAL int pro_rd_secbuf_ptr;
45
46 LOCAL int pro_rd_ep;
47 LOCAL int pro_rd_sec;
48 LOCAL int pro_rd_cyl;
49 LOCAL int pro_rd_head;
50 LOCAL int pro_rd_s2c;
51 LOCAL int pro_rd_status;
52
53 /* Known hard drive geometries */
54
55 #define NUMGEOM 12 /* number of defined geometries */
56
57 LOCAL const int pro_rd_geom[NUMGEOM][3] = {{4, 615, 16}, /* RD31 21M */
58 {4, 615, 17},
59 {6, 820, 16}, /* RD32 43M */
60 {6, 820, 17},
61 {4, 153, 16}, /* RD50 5M */
62 {4, 153, 17},
63 {4, 306, 16}, /* RD51 10M */
64 {4, 306, 17},
65 {8, 512, 16}, /* RD52 36M */
66 {8, 512, 17},
67 {8, 1024, 16}, /* RD53 71M */
68 {8, 1024, 17}};
69
70
71 /* Trigger interrupt A */
72
pro_rd_inta()73 LOCAL void pro_rd_inta ()
74 {
75 /* Signal end of operation */
76
77 pro_rd_status = pro_rd_status | PRO_RD_OPENDED;
78
79 /* XXX use better int name */
80
81 pro_int_set(PRO_INT_0A);
82 }
83
84
85 /* Trigger interrupt B */
86
pro_rd_intb()87 LOCAL void pro_rd_intb ()
88 {
89 /* Set DRQ bits */
90
91 pro_rd_status = pro_rd_status | PRO_RD_DRQ;
92 pro_rd_s2c = pro_rd_s2c | PRO_RD_DATAREQ;
93
94 /* XXX use better int name */
95
96 pro_int_set(PRO_INT_0B);
97 }
98
99
100 /* Signal error condition */
101
pro_rd_error()102 LOCAL void pro_rd_error ()
103 {
104 pro_rd_status = pro_rd_status & (~PRO_RD_BUSY);
105 pro_rd_s2c = pro_rd_s2c | PRO_RD_ERROR;
106
107 /* XXX Currently only handles sector not found errors */
108
109 pro_rd_ep = pro_rd_ep | PRO_RD_IDNF;
110
111 /* XXX is this ok? */
112
113 pro_rd_inta();
114 }
115
116
117 /* Event scheduler */
118
pro_rd_sched()119 LOCAL void pro_rd_sched ()
120 {
121 /* A 4-5 instruction delay is needed to pass diagnostics */
122
123 pro_eq_sched(PRO_EVENT_RD, PRO_EQ_RD);
124 }
125
126 /* Event queue handler */
127
pro_rd_eq()128 void pro_rd_eq ()
129 {
130 /* Trigger interrupt */
131
132 pro_rd_intb();
133 }
134
135
136 /* RD registers */
137
pro_rd_rd(int pa)138 int pro_rd_rd (int pa)
139 {
140 int data;
141
142 switch (pa & 017777776)
143 {
144 case 017774000:
145 data = PRO_ID_RD;
146 break;
147
148 case 017774004:
149 data = pro_rd_ep;
150 break;
151
152 case 017774006:
153 data = pro_rd_sec;
154 break;
155
156 case 017774010:
157 data = PRO_RD_SECBUF[pro_rd_secbuf_ptr+1] * 256
158 + PRO_RD_SECBUF[pro_rd_secbuf_ptr];
159
160 if ((pro_rd_status & PRO_RD_DRQ) != 0)
161 {
162 if (pro_rd_secbuf_ptr < 510)
163 {
164 pro_rd_secbuf_ptr += 2;
165
166 /* Trigger interrupt for next transfer */
167
168 pro_rd_intb();
169 }
170 else
171 {
172 /* Reset data request bits */
173
174 pro_rd_status = pro_rd_status & (~PRO_RD_DRQ);
175 pro_rd_s2c = pro_rd_s2c & (~PRO_RD_DATAREQ);
176
177 /* Signal end of operation */
178
179 pro_rd_inta();
180 }
181 }
182
183 break;
184
185 case 017774012:
186 data = pro_rd_cyl;
187 break;
188
189 case 017774014:
190 data = pro_rd_head;
191 break;
192
193 case 017774016:
194 data = pro_rd_s2c;
195
196 /* XXX should OPENDED really be cleared? */
197
198 pro_rd_status = pro_rd_status & (~PRO_RD_OPENDED);
199
200 break;
201
202 case 017774020:
203 data = pro_rd_status;
204 break;
205
206 default:
207 data = 0;
208 break;
209 }
210
211 return data;
212 }
213
pro_rd_wr(int data,int pa,int access)214 void pro_rd_wr (int data, int pa, int access)
215 {
216 int i, head, cyl, sec, offset;
217
218 switch (pa & 017777776)
219 {
220 case 017774004:
221 WRITE_W(pro_rd_ep, PRO_RD_EP_W);
222 break;
223
224 case 017774006:
225 WRITE_W(pro_rd_sec, PRO_RD_SEC_W);
226 break;
227
228 case 017774010:
229 if ((pro_rd_status & PRO_RD_DRQ) != 0)
230 {
231 PRO_RD_SECBUF[pro_rd_secbuf_ptr] = data & 0377;
232 PRO_RD_SECBUF[pro_rd_secbuf_ptr+1] = (data & 0177400) >> 8;
233
234 if (pro_rd_secbuf_ptr < 510)
235 {
236 pro_rd_secbuf_ptr += 2;
237
238 /* Trigger interrupt for next transfer */
239
240 pro_rd_intb();
241 }
242 else
243 {
244 /* Perform write or format command */
245
246 head = pro_rd_head;
247
248 cyl = pro_rd_cyl;
249
250 sec = pro_rd_sec & PRO_RD_SEC;
251
252 switch (pro_rd_s2c & PRO_RD_CMD)
253 {
254 case PRO_RD_CMD_FORMAT:
255 offset = (cyl * pro_rd_heads * pro_rd_secs + head * pro_rd_secs) * 512;
256
257 fseek(pro_rd_fptr, offset, 0);
258
259 /* Use last character of secbuf as padding character */
260
261 for(i=0; i<(512*pro_rd_secs); i++)
262 putc(PRO_RD_SECBUF[511], pro_rd_fptr);
263
264 fflush(pro_rd_fptr);
265
266 break;
267
268 case PRO_RD_CMD_WRITE:
269 offset = (cyl * pro_rd_heads * pro_rd_secs + head * pro_rd_secs + sec) * 512;
270
271 fseek(pro_rd_fptr, offset, 0);
272
273 for(i=0; i<512; i+=2)
274 {
275 putc(PRO_RD_SECBUF[i], pro_rd_fptr);
276 putc(PRO_RD_SECBUF[i+1], pro_rd_fptr);
277 }
278
279 fflush(pro_rd_fptr);
280
281 break;
282
283 default:
284 break;
285 }
286
287 /* Reset data request bits */
288
289 pro_rd_status = pro_rd_status & (~PRO_RD_DRQ);
290 pro_rd_s2c = pro_rd_s2c & (~PRO_RD_DATAREQ);
291
292 /* Signal end of operation */
293
294 pro_rd_inta();
295 }
296 }
297
298 break;
299
300 case 017774012:
301 WRITE_W(pro_rd_cyl, PRO_RD_CYL_W);
302 break;
303
304 case 017774014:
305 WRITE_W(pro_rd_head, PRO_RD_HEAD_W);
306 break;
307
308 case 017774016:
309 WRITE_W(pro_rd_s2c, PRO_RD_S2C_W);
310
311 head = pro_rd_head;
312
313 cyl = pro_rd_cyl;
314
315 sec = pro_rd_sec & PRO_RD_SEC;
316
317 offset = (cyl * pro_rd_heads * pro_rd_secs + head * pro_rd_secs + sec) * 512;
318
319 /* Clear error conditions */
320
321 pro_rd_s2c = pro_rd_s2c & (~PRO_RD_ERROR);
322 pro_rd_ep = pro_rd_ep & (~PRO_RD_ERRORS);
323
324 /* Clear data transfer requests */
325
326 pro_rd_s2c = pro_rd_s2c & (~PRO_RD_DATAREQ);
327 pro_rd_status = pro_rd_status & (~PRO_RD_DRQ);
328
329 /* Clear operation ended bit */
330
331 pro_rd_status = pro_rd_status & (~PRO_RD_OPENDED);
332
333 /* Execute command */
334
335 switch (pro_rd_s2c & PRO_RD_CMD)
336 {
337 case PRO_RD_CMD_RESTORE:
338 /* XXX
339 printf("RD restore command\r\n");
340 */
341
342 /* Set operation ended bit */
343
344 pro_rd_inta();
345
346 break;
347
348 case PRO_RD_CMD_READ:
349 /* XXX
350 printf("RD read head = %d cyl = %d sec = %d\r\n", pro_rd_head, pro_rd_cyl, pro_rd_sec);
351 */
352
353 if ((head >= pro_rd_heads) || (cyl >= pro_rd_cyls) || (sec >= pro_rd_secs))
354 pro_rd_error();
355 else
356 {
357 fseek(pro_rd_fptr, offset, 0);
358
359 for(i=0; i<512; i++)
360 PRO_RD_SECBUF[i] = getc(pro_rd_fptr);
361
362 pro_rd_secbuf_ptr = 0;
363
364 /* Schedule first DRQ */
365
366 pro_rd_sched();
367 }
368 break;
369
370 case PRO_RD_CMD_WRITE:
371 /* XXX
372 printf("RD write head = %d cyl = %d sec = %d\r\n", pro_rd_head, pro_rd_cyl, pro_rd_sec);
373 */
374
375 if ((head >= pro_rd_heads) || (cyl >= pro_rd_cyls) || (sec >= pro_rd_secs))
376 pro_rd_error();
377 else
378 {
379 pro_rd_secbuf_ptr = 0;
380
381 /* Trigger first DRQ interrupt */
382
383 pro_rd_intb();
384 }
385
386 break;
387
388 case PRO_RD_CMD_FORMAT:
389 /* XXX
390 printf("RD format head = %d cyl = %d\r\n", pro_rd_head, pro_rd_cyl);
391 */
392
393 if ((head >= pro_rd_heads) || (cyl >= pro_rd_cyls))
394 pro_rd_error();
395 else
396 {
397 pro_rd_secbuf_ptr = 0;
398
399 /* Trigger first DRQ interrupt */
400
401 pro_rd_intb();
402 }
403 break;
404
405 default:
406 printf("RD illegal command\r\n");
407 break;
408 }
409
410 break;
411
412 case 017774020:
413 /* XXX watch for reset command */
414
415 break;
416
417 default:
418 break;
419 }
420 }
421
pro_rd_reset()422 void pro_rd_reset ()
423 {
424 struct stat statbuf;
425 int i, fsize, gfound;
426 char *fname;
427
428
429 /* Construct filename */
430
431 fname = malloc(strlen(pro_rd_dir) + strlen(pro_rd_file) + 1);
432 strcpy(fname, pro_rd_dir);
433 strcat(fname, pro_rd_file);
434
435 /* Clear 512-byte sector buffer */
436
437 memset(&PRO_RD_SECBUF, 0, sizeof(PRO_RD_SECBUF));
438
439 /* Open disk image file */
440
441 if (pro_rd_fptr) fclose(pro_rd_fptr);
442 pro_rd_fptr = fopen(fname, "r+");
443
444 gfound = 0;
445
446 if (pro_rd_fptr == NULL)
447 printf("Unable to open rd image %s\n", fname);
448 else
449 {
450 /* Get filesize */
451
452 stat(fname, &statbuf);
453
454 fsize = (int)statbuf.st_size;
455
456 /* Check if geometry was forced in config file */
457
458 if ((pro_rd_heads != 0) || (pro_rd_cyls != 0) || (pro_rd_secs != 0))
459 {
460 if (pro_rd_heads*pro_rd_cyls*pro_rd_secs*512 == fsize)
461 gfound = 1;
462 else
463 printf("Geometry %d %d %d does not match rd filesize %d\n",
464 pro_rd_heads, pro_rd_cyls, pro_rd_secs, fsize);
465 }
466 else
467
468 /* Scan geometry table for a match */
469
470 {
471 for(i=0; i<NUMGEOM; i++)
472 if (pro_rd_geom[i][0]*pro_rd_geom[i][1]
473 *pro_rd_geom[i][2]*512 == fsize)
474 {
475 pro_rd_heads = pro_rd_geom[i][0];
476 pro_rd_cyls = pro_rd_geom[i][1];
477 pro_rd_secs = pro_rd_geom[i][2];
478 gfound = 1;
479 break;
480 }
481
482 if (gfound == 0)
483 printf("Unable to determine rd geometry for filesize %d bytes\n", fsize);
484 }
485
486 /* Close image file if geometry does not match filesize */
487
488 if (gfound == 0)
489 fclose(pro_rd_fptr);
490 }
491
492 if (gfound == 0)
493 {
494 pro_rd_heads = 0;
495 pro_rd_cyls = 0;
496 pro_rd_secs = 0;
497 }
498
499 pro_rd_secbuf_ptr = 0;
500
501 pro_rd_ep = 0;
502 pro_rd_sec = 0;
503 pro_rd_cyl = 0;
504 pro_rd_head = 0;
505 pro_rd_s2c = PRO_RD_DRDY | PRO_RD_SEEKC; /* XXX plus SEEKC? */
506 pro_rd_status = PRO_RD_OPENDED;
507
508 free(fname);
509 }
510
511
512 /* Exit routine */
513
pro_rd_exit()514 void pro_rd_exit ()
515 {
516 if (pro_rd_fptr!=NULL)
517 {
518 fclose(pro_rd_fptr);
519 pro_rd_fptr = NULL;
520 }
521 }
522 #endif
523