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