ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541d64.cpp
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (20 years, 8 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

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
36
37 // Channel modes (IRC users listen up :-)
38 enum {
39 CHMOD_FREE, // Channel free
40 CHMOD_COMMAND, // Command/error channel
41 CHMOD_DIRECTORY, // Reading directory
42 CHMOD_FILE, // Sequential file open
43 CHMOD_DIRECT // Direct buffer access ('#')
44 };
45
46 // Access modes
47 enum {
48 FMODE_READ, FMODE_WRITE, FMODE_APPEND
49 };
50
51 // File types
52 enum {
53 FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
54 };
55
56 // Number of tracks/sectors
57 const int NUM_TRACKS = 35;
58 const int NUM_SECTORS = 683;
59
60 // Prototypes
61 static bool match(uint8 *p, uint8 *n);
62
63
64 /*
65 * Constructor: Prepare emulation, open .d64 file
66 */
67
68 D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec)
69 {
70 the_file = NULL;
71 ram = NULL;
72
73 Ready = false;
74 strcpy(orig_d64_name, filepath);
75 for (int i=0; i<=14; i++) {
76 chan_mode[i] = CHMOD_FREE;
77 chan_buf[i] = NULL;
78 }
79 chan_mode[15] = CHMOD_COMMAND;
80
81 // Open .d64 file
82 open_close_d64_file(filepath);
83 if (the_file != NULL) {
84
85 // Allocate 1541 RAM
86 ram = new uint8[0x800];
87 bam = (BAM *)(ram + 0x700);
88
89 Reset();
90 Ready = true;
91 }
92 }
93
94
95 /*
96 * Destructor
97 */
98
99 D64Drive::~D64Drive()
100 {
101 // Close .d64 file
102 open_close_d64_file("");
103
104 delete[] ram;
105 Ready = false;
106 }
107
108
109 /*
110 * Open/close the .d64 file
111 */
112
113 void D64Drive::open_close_d64_file(char *d64name)
114 {
115 long size;
116 uint8 magic[4];
117
118 // Close old .d64, if open
119 if (the_file != NULL) {
120 close_all_channels();
121 fclose(the_file);
122 the_file = NULL;
123 }
124
125 // Open new .d64 file
126 if (d64name[0]) {
127 if ((the_file = fopen(d64name, "rb")) != NULL) {
128
129 // Check length
130 fseek(the_file, 0, SEEK_END);
131 if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
132 fclose(the_file);
133 the_file = NULL;
134 return;
135 }
136
137 // x64 image?
138 rewind(the_file);
139 fread(&magic, 4, 1, the_file);
140 if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
141 image_header = 64;
142 else
143 image_header = 0;
144
145 // Preset error info (all sectors no error)
146 memset(error_info, 1, NUM_SECTORS);
147
148 // Load sector error info from .d64 file, if present
149 if (!image_header && size == NUM_SECTORS * 257) {
150 fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
151 fread(&error_info, NUM_SECTORS, 1, the_file);
152 }
153 }
154 }
155 }
156
157
158 /*
159 * Open channel
160 */
161
162 uint8 D64Drive::Open(int channel, char *filename)
163 {
164 set_error(ERR_OK);
165
166 // Channel 15: execute file name as command
167 if (channel == 15) {
168 execute_command(filename);
169 return ST_OK;
170 }
171
172 if (chan_mode[channel] != CHMOD_FREE) {
173 set_error(ERR_NOCHANNEL);
174 return ST_OK;
175 }
176
177 if (filename[0] == '$')
178 if (channel)
179 return open_file_ts(channel, 18, 0);
180 else
181 return open_directory(filename+1);
182
183 if (filename[0] == '#')
184 return open_direct(channel, filename);
185
186 return open_file(channel, filename);
187 }
188
189
190 /*
191 * Open file
192 */
193
194 uint8 D64Drive::open_file(int channel, char *filename)
195 {
196 char plainname[256];
197 int filemode = FMODE_READ;
198 int filetype = FTYPE_PRG;
199 int track, sector;
200
201 convert_filename(filename, plainname, &filemode, &filetype);
202
203 // Channel 0 is READ PRG, channel 1 is WRITE PRG
204 if (!channel) {
205 filemode = FMODE_READ;
206 filetype = FTYPE_PRG;
207 }
208 if (channel == 1) {
209 filemode = FMODE_WRITE;
210 filetype = FTYPE_PRG;
211 }
212
213 // Allow only read accesses
214 if (filemode != FMODE_READ) {
215 set_error(ERR_WRITEPROTECT);
216 return ST_OK;
217 }
218
219 // Find file in directory and open it
220 if (find_file(plainname, &track, &sector))
221 return open_file_ts(channel, track, sector);
222 else
223 set_error(ERR_FILENOTFOUND);
224
225 return ST_OK;
226 }
227
228
229 /*
230 * Analyze file name, get access mode and type
231 */
232
233 void D64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype)
234 {
235 char *p;
236
237 // Search for ':', p points to first character after ':'
238 if ((p = strchr(srcname, ':')) != NULL)
239 p++;
240 else
241 p = srcname;
242
243 // Remaining string -> destname
244 strncpy(destname, p, NAMEBUF_LENGTH);
245
246 // Search for ','
247 p = destname;
248 while (*p && (*p != ',')) p++;
249
250 // Look for mode parameters seperated by ','
251 p = destname;
252 while ((p = strchr(p, ',')) != NULL) {
253
254 // Cut string after the first ','
255 *p++ = 0;
256
257 switch (*p) {
258 case 'P':
259 *filetype = FTYPE_PRG;
260 break;
261 case 'S':
262 *filetype = FTYPE_SEQ;
263 break;
264 case 'U':
265 *filetype = FTYPE_USR;
266 break;
267 case 'L':
268 *filetype = FTYPE_REL;
269 break;
270 case 'R':
271 *filemode = FMODE_READ;
272 break;
273 case 'W':
274 *filemode = FMODE_WRITE;
275 break;
276 case 'A':
277 *filemode = FMODE_APPEND;
278 break;
279 }
280 }
281 }
282
283
284 /*
285 * Search file in directory, find first track and sector
286 * false: not found, true: found
287 */
288
289 bool D64Drive::find_file(char *filename, int *track, int *sector)
290 {
291 int i, j;
292 uint8 *p, *q;
293 DirEntry *de;
294
295 // Scan all directory blocks
296 dir.next_track = bam->dir_track;
297 dir.next_sector = bam->dir_sector;
298
299 while (dir.next_track) {
300 if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
301 return false;
302
303 // Scan all 8 entries of a block
304 for (j=0; j<8; j++) {
305 de = &dir.entry[j];
306 *track = de->track;
307 *sector = de->sector;
308
309 if (de->type) {
310 p = (uint8 *)filename;
311 q = de->name;
312 for (i=0; i<16 && *p; i++, p++, q++) {
313 if (*p == '*') // Wildcard '*' matches all following characters
314 return true;
315 if (*p != *q) {
316 if (*p != '?') goto next_entry; // Wildcard '?' matches single character
317 if (*q == 0xa0) goto next_entry;
318 }
319 }
320
321 if (i == 16 || *q == 0xa0)
322 return true;
323 }
324 next_entry: ;
325 }
326 }
327
328 return false;
329 }
330
331
332 /*
333 * Open file given track/sector of first block
334 */
335
336 uint8 D64Drive::open_file_ts(int channel, int track, int sector)
337 {
338 chan_buf[channel] = new uint8[256];
339 chan_mode[channel] = CHMOD_FILE;
340
341 // On the next call to Read, the first block will be read
342 chan_buf[channel][0] = track;
343 chan_buf[channel][1] = sector;
344 buf_len[channel] = 0;
345
346 return ST_OK;
347 }
348
349
350 /*
351 * Prepare directory as BASIC program (channel 0)
352 */
353
354 const char type_char_1[] = "DSPUREERSELQGRL?";
355 const char type_char_2[] = "EERSELQGRL??????";
356 const char type_char_3[] = "LQGRL???????????";
357
358 // Return true if name 'n' matches pattern 'p'
359 static bool match(uint8 *p, uint8 *n)
360 {
361 if (!*p) // Null pattern matches everything
362 return true;
363
364 do {
365 if (*p == '*') // Wildcard '*' matches all following characters
366 return true;
367 if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character
368 return false;
369 p++; n++;
370 } while (*p);
371
372 return *n == 0xa0;
373 }
374
375 uint8 D64Drive::open_directory(char *pattern)
376 {
377 int i, j, n, m;
378 uint8 *p, *q;
379 DirEntry *de;
380 uint8 c;
381 char *tmppat;
382
383 // Special treatment for "$0"
384 if (pattern[0] == '0' && pattern[1] == 0)
385 pattern += 1;
386
387 // Skip everything before the ':' in the pattern
388 if ((tmppat = strchr(pattern, ':')) != NULL)
389 pattern = tmppat + 1;
390
391 p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
392 chan_mode[0] = CHMOD_DIRECTORY;
393
394 // Create directory title
395 *p++ = 0x01; // Load address $0401 (from PET days :-)
396 *p++ = 0x04;
397 *p++ = 0x01; // Dummy line link
398 *p++ = 0x01;
399 *p++ = 0; // Drive number (0) as line number
400 *p++ = 0;
401 *p++ = 0x12; // RVS ON
402 *p++ = '\"';
403
404 q = bam->disk_name;
405 for (i=0; i<23; i++) {
406 if ((c = *q++) == 0xa0)
407 *p++ = ' '; // Replace 0xa0 by space
408 else
409 *p++ = c;
410 }
411 *(p-7) = '\"';
412 *p++ = 0;
413
414 // Scan all directory blocks
415 dir.next_track = bam->dir_track;
416 dir.next_sector = bam->dir_sector;
417
418 while (dir.next_track) {
419 if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
420 return ST_OK;
421
422 // Scan all 8 entries of a block
423 for (j=0; j<8; j++) {
424 de = &dir.entry[j];
425
426 if (de->type && match((uint8 *)pattern, de->name)) {
427 *p++ = 0x01; // Dummy line link
428 *p++ = 0x01;
429
430 *p++ = de->num_blocks_l; // Line number
431 *p++ = de->num_blocks_h;
432
433 *p++ = ' ';
434 n = (de->num_blocks_h << 8) + de->num_blocks_l;
435 if (n<10) *p++ = ' ';
436 if (n<100) *p++ = ' ';
437
438 *p++ = '\"';
439 q = de->name;
440 m = 0;
441 for (i=0; i<16; i++) {
442 if ((c = *q++) == 0xa0) {
443 if (m)
444 *p++ = ' '; // Replace all 0xa0 by spaces
445 else
446 m = *p++ = '\"'; // But the first by a '"'
447 } else
448 *p++ = c;
449 }
450 if (m)
451 *p++ = ' ';
452 else
453 *p++ = '\"'; // No 0xa0, then append a space
454
455 if (de->type & 0x80)
456 *p++ = ' ';
457 else
458 *p++ = '*';
459
460 *p++ = type_char_1[de->type & 0x0f];
461 *p++ = type_char_2[de->type & 0x0f];
462 *p++ = type_char_3[de->type & 0x0f];
463
464 if (de->type & 0x40)
465 *p++ = '<';
466 else
467 *p++ = ' ';
468
469 *p++ = ' ';
470 if (n >= 10) *p++ = ' ';
471 if (n >= 100) *p++ = ' ';
472 *p++ = 0;
473 }
474 }
475 }
476
477 // Final line
478 q = p;
479 for (i=0; i<29; i++)
480 *q++ = ' ';
481
482 n = 0;
483 for (i=0; i<35; i++)
484 n += bam->bitmap[i*4];
485
486 *p++ = 0x01; // Dummy line link
487 *p++ = 0x01;
488 *p++ = n & 0xff; // Number of free blocks as line number
489 *p++ = (n >> 8) & 0xff;
490
491 *p++ = 'B';
492 *p++ = 'L';
493 *p++ = 'O';
494 *p++ = 'C';
495 *p++ = 'K';
496 *p++ = 'S';
497 *p++ = ' ';
498 *p++ = 'F';
499 *p++ = 'R';
500 *p++ = 'E';
501 *p++ = 'E';
502 *p++ = '.';
503
504 p = q;
505 *p++ = 0;
506 *p++ = 0;
507 *p++ = 0;
508
509 buf_len[0] = p - chan_buf[0];
510
511 return ST_OK;
512 }
513
514
515 /*
516 * Open channel for direct buffer access
517 */
518
519 uint8 D64Drive::open_direct(int channel, char *filename)
520 {
521 int buf = -1;
522
523 if (filename[1] == 0)
524 buf = alloc_buffer(-1);
525 else
526 if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0))
527 buf = alloc_buffer(filename[1] - '0');
528
529 if (buf == -1) {
530 set_error(ERR_NOCHANNEL);
531 return ST_OK;
532 }
533
534 // The buffers are in the 1541 RAM at $300 and are 256 bytes each
535 chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8);
536 chan_mode[channel] = CHMOD_DIRECT;
537 chan_buf_num[channel] = buf;
538
539 // Store actual buffer number in buffer
540 *chan_buf[channel] = buf + '0';
541 buf_len[channel] = 1;
542
543 return ST_OK;
544 }
545
546
547 /*
548 * Close channel
549 */
550
551 uint8 D64Drive::Close(int channel)
552 {
553 if (channel == 15) {
554 close_all_channels();
555 return ST_OK;
556 }
557
558 switch (chan_mode[channel]) {
559 case CHMOD_FREE:
560 break;
561
562 case CHMOD_DIRECT:
563 free_buffer(chan_buf_num[channel]);
564 chan_buf[channel] = NULL;
565 chan_mode[channel] = CHMOD_FREE;
566 break;
567
568 default:
569 delete[] chan_buf[channel];
570 chan_buf[channel] = NULL;
571 chan_mode[channel] = CHMOD_FREE;
572 break;
573 }
574
575 return ST_OK;
576 }
577
578
579 /*
580 * Close all channels
581 */
582
583 void D64Drive::close_all_channels()
584 {
585 for (int i=0; i<15; i++)
586 Close(i);
587
588 cmd_len = 0;
589 }
590
591
592 /*
593 * Read from channel
594 */
595
596 uint8 D64Drive::Read(int channel, uint8 *byte)
597 {
598 switch (chan_mode[channel]) {
599 case CHMOD_COMMAND:
600 *byte = *error_ptr++;
601 if (--error_len)
602 return ST_OK;
603 else {
604 set_error(ERR_OK);
605 return ST_EOF;
606 }
607 break;
608
609 case CHMOD_FILE:
610 // Read next block if necessary
611 if (chan_buf[channel][0] && !buf_len[channel]) {
612 if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel]))
613 return ST_READ_TIMEOUT;
614 buf_ptr[channel] = chan_buf[channel] + 2;
615
616 // Determine block length
617 buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1;
618 }
619
620 if (buf_len[channel] > 0) {
621 *byte = *buf_ptr[channel]++;
622 if (!--buf_len[channel] && !chan_buf[channel][0])
623 return ST_EOF;
624 else
625 return ST_OK;
626 } else
627 return ST_READ_TIMEOUT;
628 break;
629
630 case CHMOD_DIRECTORY:
631 case CHMOD_DIRECT:
632 if (buf_len[channel] > 0) {
633 *byte = *buf_ptr[channel]++;
634 if (--buf_len[channel])
635 return ST_OK;
636 else
637 return ST_EOF;
638 } else
639 return ST_READ_TIMEOUT;
640 break;
641 }
642 return ST_READ_TIMEOUT;
643 }
644
645
646 /*
647 * Write byte to channel
648 */
649
650 uint8 D64Drive::Write(int channel, uint8 byte, bool eoi)
651 {
652 switch (chan_mode[channel]) {
653 case CHMOD_FREE:
654 set_error(ERR_FILENOTOPEN);
655 break;
656
657 case CHMOD_COMMAND:
658 // Collect characters and execute command on EOI
659 if (cmd_len >= 40)
660 return ST_TIMEOUT;
661
662 cmd_buffer[cmd_len++] = byte;
663
664 if (eoi) {
665 cmd_buffer[cmd_len++] = 0;
666 cmd_len = 0;
667 execute_command(cmd_buffer);
668 }
669 return ST_OK;
670
671 case CHMOD_DIRECTORY:
672 set_error(ERR_WRITEFILEOPEN);
673 break;
674 }
675 return ST_TIMEOUT;
676 }
677
678
679 /*
680 * Execute command string
681 */
682
683 void D64Drive::execute_command(char *command)
684 {
685 uint16 adr;
686 int len;
687
688 switch (command[0]) {
689 case 'B':
690 if (command[1] != '-')
691 set_error(ERR_SYNTAX30);
692 else
693 switch (command[2]) {
694 case 'R':
695 block_read_cmd(&command[3]);
696 break;
697
698 case 'P':
699 buffer_ptr_cmd(&command[3]);
700 break;
701
702 case 'A':
703 case 'F':
704 case 'W':
705 set_error(ERR_WRITEPROTECT);
706 break;
707
708 default:
709 set_error(ERR_SYNTAX30);
710 break;
711 }
712 break;
713
714 case 'M':
715 if (command[1] != '-')
716 set_error(ERR_SYNTAX30);
717 else
718 switch (command[2]) {
719 case 'R':
720 adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
721 error_ptr = (char *)(ram + (adr & 0x07ff));
722 if (!(error_len = (uint8)command[5]))
723 error_len = 1;
724 break;
725
726 case 'W':
727 adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
728 len = (uint8)command[5];
729 for (int i=0; i<len; i++)
730 ram[adr+i] = (uint8)command[i+6];
731 break;
732
733 default:
734 set_error(ERR_SYNTAX30);
735 }
736 break;
737
738 case 'I':
739 close_all_channels();
740 read_sector(18, 0, (uint8 *)bam);
741 set_error(ERR_OK);
742 break;
743
744 case 'U':
745 switch (command[1] & 0x0f) {
746 case 1: // U1/UA: Block-Read
747 block_read_cmd(&command[2]);
748 break;
749
750 case 2: // U2/UB: Block-Write
751 set_error(ERR_WRITEPROTECT);
752 break;
753
754 case 10: // U:/UJ: Reset
755 Reset();
756 break;
757
758 default:
759 set_error(ERR_SYNTAX30);
760 break;
761 }
762 break;
763
764 case 'G':
765 if (command[1] != ':')
766 set_error(ERR_SYNTAX30);
767 else
768 chd64_cmd(&command[2]);
769 break;
770
771 case 'C':
772 case 'N':
773 case 'R':
774 case 'S':
775 case 'V':
776 set_error(ERR_WRITEPROTECT);
777 break;
778
779 default:
780 set_error(ERR_SYNTAX30);
781 break;
782 }
783 }
784
785
786 /*
787 * Execute B-R command
788 */
789
790 void D64Drive::block_read_cmd(char *command)
791 {
792 int channel, drvnum, track, sector;
793
794 if (parse_bcmd(command, &channel, &drvnum, &track, &sector)) {
795 if (chan_mode[channel] == CHMOD_DIRECT) {
796 read_sector(track, sector, buf_ptr[channel] = chan_buf[channel]);
797 buf_len[channel] = 256;
798 set_error(ERR_OK);
799 } else
800 set_error(ERR_NOCHANNEL);
801 } else
802 set_error(ERR_SYNTAX30);
803 }
804
805
806 /*
807 * Execute B-P command
808 */
809
810 void D64Drive::buffer_ptr_cmd(char *command)
811 {
812 int channel, pointer, i;
813
814 if (parse_bcmd(command, &channel, &pointer, &i, &i)) {
815 if (chan_mode[channel] == CHMOD_DIRECT) {
816 buf_ptr[channel] = chan_buf[channel] + pointer;
817 buf_len[channel] = 256 - pointer;
818 set_error(ERR_OK);
819 } else
820 set_error(ERR_NOCHANNEL);
821 } else
822 set_error(ERR_SYNTAX30);
823 }
824
825
826 /*
827 * Parse block command parameters
828 * true: OK, false: error
829 */
830
831 bool D64Drive::parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4)
832 {
833 int i;
834
835 if (*cmd == ':') cmd++;
836
837 // Read four parameters separated by space, cursor right or comma
838 while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
839 if (!*cmd) return false;
840
841 i = 0;
842 while (*cmd >= 0x30 && *cmd < 0x40) {
843 i *= 10;
844 i += *cmd++ & 0x0f;
845 }
846 *arg1 = i & 0xff;
847
848 while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
849 if (!*cmd) return false;
850
851 i = 0;
852 while (*cmd >= 0x30 && *cmd < 0x40) {
853 i *= 10;
854 i += *cmd++ & 0x0f;
855 }
856 *arg2 = i & 0xff;
857
858 while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
859 if (!*cmd) return false;
860
861 i = 0;
862 while (*cmd >= 0x30 && *cmd < 0x40) {
863 i *= 10;
864 i += *cmd++ & 0x0f;
865 }
866 *arg3 = i & 0xff;
867
868 while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
869 if (!*cmd) return false;
870
871 i = 0;
872 while (*cmd >= 0x30 && *cmd < 0x40) {
873 i *= 10;
874 i += *cmd++ & 0x0f;
875 }
876 *arg4 = i & 0xff;
877
878 return true;
879 }
880
881
882 /*
883 * Execute 'G' command
884 */
885
886 void D64Drive::chd64_cmd(char *d64name)
887 {
888 char str[NAMEBUF_LENGTH];
889 char *p = str;
890
891 // Convert .d64 file name
892 for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*d64name++, false)); i++) ;
893
894 close_all_channels();
895
896 // G:. resets the .d64 file name to its original setting
897 if (str[0] == '.' && str[1] == 0)
898 open_close_d64_file(orig_d64_name);
899 else
900 open_close_d64_file(str);
901
902 // Read BAM
903 read_sector(18, 0, (uint8 *)bam);
904 }
905
906
907 /*
908 * Reset drive
909 */
910
911 void D64Drive::Reset(void)
912 {
913 close_all_channels();
914
915 read_sector(18, 0, (uint8 *)bam);
916
917 cmd_len = 0;
918 for (int i=0; i<4; i++)
919 buf_free[i] = true;
920
921 set_error(ERR_STARTUP);
922 }
923
924
925 /*
926 * Allocate floppy buffer
927 * -> Desired buffer number or -1
928 * <- Allocated buffer number or -1
929 */
930
931 int D64Drive::alloc_buffer(int want)
932 {
933 if (want == -1) {
934 for (want=3; want>=0; want--)
935 if (buf_free[want]) {
936 buf_free[want] = false;
937 return want;
938 }
939 return -1;
940 }
941
942 if (want < 4)
943 if (buf_free[want]) {
944 buf_free[want] = false;
945 return want;
946 } else
947 return -1;
948 else
949 return -1;
950 }
951
952
953 /*
954 * Free floppy buffer
955 */
956
957 void D64Drive::free_buffer(int buf)
958 {
959 buf_free[buf] = true;
960 }
961
962
963 /*
964 * Read sector (256 bytes)
965 * true: success, false: error
966 */
967
968 bool D64Drive::read_sector(int track, int sector, uint8 *buffer)
969 {
970 int offset;
971
972 // Convert track/sector to byte offset in file
973 if ((offset = offset_from_ts(track, sector)) < 0) {
974 set_error(ERR_ILLEGALTS);
975 return false;
976 }
977
978 if (the_file == NULL) {
979 set_error(ERR_NOTREADY);
980 return false;
981 }
982
983 #ifdef AMIGA
984 if (offset != ftell(the_file))
985 fseek(the_file, offset + image_header, SEEK_SET);
986 #else
987 fseek(the_file, offset + image_header, SEEK_SET);
988 #endif
989 fread(buffer, 256, 1, the_file);
990 return true;
991 }
992
993
994 /*
995 * Convert track/sector to offset
996 */
997
998 const int num_sectors[41] = {
999 0,
1000 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
1001 19,19,19,19,19,19,19,
1002 18,18,18,18,18,18,
1003 17,17,17,17,17,
1004 17,17,17,17,17 // Tracks 36..40
1005 };
1006
1007 const int sector_offset[41] = {
1008 0,
1009 0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
1010 357,376,395,414,433,452,471,
1011 490,508,526,544,562,580,
1012 598,615,632,649,666,
1013 683,700,717,734,751 // Tracks 36..40
1014 };
1015
1016 int D64Drive::offset_from_ts(int track, int sector)
1017 {
1018 if ((track < 1) || (track > NUM_TRACKS) ||
1019 (sector < 0) || (sector >= num_sectors[track]))
1020 return -1;
1021
1022 return (sector_offset[track] + sector) << 8;
1023 }
1024
1025
1026 /*
1027 * Conversion PETSCII->ASCII
1028 */
1029
1030 uint8 D64Drive::conv_from_64(uint8 c, bool map_slash)
1031 {
1032 if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
1033 return c ^ 0x20;
1034 if ((c >= 0xc1) && (c <= 0xda))
1035 return c ^ 0x80;
1036 if ((c == '/') && map_slash && ThePrefs.MapSlash)
1037 return '\\';
1038 return c;
1039 }