1 /* pro_rx.c: RX50 floppy 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 -writing a write-protected disk under DCL returns
24 different error than real PRO
25 -check byte enables?
26 -implement write sector command with error checks
27 */
28
29 #ifdef PRO
30 #include "pdp11_defs.h"
31 #include "sim_defs.h" /* For sim_gtime() */
32 #include <sys/stat.h>
33 #include <unistd.h>
34
35 #define PRO_RX_TRACKS 80 /* tracks per disk */
36 #define PRO_RX_SPT 10 /* sectors per track */
37 #define PRO_RX_BPS 512 /* bytes per sector */
38 #define PRO_RX_SIZE (PRO_RX_TRACKS*PRO_RX_SPT*PRO_RX_BPS)
39
40
41 char *pro_rx_dir[4];
42 char *pro_rx_file[4];
43
44 int pro_rx_closed[4] = {0,0,0,0}; /* initial floppy door status */
45
46 LOCAL FILE *pro_rx_fptr[4];
47
48 LOCAL unsigned char PRO_RX_SECBUF[PRO_RX_BPS];
49
50 LOCAL int pro_rx_csr0_c; /* csr0 for command mode */
51 LOCAL int pro_rx_csr1_c;
52 LOCAL int pro_rx_csr2_c;
53 LOCAL int pro_rx_csr3_c;
54 LOCAL int pro_rx_csr5_c;
55
56 LOCAL int pro_rx_csr0_s; /* csr0 for maint/status */
57 LOCAL int pro_rx_csr1_s;
58 LOCAL int pro_rx_csr2_s;
59 LOCAL int pro_rx_csr3_s;
60 LOCAL int pro_rx_csr4_s;
61
62 LOCAL int pro_rx_secbuf_addr;
63 LOCAL int pro_rx_intb_en; /* enable interrupt B */
64
65 LOCAL int pro_rx_exist[4]; /* existence of 4 floppy drives */
66 LOCAL int pro_rx_ready[4] = {0,0,0,0}; /* diskette present */
67 LOCAL int pro_rx_wprot[4] = {0,0,0,0}; /* write protection status */
68 LOCAL int pro_rx_vchanged[4]; /* volume changed bits */
69
70
71 /* Volume-changed interrupt generator */
72
pro_rx_intb()73 LOCAL void pro_rx_intb ()
74 {
75 /* Check if intb is enabled */
76
77 /* XXX use better int name */
78
79 if (pro_rx_intb_en == 1)
80 pro_int_set(PRO_INT_1B);
81
82 /* Disable generation of future interrupts until status is read */
83
84 pro_rx_intb_en = 0;
85 }
86
87
88 /* Open floppy door */
89
pro_rx_open_door(int disknum)90 void pro_rx_open_door (int disknum)
91 {
92 if (pro_rx_ready[disknum] == 1)
93 {
94 /* XXX */
95
96 printf("Open floppy %d\r\n", disknum);
97
98 /* Close image file */
99
100 fclose(pro_rx_fptr[disknum]);
101
102 pro_rx_ready[disknum] = 0;
103
104 pro_rx_wprot[disknum] = 0;
105
106 pro_rx_vchanged[disknum] = 1;
107
108 /* Generate interrupt, if needed */
109
110 pro_rx_intb();
111 }
112 else
113 printf("Floppy %d already open!\r\n", disknum);
114 }
115
116
117 /* Close floppy door */
118
pro_rx_close_door(int disknum)119 void pro_rx_close_door (int disknum)
120 {
121 struct stat statbuf;
122 char *fname;
123
124
125 if (pro_rx_ready[disknum] == 0)
126 {
127 /* Construct filename */
128
129 fname = malloc(strlen(pro_rx_dir[disknum]) + strlen(pro_rx_file[disknum]) + 1);
130 strcpy(fname, pro_rx_dir[disknum]);
131 strcat(fname, pro_rx_file[disknum]);
132
133 /* Get file status */
134
135 stat(fname, &statbuf);
136
137 /* Check if writeable, and open new image file */
138
139 if ((statbuf.st_mode & S_IWUSR) != 0)
140 pro_rx_fptr[disknum] = fopen(fname, "r+");
141 else
142 {
143 pro_rx_fptr[disknum] = fopen(fname, "r");
144 pro_rx_wprot[disknum] = 1;
145 }
146
147 if (pro_rx_fptr[disknum] != NULL)
148 {
149 /* Check if image is correct size */
150
151 if (statbuf.st_size == PRO_RX_SIZE)
152 {
153 /* XXX */
154
155 printf("Close floppy %d\r\n", disknum);
156
157 pro_rx_ready[disknum] = 1;
158
159 pro_rx_vchanged[disknum] = 1;
160
161 /* Generate interrupt, if needed */
162
163 pro_rx_intb();
164 }
165 else
166 {
167 printf("Floppy %d image filesize %d incorrect! (should be %d)\r\n",
168 disknum, (int)statbuf.st_size, PRO_RX_SIZE);
169 pro_rx_wprot[disknum] = 0;
170 fclose(pro_rx_fptr[disknum]);
171 }
172 }
173 else
174 {
175 printf("Floppy %d close failed!\r\n", disknum);
176 pro_rx_wprot[disknum] = 0;
177 }
178
179 free(fname);
180 }
181 else
182 printf("Floppy %d already closed!\r\n", disknum);
183 }
184
185
186 /* Set maintenance status registers */
187
pro_rx_set_maintstat()188 LOCAL void pro_rx_set_maintstat ()
189 {
190 int disknum;
191
192 disknum = (pro_rx_csr0_c & PRO_RX_DISKNUM) >> 1;
193
194 pro_rx_csr0_s = (pro_rx_csr0_c & (PRO_RX_FUNC | PRO_RX_DISKNUM)) | PRO_RX_DONE;
195
196 pro_rx_csr1_s = 0;
197
198 pro_rx_csr2_s = pro_rx_csr1_c;
199
200 pro_rx_csr3_s = (pro_rx_vchanged[3] << 7)
201 | (pro_rx_vchanged[2] << 6)
202 | (pro_rx_vchanged[1] << 5)
203 | (pro_rx_vchanged[0] << 4)
204 | (pro_rx_wprot[disknum] << 3)
205 | (pro_rx_ready[disknum] << 2)
206 | pro_rx_exist[disknum];
207
208 pro_rx_csr4_s = (pro_rx_exist[3] << 6)
209 | (pro_rx_exist[2] << 4)
210 | (pro_rx_exist[1] << 2)
211 | pro_rx_exist[0];
212 }
213
214
215 /* Set read/write status registers */
216
pro_rx_set_rwstat()217 LOCAL void pro_rx_set_rwstat ()
218 {
219 pro_rx_csr0_s = (pro_rx_csr0_c & (PRO_RX_FUNC | PRO_RX_DISKNUM)) | PRO_RX_DONE;
220
221 pro_rx_csr1_s = 0;
222
223 pro_rx_csr2_s = pro_rx_csr1_c;
224
225 pro_rx_csr3_s = pro_rx_csr2_c;
226
227 pro_rx_csr4_s = 0;
228 }
229
230
231 /* Read sector */
232
pro_rx_readsec()233 LOCAL void pro_rx_readsec ()
234 {
235 int i, disk, track, sector, offset;
236
237 disk = (pro_rx_csr0_c & PRO_RX_DISKNUM) >> 1;
238
239 track = pro_rx_csr1_c;
240
241 sector = pro_rx_csr2_c-1;
242
243 offset = (track*PRO_RX_SPT+sector)*PRO_RX_BPS;
244
245 /* XXX */
246
247 /*
248 printf("READ sector: disk %d track %d sector %d!\r\n", disk, track, sector);
249 */
250
251 /* Perform error checks */
252
253 if ((pro_rx_exist[disk] != 1) || (pro_rx_ready[disk] != 1))
254 pro_rx_csr1_s = 0220; /* unavailable diskette */
255 else if (track >= PRO_RX_TRACKS)
256 pro_rx_csr1_s = 0040; /* unspecified track number */
257 else if ((sector < 0) || (sector >= PRO_RX_SPT))
258 pro_rx_csr1_s = 0270; /* unspecified sector number */
259 else
260 {
261 /* Read sector */
262
263 fseek(pro_rx_fptr[disk], offset, 0);
264
265 for(i=0; i<PRO_RX_BPS; i++)
266 PRO_RX_SECBUF[i] = getc(pro_rx_fptr[disk]);
267 }
268 }
269
270
271 /* Write sector */
272
pro_rx_writesec()273 LOCAL void pro_rx_writesec ()
274 {
275 int i, disk, track, sector, offset;
276
277 disk = (pro_rx_csr0_c & PRO_RX_DISKNUM) >> 1;
278
279 track = pro_rx_csr1_c;
280
281 sector = pro_rx_csr2_c-1;
282
283 offset = (track*PRO_RX_SPT+sector)*PRO_RX_BPS;
284
285 /* XXX */
286
287 /*
288 printf("WRITE sector: disk %d track %d sector %d!\r\n", disk, track, sector);
289 */
290
291 /* Perform error checks */
292
293 if ((pro_rx_exist[disk] != 1) || (pro_rx_ready[disk] != 1))
294 pro_rx_csr1_s = 0220; /* unavailable diskette */
295 else if (pro_rx_wprot[disk] == 1)
296 pro_rx_csr1_s = 0260; /* write protected */
297 else if (track >= PRO_RX_TRACKS)
298 pro_rx_csr1_s = 0040; /* unspecified track number */
299 else if ((sector < 0) || (sector >= PRO_RX_SPT))
300 pro_rx_csr1_s = 0270; /* unspecified sector number */
301 else
302 {
303 /* Write sector */
304
305 fseek(pro_rx_fptr[disk], offset, 0);
306
307 for(i=0; i<PRO_RX_BPS; i++)
308 putc(PRO_RX_SECBUF[i], pro_rx_fptr[disk]);
309
310 fflush(pro_rx_fptr[disk]);
311 }
312 }
313
314
315 /* Start Command */
316
pro_rx_start_command(void)317 LOCAL void pro_rx_start_command(void)
318 {
319 int i;
320
321 switch ((pro_rx_csr0_c & PRO_RX_FUNC) >> 4)
322 {
323 case PRO_RX_CMD_STAT:
324 pro_rx_set_maintstat();
325
326 /* Reset volume change bits */
327
328 for(i=0; i<4; i++)
329 pro_rx_vchanged[i] = 0;
330
331 /* Enable generation of volume changed interrupt */
332
333 pro_rx_intb_en = 1;
334
335 break;
336
337 case PRO_RX_CMD_MAINT:
338 pro_rx_set_maintstat();
339
340 break;
341
342 case PRO_RX_CMD_RESTORE:
343 pro_rx_set_maintstat();
344
345 break;
346
347 case PRO_RX_CMD_INIT:
348 pro_rx_set_maintstat();
349
350 break;
351
352 case PRO_RX_CMD_READ:
353 pro_rx_set_rwstat();
354
355 pro_rx_readsec();
356
357 break;
358
359 case PRO_RX_CMD_EXTEND:
360 switch (pro_rx_csr5_c & PRO_RX_EXTEND)
361 {
362 case PRO_RX_CMD_RETRY:
363 pro_rx_set_rwstat();
364
365 pro_rx_readsec();
366
367 break;
368
369 case PRO_RX_CMD_DELETE:
370 pro_rx_set_rwstat();
371
372 pro_rx_writesec();
373
374 break;
375
376 case PRO_RX_CMD_RFORMAT:
377 /* XXX */
378
379 /* Update csr2, etc. */
380
381 printf("Read format not implemented in RX50!\r\n");
382
383 break;
384
385 case PRO_RX_CMD_SFORMAT:
386 /* XXX */
387
388 /* Read csr2, etc. */
389
390 printf("Set format not implemented in RX50!\r\n");
391
392 break;
393
394 case PRO_RX_CMD_VERSION:
395 pro_rx_csr2_s = PRO_RX_VERSION;
396
397 break;
398
399 case PRO_RX_CMD_COMPARE:
400 /* XXX */
401
402 printf("Read and compare not implemented in RX50!\r\n");
403
404 break;
405
406 default:
407 printf("Warning: unknown extended function command in RX50!\r\n");
408
409 break;
410 }
411
412 break;
413
414 case PRO_RX_CMD_ADDR:
415 pro_rx_set_rwstat();
416
417 /* XXX */
418 printf("%10.0f Warning: read address command not implemented in RX50!\r\n", sim_gtime());
419 break;
420
421 case PRO_RX_CMD_WRITE:
422 pro_rx_set_rwstat();
423
424 pro_rx_writesec();
425
426 break;
427
428 default:
429 break;
430 }
431
432 /* XXX use better int name */
433
434 pro_int_set(PRO_INT_1A);
435 }
436
437
438 /* RX registers */
439
pro_rx_rd(int pa)440 int pro_rx_rd (int pa)
441 {
442 int data;
443
444 switch (pa & 017777776)
445 {
446 case 017774200:
447 data = PRO_ID_RX;
448 break;
449
450 case 017774204:
451 data = pro_rx_csr0_s;
452 break;
453
454 case 017774206:
455 data = pro_rx_csr1_s;
456 break;
457
458 case 017774210:
459 data = pro_rx_csr2_s;
460 break;
461
462 case 017774212:
463 data = pro_rx_csr3_s;
464 break;
465
466 case 017774214:
467 data = pro_rx_csr4_s;
468 break;
469
470 case 017774216:
471 /* XXX correct? */
472
473 data = pro_rx_csr5_c;
474 break;
475
476 case 017774220:
477 /* Empty data buffer */
478
479 data = PRO_RX_SECBUF[pro_rx_secbuf_addr];
480
481 pro_rx_secbuf_addr++;
482
483 if (pro_rx_secbuf_addr == PRO_RX_BPS)
484 pro_rx_secbuf_addr = 0;
485
486 break;
487
488 case 017774222:
489 /* Clear sector buffer address */
490
491 pro_rx_secbuf_addr = 0;
492
493 /* XXX what does the real PRO return? */
494
495 data = 0;
496 break;
497
498 case 017774224:
499 /* Start command */
500
501 pro_rx_start_command();
502
503 data = 0;
504 break;
505
506 default:
507 data = 0;
508 break;
509 }
510
511 return data;
512 }
513
pro_rx_wr(int data,int pa,int access)514 void pro_rx_wr (int data, int pa, int access)
515 {
516 switch (pa & 017777776)
517 {
518 case 017774204:
519 WRITE_WB(pro_rx_csr0_c, PRO_RX_CSR0_C_W, access);
520 break;
521
522 case 017774206:
523 WRITE_WB(pro_rx_csr1_c, PRO_RX_CSR1_C_W, access);
524 break;
525
526 case 017774210:
527 WRITE_WB(pro_rx_csr2_c, PRO_RX_CSR2_C_W, access);
528 break;
529
530 case 017774212:
531 WRITE_WB(pro_rx_csr3_c, PRO_RX_CSR3_C_W, access);
532 break;
533
534 case 017774216:
535 WRITE_WB(pro_rx_csr5_c, PRO_RX_CSR5_C_W, access);
536 break;
537
538 case 017774222:
539 /* Clear sector buffer address */
540
541 pro_rx_secbuf_addr = 0;
542
543 break;
544
545 case 017774224:
546 /* Start command */
547
548 pro_rx_start_command();
549
550 break;
551
552 /* The PRO technical manual specifies 17774226.
553 Venix 1.0/2.0 use this address.
554 However, P/OS 3.2 uses 17774220. */
555
556 case 017774220:
557 case 017774226:
558 /* Fill sector buffer */
559
560 PRO_RX_SECBUF[pro_rx_secbuf_addr] = (data & 0377);
561
562 pro_rx_secbuf_addr++;
563
564 if (pro_rx_secbuf_addr == PRO_RX_BPS)
565 pro_rx_secbuf_addr = 0;
566
567 break;
568
569 default:
570 break;
571 }
572 }
573
pro_rx_reset()574 void pro_rx_reset ()
575 {
576 int i;
577
578
579 /* Clear the 512-byte sector buffer */
580
581 memset(&PRO_RX_SECBUF, 0, sizeof(PRO_RX_SECBUF));
582
583 pro_rx_secbuf_addr = 0;
584
585 pro_rx_csr0_c = 0;
586 pro_rx_csr1_c = 0;
587 pro_rx_csr2_c = 0;
588 pro_rx_csr3_c = 0;
589 pro_rx_csr5_c = 0;
590
591 pro_rx_csr0_s = PRO_RX_DONE;
592 pro_rx_csr1_s = 0;
593 pro_rx_csr2_s = 0;
594 pro_rx_csr3_s = 0;
595 pro_rx_csr4_s = 0;
596
597 /* XXX The following is hardcoded for now */
598
599 pro_rx_exist[0] = 1;
600 pro_rx_exist[1] = 1;
601 pro_rx_exist[2] = 0;
602 pro_rx_exist[3] = 0;
603
604 pro_rx_vchanged[0] = 0;
605 pro_rx_vchanged[1] = 0;
606 pro_rx_vchanged[2] = 0;
607 pro_rx_vchanged[3] = 0;
608
609 pro_rx_intb_en = 0;
610
611 /* Open floppy image files by "closing" door */
612
613 for(i=0; i<4; i++)
614 if (pro_rx_closed[i] == 1)
615 pro_rx_close_door(i);
616 }
617
618
619 /* Exit routine */
620
pro_rx_exit()621 void pro_rx_exit ()
622 {
623 int i;
624
625 /* Close all floppy image files by "opening" door */
626
627 for(i=0; i<4; i++)
628 if (pro_rx_ready[i] == 1)
629 pro_rx_open_door(i);
630 }
631 #endif
632