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 (19 years ago) by cebix
Branch: MAIN
Changes since 1.3: +122 -350 lines
Log Message:
some cleanups and refactoring

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     // 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 cebix 1.3 ram = new uint8[DRIVE_RAM_SIZE];
78 cebix 1.1 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 cebix 1.4 uint8 D64Drive::Open(int channel, const uint8 *name, int name_len)
154 cebix 1.1 {
155     set_error(ERR_OK);
156    
157     // Channel 15: execute file name as command
158     if (channel == 15) {
159 cebix 1.4 execute_cmd(name, name_len);
160 cebix 1.1 return ST_OK;
161     }
162    
163     if (chan_mode[channel] != CHMOD_FREE) {
164     set_error(ERR_NOCHANNEL);
165     return ST_OK;
166     }
167    
168 cebix 1.4 if (name[0] == '$')
169 cebix 1.1 if (channel)
170     return open_file_ts(channel, 18, 0);
171     else
172 cebix 1.4 return open_directory(name + 1, name_len - 1);
173 cebix 1.1
174 cebix 1.4 if (name[0] == '#')
175     return open_direct(channel, name);
176 cebix 1.1
177 cebix 1.4 return open_file(channel, name, name_len);
178 cebix 1.1 }
179    
180    
181     /*
182     * Open file
183     */
184    
185 cebix 1.4 uint8 D64Drive::open_file(int channel, const uint8 *name, int name_len)
186 cebix 1.1 {
187 cebix 1.4 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 cebix 1.1 }
202    
203     // Allow only read accesses
204 cebix 1.4 if (mode != FMODE_READ) {
205 cebix 1.1 set_error(ERR_WRITEPROTECT);
206     return ST_OK;
207     }
208    
209 cebix 1.4 // Relative files are not supported
210     if (type == FTYPE_REL) {
211     set_error(ERR_UNIMPLEMENTED);
212     return ST_OK;
213     }
214    
215 cebix 1.1 // Find file in directory and open it
216 cebix 1.4 int track, sector;
217     if (find_file(plain_name, &track, &sector))
218 cebix 1.1 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 cebix 1.4 bool D64Drive::find_file(const uint8 *pattern, int *track, int *sector)
232 cebix 1.1 {
233     int i, j;
234 cebix 1.4 const uint8 *p, *q;
235 cebix 1.1 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 cebix 1.4 p = pattern;
253 cebix 1.1 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 cebix 1.4 uint8 D64Drive::open_directory(const uint8 *pattern, int pattern_len)
318 cebix 1.1 {
319     // Special treatment for "$0"
320 cebix 1.4 if (pattern[0] == '0' && pattern[1] == 0) {
321     pattern++;
322     pattern_len--;
323     }
324 cebix 1.1
325     // Skip everything before the ':' in the pattern
326 cebix 1.4 uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len);
327     if (t) {
328     t++;
329     pattern_len -= t - pattern;
330     pattern = t;
331     }
332 cebix 1.1
333     chan_mode[0] = CHMOD_DIRECTORY;
334 cebix 1.4 uint8 *p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
335 cebix 1.1
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 cebix 1.4 uint8 *q = bam->disk_name;
347     for (int i=0; i<23; i++) {
348     int c;
349 cebix 1.1 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 cebix 1.4 for (int j=0; j<8; j++) {
367     DirEntry *de = &dir.entry[j];
368 cebix 1.1
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 cebix 1.4 int n = (de->num_blocks_h << 8) + de->num_blocks_l;
378 cebix 1.1 if (n<10) *p++ = ' ';
379     if (n<100) *p++ = ' ';
380    
381     *p++ = '\"';
382     q = de->name;
383 cebix 1.4 uint8 c;
384     int m = 0;
385     for (int i=0; i<16; i++) {
386 cebix 1.1 if ((c = *q++) == 0xa0) {
387     if (m)
388 cebix 1.4 *p++ = ' '; // Replace all 0xa0 by spaces
389 cebix 1.1 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 cebix 1.4 // Open files are marked by '*'
400 cebix 1.1 if (de->type & 0x80)
401     *p++ = ' ';
402     else
403     *p++ = '*';
404    
405 cebix 1.4 // File type
406 cebix 1.1 *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 cebix 1.4 // Protected files are marked by '<'
411 cebix 1.1 if (de->type & 0x40)
412     *p++ = '<';
413     else
414     *p++ = ' ';
415    
416 cebix 1.4 // Appropriate number of spaces at the end
417 cebix 1.1 *p++ = ' ';
418     if (n >= 10) *p++ = ' ';
419     if (n >= 100) *p++ = ' ';
420     *p++ = 0;
421     }
422     }
423     }
424    
425 cebix 1.4 // 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 cebix 1.1
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 cebix 1.4 memset(p, ' ', 13);
451     p += 13;
452    
453 cebix 1.1 *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 cebix 1.4 uint8 D64Drive::open_direct(int channel, const uint8 *name)
468 cebix 1.1 {
469     int buf = -1;
470    
471 cebix 1.4 if (name[1] == 0)
472 cebix 1.1 buf = alloc_buffer(-1);
473     else
474 cebix 1.4 if ((name[1] >= '0') && (name[1] <= '3') && (name[2] == 0))
475     buf = alloc_buffer(name[1] - '0');
476 cebix 1.1
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 cebix 1.4 if (cmd_len >= 58)
608 cebix 1.1 return ST_TIMEOUT;
609    
610 cebix 1.4 cmd_buf[cmd_len++] = byte;
611 cebix 1.1
612     if (eoi) {
613 cebix 1.4 execute_cmd(cmd_buf, cmd_len);
614 cebix 1.1 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 cebix 1.4 // BLOCK-READ:channel,0,track,sector
631     void D64Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
632 cebix 1.1 {
633 cebix 1.4 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 cebix 1.1 }
645     }
646    
647 cebix 1.4 // BUFFER-POINTER:channel,pos
648     void D64Drive::buffer_pointer_cmd(int channel, int pos)
649 cebix 1.1 {
650 cebix 1.4 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 cebix 1.1 }
657    
658 cebix 1.4 // M-R<adr low><adr high>[<number>]
659     void D64Drive::mem_read_cmd(uint16 adr, uint8 len)
660 cebix 1.1 {
661 cebix 1.4 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 cebix 1.1 }
671    
672 cebix 1.4 // M-W<adr low><adr high><number><data...>
673     void D64Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
674 cebix 1.1 {
675 cebix 1.4 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 cebix 1.1 }
685     }
686    
687 cebix 1.4 // INITIALIZE
688     void D64Drive::initialize_cmd(void)
689 cebix 1.1 {
690 cebix 1.4 // Close all channels and re-read BAM
691 cebix 1.1 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     }