ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541d64.cpp
Revision: 1.4
Committed: 2004-01-11T00:09:51Z (20 years, 3 months ago) by cebix
Branch: MAIN
Changes since 1.3: +122 -350 lines
Log Message:
some cleanups and refactoring

File Contents

# Content
1 /*
2 * 1541d64.cpp - 1541 emulation in .d64 file
3 *
4 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * Incompatibilities:
23 * ------------------
24 *
25 * - Only read accesses possible
26 * - Not all commands implemented
27 * - The .d64 error info is read, but unused
28 */
29
30 #include "sysdeps.h"
31
32 #include "1541d64.h"
33 #include "IEC.h"
34 #include "Prefs.h"
35 #include "C64.h"
36
37
38 // Channel modes (IRC users listen up :-)
39 enum {
40 CHMOD_FREE, // Channel free
41 CHMOD_COMMAND, // Command/error channel
42 CHMOD_DIRECTORY, // Reading directory
43 CHMOD_FILE, // Sequential file open
44 CHMOD_DIRECT // Direct buffer access ('#')
45 };
46
47 // Number of tracks/sectors
48 const int NUM_TRACKS = 35;
49 const int NUM_SECTORS = 683;
50
51 // Prototypes
52 static bool match(uint8 *p, uint8 *n);
53
54
55 /*
56 * Constructor: Prepare emulation, open .d64 file
57 */
58
59 D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec)
60 {
61 the_file = NULL;
62 ram = NULL;
63
64 Ready = false;
65 strcpy(orig_d64_name, filepath);
66 for (int i=0; i<=14; i++) {
67 chan_mode[i] = CHMOD_FREE;
68 chan_buf[i] = NULL;
69 }
70 chan_mode[15] = CHMOD_COMMAND;
71
72 // Open .d64 file
73 open_close_d64_file(filepath);
74 if (the_file != NULL) {
75
76 // Allocate 1541 RAM
77 ram = new uint8[DRIVE_RAM_SIZE];
78 bam = (BAM *)(ram + 0x700);
79
80 Reset();
81 Ready = true;
82 }
83 }
84
85
86 /*
87 * Destructor
88 */
89
90 D64Drive::~D64Drive()
91 {
92 // Close .d64 file
93 open_close_d64_file("");
94
95 delete[] ram;
96 Ready = false;
97 }
98
99
100 /*
101 * Open/close the .d64 file
102 */
103
104 void D64Drive::open_close_d64_file(char *d64name)
105 {
106 long size;
107 uint8 magic[4];
108
109 // Close old .d64, if open
110 if (the_file != NULL) {
111 close_all_channels();
112 fclose(the_file);
113 the_file = NULL;
114 }
115
116 // Open new .d64 file
117 if (d64name[0]) {
118 if ((the_file = fopen(d64name, "rb")) != NULL) {
119
120 // Check length
121 fseek(the_file, 0, SEEK_END);
122 if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
123 fclose(the_file);
124 the_file = NULL;
125 return;
126 }
127
128 // x64 image?
129 rewind(the_file);
130 fread(&magic, 4, 1, the_file);
131 if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
132 image_header = 64;
133 else
134 image_header = 0;
135
136 // Preset error info (all sectors no error)
137 memset(error_info, 1, NUM_SECTORS);
138
139 // Load sector error info from .d64 file, if present
140 if (!image_header && size == NUM_SECTORS * 257) {
141 fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
142 fread(&error_info, NUM_SECTORS, 1, the_file);
143 }
144 }
145 }
146 }
147
148
149 /*
150 * Open channel
151 */
152
153 uint8 D64Drive::Open(int channel, const uint8 *name, int name_len)
154 {
155 set_error(ERR_OK);
156
157 // Channel 15: execute file name as command
158 if (channel == 15) {
159 execute_cmd(name, name_len);
160 return ST_OK;
161 }
162
163 if (chan_mode[channel] != CHMOD_FREE) {
164 set_error(ERR_NOCHANNEL);
165 return ST_OK;
166 }
167
168 if (name[0] == '$')
169 if (channel)
170 return open_file_ts(channel, 18, 0);
171 else
172 return open_directory(name + 1, name_len - 1);
173
174 if (name[0] == '#')
175 return open_direct(channel, name);
176
177 return open_file(channel, name, name_len);
178 }
179
180
181 /*
182 * Open file
183 */
184
185 uint8 D64Drive::open_file(int channel, const uint8 *name, int name_len)
186 {
187 uint8 plain_name[256];
188 int plain_name_len;
189 int mode = FMODE_READ;
190 int type = FTYPE_PRG;
191 int rec_len = 0;
192 parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len);
193 if (plain_name_len > 16)
194 plain_name_len = 16;
195
196 // Channel 0 is READ, channel 1 is WRITE
197 if (channel == 0 || channel == 1) {
198 mode = channel ? FMODE_WRITE : FMODE_READ;
199 if (type == FTYPE_DEL)
200 type = FTYPE_PRG;
201 }
202
203 // Allow only read accesses
204 if (mode != FMODE_READ) {
205 set_error(ERR_WRITEPROTECT);
206 return ST_OK;
207 }
208
209 // Relative files are not supported
210 if (type == FTYPE_REL) {
211 set_error(ERR_UNIMPLEMENTED);
212 return ST_OK;
213 }
214
215 // Find file in directory and open it
216 int track, sector;
217 if (find_file(plain_name, &track, &sector))
218 return open_file_ts(channel, track, sector);
219 else
220 set_error(ERR_FILENOTFOUND);
221
222 return ST_OK;
223 }
224
225
226 /*
227 * Search file in directory, find first track and sector
228 * false: not found, true: found
229 */
230
231 bool D64Drive::find_file(const uint8 *pattern, int *track, int *sector)
232 {
233 int i, j;
234 const uint8 *p, *q;
235 DirEntry *de;
236
237 // Scan all directory blocks
238 dir.next_track = bam->dir_track;
239 dir.next_sector = bam->dir_sector;
240
241 while (dir.next_track) {
242 if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
243 return false;
244
245 // Scan all 8 entries of a block
246 for (j=0; j<8; j++) {
247 de = &dir.entry[j];
248 *track = de->track;
249 *sector = de->sector;
250
251 if (de->type) {
252 p = pattern;
253 q = de->name;
254 for (i=0; i<16 && *p; i++, p++, q++) {
255 if (*p == '*') // Wildcard '*' matches all following characters
256 return true;
257 if (*p != *q) {
258 if (*p != '?') goto next_entry; // Wildcard '?' matches single character
259 if (*q == 0xa0) goto next_entry;
260 }
261 }
262
263 if (i == 16 || *q == 0xa0)
264 return true;
265 }
266 next_entry: ;
267 }
268 }
269
270 return false;
271 }
272
273
274 /*
275 * Open file given track/sector of first block
276 */
277
278 uint8 D64Drive::open_file_ts(int channel, int track, int sector)
279 {
280 chan_buf[channel] = new uint8[256];
281 chan_mode[channel] = CHMOD_FILE;
282
283 // On the next call to Read, the first block will be read
284 chan_buf[channel][0] = track;
285 chan_buf[channel][1] = sector;
286 buf_len[channel] = 0;
287
288 return ST_OK;
289 }
290
291
292 /*
293 * Prepare directory as BASIC program (channel 0)
294 */
295
296 const char type_char_1[] = "DSPUREERSELQGRL?";
297 const char type_char_2[] = "EERSELQGRL??????";
298 const char type_char_3[] = "LQGRL???????????";
299
300 // Return true if name 'n' matches pattern 'p'
301 static bool match(uint8 *p, uint8 *n)
302 {
303 if (!*p) // Null pattern matches everything
304 return true;
305
306 do {
307 if (*p == '*') // Wildcard '*' matches all following characters
308 return true;
309 if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
310 return false;
311 p++; n++;
312 } while (*p);
313
314 return *n == 0xa0;
315 }
316
317 uint8 D64Drive::open_directory(const uint8 *pattern, int pattern_len)
318 {
319 // Special treatment for "$0"
320 if (pattern[0] == '0' && pattern[1] == 0) {
321 pattern++;
322 pattern_len--;
323 }
324
325 // Skip everything before the ':' in the pattern
326 uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len);
327 if (t) {
328 t++;
329 pattern_len -= t - pattern;
330 pattern = t;
331 }
332
333 chan_mode[0] = CHMOD_DIRECTORY;
334 uint8 *p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
335
336 // Create directory title
337 *p++ = 0x01; // Load address $0401 (from PET days :-)
338 *p++ = 0x04;
339 *p++ = 0x01; // Dummy line link
340 *p++ = 0x01;
341 *p++ = 0; // Drive number (0) as line number
342 *p++ = 0;
343 *p++ = 0x12; // RVS ON
344 *p++ = '\"';
345
346 uint8 *q = bam->disk_name;
347 for (int i=0; i<23; i++) {
348 int c;
349 if ((c = *q++) == 0xa0)
350 *p++ = ' '; // Replace 0xa0 by space
351 else
352 *p++ = c;
353 }
354 *(p-7) = '\"';
355 *p++ = 0;
356
357 // Scan all directory blocks
358 dir.next_track = bam->dir_track;
359 dir.next_sector = bam->dir_sector;
360
361 while (dir.next_track) {
362 if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
363 return ST_OK;
364
365 // Scan all 8 entries of a block
366 for (int j=0; j<8; j++) {
367 DirEntry *de = &dir.entry[j];
368
369 if (de->type && match((uint8 *)pattern, de->name)) {
370 *p++ = 0x01; // Dummy line link
371 *p++ = 0x01;
372
373 *p++ = de->num_blocks_l; // Line number
374 *p++ = de->num_blocks_h;
375
376 *p++ = ' ';
377 int n = (de->num_blocks_h << 8) + de->num_blocks_l;
378 if (n<10) *p++ = ' ';
379 if (n<100) *p++ = ' ';
380
381 *p++ = '\"';
382 q = de->name;
383 uint8 c;
384 int m = 0;
385 for (int i=0; i<16; i++) {
386 if ((c = *q++) == 0xa0) {
387 if (m)
388 *p++ = ' '; // Replace all 0xa0 by spaces
389 else
390 m = *p++ = '\"'; // But the first by a '"'
391 } else
392 *p++ = c;
393 }
394 if (m)
395 *p++ = ' ';
396 else
397 *p++ = '\"'; // No 0xa0, then append a space
398
399 // Open files are marked by '*'
400 if (de->type & 0x80)
401 *p++ = ' ';
402 else
403 *p++ = '*';
404
405 // File type
406 *p++ = type_char_1[de->type & 0x0f];
407 *p++ = type_char_2[de->type & 0x0f];
408 *p++ = type_char_3[de->type & 0x0f];
409
410 // Protected files are marked by '<'
411 if (de->type & 0x40)
412 *p++ = '<';
413 else
414 *p++ = ' ';
415
416 // Appropriate number of spaces at the end
417 *p++ = ' ';
418 if (n >= 10) *p++ = ' ';
419 if (n >= 100) *p++ = ' ';
420 *p++ = 0;
421 }
422 }
423 }
424
425 // Final line, count number of free blocks
426 int n = 0;
427 for (int i=0; i<35; i++) {
428 if (i != 17) // exclude directory track
429 n += bam->bitmap[i*4];
430 }
431
432 *p++ = 0x01; // Dummy line link
433 *p++ = 0x01;
434 *p++ = n & 0xff; // Number of free blocks as line number
435 *p++ = (n >> 8) & 0xff;
436
437 *p++ = 'B';
438 *p++ = 'L';
439 *p++ = 'O';
440 *p++ = 'C';
441 *p++ = 'K';
442 *p++ = 'S';
443 *p++ = ' ';
444 *p++ = 'F';
445 *p++ = 'R';
446 *p++ = 'E';
447 *p++ = 'E';
448 *p++ = '.';
449
450 memset(p, ' ', 13);
451 p += 13;
452
453 *p++ = 0;
454 *p++ = 0;
455 *p++ = 0;
456
457 buf_len[0] = p - chan_buf[0];
458
459 return ST_OK;
460 }
461
462
463 /*
464 * Open channel for direct buffer access
465 */
466
467 uint8 D64Drive::open_direct(int channel, const uint8 *name)
468 {
469 int buf = -1;
470
471 if (name[1] == 0)
472 buf = alloc_buffer(-1);
473 else
474 if ((name[1] >= '0') && (name[1] <= '3') && (name[2] == 0))
475 buf = alloc_buffer(name[1] - '0');
476
477 if (buf == -1) {
478 set_error(ERR_NOCHANNEL);
479 return ST_OK;
480 }
481
482 // The buffers are in the 1541 RAM at $300 and are 256 bytes each
483 chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8);
484 chan_mode[channel] = CHMOD_DIRECT;
485 chan_buf_num[channel] = buf;
486
487 // Store actual buffer number in buffer
488 *chan_buf[channel] = buf + '0';
489 buf_len[channel] = 1;
490
491 return ST_OK;
492 }
493
494
495 /*
496 * Close channel
497 */
498
499 uint8 D64Drive::Close(int channel)
500 {
501 if (channel == 15) {
502 close_all_channels();
503 return ST_OK;
504 }
505
506 switch (chan_mode[channel]) {
507 case CHMOD_FREE:
508 break;
509
510 case CHMOD_DIRECT:
511 free_buffer(chan_buf_num[channel]);
512 chan_buf[channel] = NULL;
513 chan_mode[channel] = CHMOD_FREE;
514 break;
515
516 default:
517 delete[] chan_buf[channel];
518 chan_buf[channel] = NULL;
519 chan_mode[channel] = CHMOD_FREE;
520 break;
521 }
522
523 return ST_OK;
524 }
525
526
527 /*
528 * Close all channels
529 */
530
531 void D64Drive::close_all_channels()
532 {
533 for (int i=0; i<15; i++)
534 Close(i);
535
536 cmd_len = 0;
537 }
538
539
540 /*
541 * Read from channel
542 */
543
544 uint8 D64Drive::Read(int channel, uint8 *byte)
545 {
546 switch (chan_mode[channel]) {
547 case CHMOD_COMMAND:
548 *byte = *error_ptr++;
549 if (--error_len)
550 return ST_OK;
551 else {
552 set_error(ERR_OK);
553 return ST_EOF;
554 }
555 break;
556
557 case CHMOD_FILE:
558 // Read next block if necessary
559 if (chan_buf[channel][0] && !buf_len[channel]) {
560 if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel]))
561 return ST_READ_TIMEOUT;
562 buf_ptr[channel] = chan_buf[channel] + 2;
563
564 // Determine block length
565 buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1;
566 }
567
568 if (buf_len[channel] > 0) {
569 *byte = *buf_ptr[channel]++;
570 if (!--buf_len[channel] && !chan_buf[channel][0])
571 return ST_EOF;
572 else
573 return ST_OK;
574 } else
575 return ST_READ_TIMEOUT;
576 break;
577
578 case CHMOD_DIRECTORY:
579 case CHMOD_DIRECT:
580 if (buf_len[channel] > 0) {
581 *byte = *buf_ptr[channel]++;
582 if (--buf_len[channel])
583 return ST_OK;
584 else
585 return ST_EOF;
586 } else
587 return ST_READ_TIMEOUT;
588 break;
589 }
590 return ST_READ_TIMEOUT;
591 }
592
593
594 /*
595 * Write byte to channel
596 */
597
598 uint8 D64Drive::Write(int channel, uint8 byte, bool eoi)
599 {
600 switch (chan_mode[channel]) {
601 case CHMOD_FREE:
602 set_error(ERR_FILENOTOPEN);
603 break;
604
605 case CHMOD_COMMAND:
606 // Collect characters and execute command on EOI
607 if (cmd_len >= 58)
608 return ST_TIMEOUT;
609
610 cmd_buf[cmd_len++] = byte;
611
612 if (eoi) {
613 execute_cmd(cmd_buf, cmd_len);
614 cmd_len = 0;
615 }
616 return ST_OK;
617
618 case CHMOD_DIRECTORY:
619 set_error(ERR_WRITEFILEOPEN);
620 break;
621 }
622 return ST_TIMEOUT;
623 }
624
625
626 /*
627 * Execute command string
628 */
629
630 // BLOCK-READ:channel,0,track,sector
631 void D64Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
632 {
633 if (channel >= 16 || chan_mode[channel] != CHMOD_DIRECT) {
634 set_error(ERR_NOCHANNEL);
635 return;
636 }
637 read_sector(track, sector, chan_buf[channel]);
638 if (user_cmd) {
639 buf_len[channel] = 256;
640 buf_ptr[channel] = chan_buf[channel];
641 } else {
642 buf_len[channel] = chan_buf[channel][0];
643 buf_ptr[channel] = chan_buf[channel] + 1;
644 }
645 }
646
647 // BUFFER-POINTER:channel,pos
648 void D64Drive::buffer_pointer_cmd(int channel, int pos)
649 {
650 if (channel >= 16 || chan_mode[channel] != CHMOD_DIRECT) {
651 set_error(ERR_NOCHANNEL);
652 return;
653 }
654 buf_ptr[channel] = chan_buf[channel] + pos;
655 buf_len[channel] = 256 - pos;
656 }
657
658 // M-R<adr low><adr high>[<number>]
659 void D64Drive::mem_read_cmd(uint16 adr, uint8 len)
660 {
661 error_len = len;
662 if (adr >= 0x300 && adr < 0x1000) {
663 // Read from RAM
664 error_ptr = (char *)ram + (adr & 0x7ff);
665 } else {
666 unsupp_cmd();
667 memset(error_buf, 0, len);
668 error_ptr = error_buf;
669 }
670 }
671
672 // M-W<adr low><adr high><number><data...>
673 void D64Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
674 {
675 while (len) {
676 if (adr >= 0x300 && adr < 0x1000) {
677 // Write to RAM
678 ram[adr & 0x7ff] = *p;
679 } else if (adr < 0xc000) {
680 unsupp_cmd();
681 return;
682 }
683 len--; adr++; p++;
684 }
685 }
686
687 // INITIALIZE
688 void D64Drive::initialize_cmd(void)
689 {
690 // Close all channels and re-read BAM
691 close_all_channels();
692 read_sector(18, 0, (uint8 *)bam);
693 }
694
695
696 /*
697 * Reset drive
698 */
699
700 void D64Drive::Reset(void)
701 {
702 close_all_channels();
703
704 read_sector(18, 0, (uint8 *)bam);
705
706 cmd_len = 0;
707 for (int i=0; i<4; i++)
708 buf_free[i] = true;
709
710 set_error(ERR_STARTUP);
711 }
712
713
714 /*
715 * Allocate floppy buffer
716 * -> Desired buffer number or -1
717 * <- Allocated buffer number or -1
718 */
719
720 int D64Drive::alloc_buffer(int want)
721 {
722 if (want == -1) {
723 for (want=3; want>=0; want--)
724 if (buf_free[want]) {
725 buf_free[want] = false;
726 return want;
727 }
728 return -1;
729 }
730
731 if (want < 4)
732 if (buf_free[want]) {
733 buf_free[want] = false;
734 return want;
735 } else
736 return -1;
737 else
738 return -1;
739 }
740
741
742 /*
743 * Free floppy buffer
744 */
745
746 void D64Drive::free_buffer(int buf)
747 {
748 buf_free[buf] = true;
749 }
750
751
752 /*
753 * Read sector (256 bytes)
754 * true: success, false: error
755 */
756
757 bool D64Drive::read_sector(int track, int sector, uint8 *buffer)
758 {
759 int offset;
760
761 // Convert track/sector to byte offset in file
762 if ((offset = offset_from_ts(track, sector)) < 0) {
763 set_error(ERR_ILLEGALTS);
764 return false;
765 }
766
767 if (the_file == NULL) {
768 set_error(ERR_NOTREADY);
769 return false;
770 }
771
772 #ifdef AMIGA
773 if (offset != ftell(the_file))
774 fseek(the_file, offset + image_header, SEEK_SET);
775 #else
776 fseek(the_file, offset + image_header, SEEK_SET);
777 #endif
778 fread(buffer, 256, 1, the_file);
779 return true;
780 }
781
782
783 /*
784 * Convert track/sector to offset
785 */
786
787 const int num_sectors[41] = {
788 0,
789 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
790 19,19,19,19,19,19,19,
791 18,18,18,18,18,18,
792 17,17,17,17,17,
793 17,17,17,17,17 // Tracks 36..40
794 };
795
796 const int sector_offset[41] = {
797 0,
798 0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
799 357,376,395,414,433,452,471,
800 490,508,526,544,562,580,
801 598,615,632,649,666,
802 683,700,717,734,751 // Tracks 36..40
803 };
804
805 int D64Drive::offset_from_ts(int track, int sector)
806 {
807 if ((track < 1) || (track > NUM_TRACKS) ||
808 (sector < 0) || (sector >= num_sectors[track]))
809 return -1;
810
811 return (sector_offset[track] + sector) << 8;
812 }