ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541d64.cpp
Revision: 1.3
Committed: 2003-07-02T14:46:24Z (19 years, 7 months ago) by cebix
Branch: MAIN
Changes since 1.2: +2 -1 lines
Log Message:
when the ROM files are not found, builtin defaults are used

File Contents

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