fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/drivers/block/blkfdc.c *
7 * Created: 2010-08-11 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2010-2025 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <drivers/block/blkpsi.h>
24
25#include <stdlib.h>
26#include <stdint.h>
27#include <string.h>
28
29#include <drivers/psi/psi.h>
30#include <drivers/psi/psi-img.h>
31
32
33static
34int dsk_psi_apply_weak (unsigned char *dst, const unsigned char *msk, unsigned cnt)
35{
36 int r;
37 unsigned i, v;
38
39 r = 0;
40
41 for (i = 0; i < cnt; i++) {
42 if (msk[i] != 0) {
43 v = (rand() >> 4) & msk[i];
44
45 if (v) {
46 dst[i] ^= v;
47 r = 1;
48 }
49 }
50 }
51
52 return (r);
53}
54
55unsigned dsk_psi_read_chs (disk_psi_t *fdc, void *buf, unsigned *cnt,
56 unsigned c, unsigned h, unsigned s, int phy)
57{
58 unsigned ret;
59 psi_sct_t *sct, *alt;
60
61 if (fdc->img == NULL) {
62 *cnt = 0;
63 return (PCE_BLK_PSI_NO_ID);
64 }
65
66 ret = 0;
67
68 sct = psi_img_get_sector (fdc->img, c, h, s, phy);
69
70 if (sct == NULL) {
71 *cnt = 0;
72 return (PCE_BLK_PSI_NO_ID);
73 }
74
75 alt = psi_sct_get_alternate (sct, sct->cur_alt);
76
77 if (alt == NULL) {
78 sct->cur_alt = 0;
79 alt = sct;
80 }
81
82 if (*cnt > alt->n) {
83 *cnt = alt->n;
84 ret |= PCE_BLK_PSI_DATALEN;
85 }
86
87 if (*cnt > 0) {
88 memcpy (buf, alt->data, *cnt);
89
90 if (alt->weak != NULL) {
91 if (dsk_psi_apply_weak (buf, alt->weak, *cnt)) {
92 ret |= PCE_BLK_PSI_CRC_DATA;
93 }
94 }
95 }
96
97 if (alt->flags & PSI_FLAG_NO_DAM) {
98 ret |= PCE_BLK_PSI_NO_DATA;
99 }
100
101 if (alt->flags & PSI_FLAG_CRC_ID) {
102 ret |= PCE_BLK_PSI_CRC_ID;
103 }
104
105 if (alt->flags & PSI_FLAG_CRC_DATA) {
106 ret |= PCE_BLK_PSI_CRC_DATA;
107 }
108
109 if (alt->flags & PSI_FLAG_DEL_DAM) {
110 ret |= PCE_BLK_PSI_DEL_DAM;
111 }
112
113 if (sct->next != NULL) {
114 sct->cur_alt += 1;
115 }
116
117 return (ret);
118}
119
120unsigned dsk_psi_read_tags (disk_psi_t *fdc, void *buf, unsigned cnt,
121 unsigned c, unsigned h, unsigned s, int phy)
122{
123 psi_sct_t *sct, *alt;
124
125 memset (buf, 0, cnt);
126
127 if (fdc->img == NULL) {
128 return (0);
129 }
130
131 sct = psi_img_get_sector (fdc->img, c, h, s, phy);
132
133 if (sct == NULL) {
134 return (0);
135 }
136
137 alt = psi_sct_get_alternate (sct, sct->cur_alt);
138
139 if (alt == NULL) {
140 sct->cur_alt = 0;
141 alt = sct;
142 }
143
144 cnt = psi_sct_get_tags (alt, buf, cnt);
145
146 return (cnt);
147}
148
149unsigned dsk_psi_write_chs (disk_psi_t *fdc, const void *buf, unsigned *cnt,
150 unsigned c, unsigned h, unsigned s, int phy)
151{
152 unsigned ret;
153 psi_sct_t *sct;
154
155 if (fdc->img == NULL) {
156 *cnt = 0;
157 return (PCE_BLK_PSI_NO_ID);
158 }
159
160 if (fdc->dsk.readonly) {
161 return (PCE_BLK_PSI_WPROT);
162 }
163
164 ret = 0;
165
166 sct = psi_img_get_sector (fdc->img, c, h, s, phy);
167
168 if (sct == NULL) {
169 *cnt = 0;
170 return (PCE_BLK_PSI_NO_ID);
171 }
172
173 if (sct->flags & PSI_FLAG_NO_DAM) {
174 sct->flags &= ~PSI_FLAG_NO_DAM;
175 }
176
177 fdc->dirty = 1;
178
179 if (*cnt > sct->n) {
180 *cnt = sct->n;
181 ret |= PCE_BLK_PSI_DATALEN;
182 }
183
184 if (*cnt > 0) {
185 memcpy (sct->data, buf, *cnt);
186 }
187
188 if (sct->weak == NULL) {
189 sct->flags &= ~PSI_FLAG_CRC_DATA;
190 }
191
192 if (sct->next != NULL) {
193 psi_sct_del (sct->next);
194
195 sct->next = NULL;
196 sct->cur_alt = 0;
197 }
198
199 return (ret);
200}
201
202unsigned dsk_psi_write_tags (disk_psi_t *fdc, const void *buf, unsigned cnt,
203 unsigned c, unsigned h, unsigned s, int phy)
204{
205 psi_sct_t *sct;
206
207 if (fdc->img == NULL) {
208 return (0);
209 }
210
211 if (fdc->dsk.readonly) {
212 return (0);
213 }
214
215 sct = psi_img_get_sector (fdc->img, c, h, s, phy);
216
217 if (sct == NULL) {
218 return (0);
219 }
220
221 fdc->dirty = 1;
222
223 cnt = psi_sct_set_tags (sct, buf, cnt);
224
225 return (cnt);
226}
227
228int dsk_psi_erase_track (disk_psi_t *fdc, unsigned c, unsigned h)
229{
230 psi_trk_t *trk;
231
232 if (fdc->img == NULL) {
233 return (1);
234 }
235
236 if (fdc->dsk.readonly) {
237 return (1);
238 }
239
240 trk = psi_img_get_track (fdc->img, c, h, 0);
241
242 if (trk == NULL) {
243 return (0);
244 }
245
246 fdc->dirty = 1;
247
248 fdc->dsk.blocks -= trk->sct_cnt;
249
250 psi_trk_free (trk);
251
252 return (0);
253}
254
255int dsk_psi_erase_disk (disk_psi_t *fdc)
256{
257 if (fdc->img == NULL) {
258 return (1);
259 }
260
261 if (fdc->dsk.readonly) {
262 return (1);
263 }
264
265 psi_img_erase (fdc->img);
266
267 fdc->dsk.blocks = 0;
268
269 fdc->dirty = 1;
270
271 return (0);
272}
273
274void dsk_psi_set_encoding (disk_psi_t *fdc, unsigned enc)
275{
276 fdc->encoding = enc;
277}
278
279int dsk_psi_format_sector (disk_psi_t *fdc,
280 unsigned pc, unsigned ph, unsigned c, unsigned h, unsigned s,
281 unsigned cnt, unsigned fill)
282{
283 psi_trk_t *trk;
284 psi_sct_t *sct;
285
286 if (fdc->img == NULL) {
287 return (1);
288 }
289
290 if (fdc->dsk.readonly) {
291 return (1);
292 }
293
294 trk = psi_img_get_track (fdc->img, pc, ph, 1);
295
296 if (trk == NULL) {
297 return (1);
298 }
299
300 fdc->dirty = 1;
301
302 sct = psi_sct_new (c, h, s, cnt);
303
304 if (sct == NULL) {
305 return (1);
306 }
307
308 psi_sct_fill (sct, fill);
309
310 if (psi_trk_add_sector (trk, sct)) {
311 psi_sct_del (sct);
312 return (1);
313 }
314
315 psi_sct_set_encoding (sct, fdc->encoding);
316
317 fdc->dsk.blocks += 1;
318
319 return (0);
320}
321
322int dsk_psi_read_id (disk_psi_t *fdc,
323 unsigned pc, unsigned ph, unsigned ps,
324 unsigned *c, unsigned *h, unsigned *s, unsigned *cnt, unsigned *cnt_id,
325 unsigned long *pos)
326{
327 unsigned mfm_size;
328 psi_sct_t *sct;
329
330 if (fdc->img == NULL) {
331 return (1);
332 }
333
334 sct = psi_img_get_sector (fdc->img, pc, ph, ps, 1);
335
336 if (sct == NULL) {
337 return (1);
338 }
339
340 *c = sct->c;
341 *h = sct->h;
342 *s = sct->s;
343 *cnt = sct->n;
344 *cnt_id = sct->n;
345
346 if (pos != NULL) {
347 if (sct->position == (unsigned long) -1) {
348 *pos = 0;
349 }
350 else {
351 *pos = sct->position;
352 }
353 }
354
355 if (sct->have_mfm_size) {
356 mfm_size = psi_sct_get_mfm_size (sct);
357
358 if (mfm_size <= 8) {
359 *cnt_id = 128U << mfm_size;
360 }
361 }
362
363 return (0);
364}
365
366
367static
368int dsk_psi_read (disk_t *dsk, void *buf, uint32_t i, uint32_t n)
369{
370 disk_psi_t *fdc;
371 unsigned c, h, s;
372 unsigned cnt;
373 unsigned char *tmp;
374
375 fdc = dsk->ext;
376
377 tmp = buf;
378
379 while (n > 0) {
380 if (psi_img_map_sector (fdc->img, i, &c, &h, &s)) {
381 return (1);
382 }
383
384 cnt = 512;
385
386 if (dsk_psi_read_chs (fdc, tmp, &cnt, c, h, s, 1)) {
387 return (1);
388 }
389
390 if (cnt != 512) {
391 return (1);
392 }
393
394 tmp += 512;
395
396 i += 1;
397 n -= 1;
398 }
399
400 return (0);
401}
402
403static
404int dsk_psi_write (disk_t *dsk, const void *buf, uint32_t i, uint32_t n)
405{
406 disk_psi_t *fdc;
407 unsigned c, h, s;
408 unsigned cnt;
409 const unsigned char *tmp;
410
411 if (dsk->readonly) {
412 return (1);
413 }
414
415 fdc = dsk->ext;
416
417 tmp = buf;
418
419 while (n > 0) {
420 if (psi_img_map_sector (fdc->img, i, &c, &h, &s)) {
421 return (1);
422 }
423
424 cnt = 512;
425
426 if (dsk_psi_write_chs (fdc, tmp, &cnt, c, h, s, 1)) {
427 return (1);
428 }
429
430 if (cnt != 512) {
431 return (1);
432 }
433
434 tmp += 512;
435
436 i += 1;
437 n -= 1;
438 }
439
440 return (0);
441}
442
443
444static
445int fdc_save (disk_psi_t *fdc)
446{
447 if (fdc->dsk.fname == NULL) {
448 return (1);
449 }
450
451 if (fdc->img == NULL) {
452 return (1);
453 }
454
455 if (dsk_get_readonly (&fdc->dsk)) {
456 return (1);
457 }
458
459 if (psi_save (fdc->dsk.fname, fdc->img, fdc->type)) {
460 return (1);
461 }
462
463 return (0);
464}
465
466static
467int fdc_set_geometry (disk_psi_t *fdc)
468{
469 unsigned c, h, s, t;
470 unsigned cyl_cnt, trk_cnt, sct_cnt;
471 psi_img_t *img;
472 psi_cyl_t *cyl;
473 psi_trk_t *trk;
474
475 img = fdc->img;
476
477 cyl_cnt = img->cyl_cnt;
478 trk_cnt = 0;
479 sct_cnt = 0;
480
481 for (c = 0; c < img->cyl_cnt; c++) {
482 cyl = img->cyl[c];
483
484 trk_cnt += cyl->trk_cnt;
485
486 for (t = 0; t < cyl->trk_cnt; t++) {
487 trk = cyl->trk[t];
488
489 sct_cnt += trk->sct_cnt;
490 }
491 }
492
493 if ((cyl_cnt == 0) || (trk_cnt == 0) || (sct_cnt == 0)) {
494 return (1);
495 }
496
497 c = cyl_cnt;
498 h = (trk_cnt + (trk_cnt / cyl_cnt / 2)) / cyl_cnt;
499 s = (sct_cnt + (sct_cnt / trk_cnt / 2)) / trk_cnt;
500
501 if (dsk_set_geometry (&fdc->dsk, sct_cnt, c, h, s)) {
502 return (1);
503 }
504
505 dsk_set_visible_chs (&fdc->dsk, c, h, s);
506
507 return (0);
508}
509
510static
511int dsk_psi_set_msg (disk_t *dsk, const char *msg, const char *val)
512{
513 disk_psi_t *fdc;
514
515 fdc = dsk->ext;
516
517 if (strcmp (msg, "commit") == 0) {
518 if (fdc_save (fdc)) {
519 return (1);
520 }
521
522 fdc->dirty = 0;
523
524 return (0);
525 }
526
527 return (1);
528}
529
530static
531void dsk_psi_del (disk_t *dsk)
532{
533 disk_psi_t *fdc;
534
535 fdc = dsk->ext;
536
537 if (fdc->dirty) {
538 fprintf (stderr, "disk %u: writing back psi image to %s\n",
539 fdc->dsk.drive,
540 (fdc->dsk.fname != NULL) ? fdc->dsk.fname : "<none>"
541 );
542
543 if (fdc_save (fdc)) {
544 fprintf (stderr, "disk %u: writing back failed\n",
545 dsk->drive
546 );
547 }
548 }
549
550 if (fdc->img != NULL) {
551 psi_img_del (fdc->img);
552 }
553
554 free (fdc);
555}
556
557disk_t *dsk_psi_open_fp (FILE *fp, unsigned type, int ro)
558{
559 disk_psi_t *fdc;
560
561 fdc = malloc (sizeof (disk_psi_t));
562
563 if (fdc == NULL) {
564 return (NULL);
565 }
566
567 dsk_init (&fdc->dsk, fdc, 0, 0, 0, 0);
568 dsk_set_type (&fdc->dsk, PCE_DISK_PSI);
569 dsk_set_readonly (&fdc->dsk, ro);
570
571 fdc->dsk.del = dsk_psi_del;
572 fdc->dsk.read = dsk_psi_read;
573 fdc->dsk.write = dsk_psi_write;
574 fdc->dsk.set_msg = dsk_psi_set_msg;
575
576 fdc->type = type;
577 fdc->encoding = PSI_ENC_MFM;
578 fdc->dirty = 0;
579
580 fdc->img = psi_load_fp (fp, type);
581
582 if (fdc->img == NULL) {
583 dsk_psi_del (&fdc->dsk);
584 return (NULL);
585 }
586
587 fdc_set_geometry (fdc);
588
589 return (&fdc->dsk);
590}
591
592disk_t *dsk_psi_open (const char *fname, unsigned type, int ro)
593{
594 disk_t *dsk;
595 FILE *fp;
596
597 if (type == PSI_FORMAT_NONE) {
598 type = psi_probe (fname);
599
600 if (type == PSI_FORMAT_NONE) {
601 type = psi_guess_type (fname);
602 }
603 }
604
605 if (type == PSI_FORMAT_NONE) {
606 return (NULL);
607 }
608
609 if (ro) {
610 fp = fopen (fname, "rb");
611 }
612 else {
613 fp = fopen (fname, "r+b");
614
615 if (fp == NULL) {
616 ro = 1;
617 fp = fopen (fname, "rb");
618 }
619 }
620
621 if (fp == NULL) {
622 return (NULL);
623 }
624
625 dsk = dsk_psi_open_fp (fp, type, ro);
626
627 fclose (fp);
628
629 if (dsk == NULL) {
630 return (NULL);
631 }
632
633 dsk_set_fname (dsk, fname);
634
635 return (dsk);
636}
637
638
639static
640int dsk_psi_init_psi (psi_img_t *img, unsigned long c, unsigned long h, unsigned long s)
641{
642 unsigned ci, hi, si;
643 psi_trk_t *trk;
644 psi_sct_t *sct;
645
646 if ((c > 65535) || (h > 65535) || (s > 65535)) {
647 return (1);
648 }
649
650 for (ci = 0; ci < c; ci++) {
651 for (hi = 0; hi < h; hi++) {
652 trk = psi_img_get_track (img, ci, hi, 1);
653
654 if (trk == NULL) {
655 return (1);
656 }
657
658 for (si = 0; si < s; si++) {
659 sct = psi_sct_new (ci, hi, si + 1, 512);
660
661 if (sct == NULL) {
662 return (1);
663 }
664
665 psi_sct_fill (sct, 0);
666 psi_sct_set_encoding (sct, 0);
667
668 psi_trk_add_sector (trk, sct);
669 }
670 }
671 }
672
673 return (0);
674}
675
676int dsk_psi_create_fp (FILE *fp, unsigned type, uint32_t c, uint32_t h, uint32_t s)
677{
678 int r;
679 psi_img_t *img;
680
681 img = psi_img_new();
682
683 if (img == NULL) {
684 return (1);
685 }
686
687 if (dsk_psi_init_psi (img, c, h, s)) {
688 psi_img_del (img);
689 return (1);
690 }
691
692 r = psi_save_fp (fp, img, type);
693
694 psi_img_del (img);
695
696 return (r);
697}
698
699int dsk_psi_create (const char *name, unsigned type, uint32_t c, uint32_t h, uint32_t s)
700{
701 int r;
702 FILE *fp;
703
704 if (type == PSI_FORMAT_NONE) {
705 type = psi_guess_type (name);
706 }
707
708 fp = fopen (name, "wb");
709
710 if (fp == NULL) {
711 return (1);
712 }
713
714 r = dsk_psi_create_fp (fp, type, c, h, s);
715
716 fclose (fp);
717
718 return (r);
719}
720
721
722unsigned dsk_psi_probe_fp (FILE *fp)
723{
724 return (psi_probe_fp (fp));
725}
726
727unsigned dsk_psi_probe (const char *fname)
728{
729 return (psi_probe (fname));
730}