ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541d64.cpp
(Generate patch)

Comparing Frodo4/Src/1541d64.cpp (file contents):
Revision 1.4 by cebix, 2004-01-11T00:09:51Z vs.
Revision 1.11 by cebix, 2010-04-21T19:52:53Z

# Line 1 | Line 1
1   /*
2 < *  1541d64.cpp - 1541 emulation in .d64 file
2 > *  1541d64.cpp - 1541 emulation in disk image files (.d64/.x64/zipcode)
3   *
4 < *  Frodo (C) 1994-1997,2002-2003 Christian Bauer
4 > *  Frodo Copyright (C) Christian Bauer
5 > *  zipcode decoding routines (C) 1993-1997 Marko Mäkelä, Paul David Doherty
6   *
7   *  This program is free software; you can redistribute it and/or modify
8   *  it under the terms of the GNU General Public License as published by
# Line 19 | Line 20
20   */
21  
22   /*
23 < * Incompatibilities:
24 < * ------------------
25 < *
26 < *  - Only read accesses possible
26 < *  - Not all commands implemented
27 < *  - The .d64 error info is read, but unused
23 > *  Incompatibilities:
24 > *   - No support for relative files
25 > *   - Unimplemented commands: P
26 > *   - Impossible to implement: B-E, M-E
27   */
28  
29   #include "sysdeps.h"
# Line 33 | Line 32
32   #include "IEC.h"
33   #include "Prefs.h"
34   #include "C64.h"
35 + #include "main.h"
36 +
37 + #define DEBUG 0
38 + #include "debug.h"
39  
40  
41   // Channel modes (IRC users listen up :-)
42   enum {
43          CHMOD_FREE,                     // Channel free
44          CHMOD_COMMAND,          // Command/error channel
45 <        CHMOD_DIRECTORY,        // Reading directory
46 <        CHMOD_FILE,                     // Sequential file open
47 <        CHMOD_DIRECT            // Direct buffer access ('#')
45 >        CHMOD_DIRECTORY,        // Reading directory, using large allocated buffer
46 >        CHMOD_FILE,                     // Sequential file open, using buffer in 1541 RAM
47 >        CHMOD_REL,                      // Relative file open, using buffer in 1541 RAM
48 >        CHMOD_DIRECT            // Direct buffer access ('#'), using buffer in 1541 RAM
49 > };
50 >
51 > // Directory track
52 > const int DIR_TRACK = 18;
53 >
54 > // BAM structure
55 > enum {
56 >        BAM_DIR_TRACK = 0,              // Track...
57 >        BAM_DIR_SECTOR = 1,             // ...and sector of first directory block (unused)
58 >        BAM_FMT_TYPE = 2,               // Format type
59 >        BAM_BITMAP = 4,                 // Sector allocation map
60 >        BAM_DISK_NAME = 144,    // Disk name
61 >        BAM_DISK_ID = 162,              // Disk ID
62 >        BAM_FMT_CHAR = 165              // Format characters
63 > };
64 >
65 > // Directory structure
66 > enum {
67 >        DIR_NEXT_TRACK = 0,             // Track...
68 >        DIR_NEXT_SECTOR = 1,    // ... and sector of next directory block
69 >        DIR_ENTRIES = 2,                // Start of directory entries (8)
70 >
71 >        DE_TYPE = 0,                    // File type/flags
72 >        DE_TRACK = 1,                   // Track...
73 >        DE_SECTOR = 2,                  // ...and sector of first data block
74 >        DE_NAME = 3,                    // File name
75 >        DE_SIDE_TRACK = 19,             // Track...
76 >        DE_SIDE_SECTOR = 20,    // ...and sector of first side sector
77 >        DE_REC_LEN = 21,                // Record length
78 >        DE_OVR_TRACK = 26,              // Track...
79 >        DE_OVR_SECTOR = 27,             // ...and sector on overwrite (@)
80 >        DE_NUM_BLOCKS_L = 28,   // Number of blocks, LSB
81 >        DE_NUM_BLOCKS_H = 29,   // Number of blocks, MSB
82 >
83 >        SIZEOF_DE = 32                  // Size of directory entry
84   };
85  
86 < // Number of tracks/sectors
87 < const int NUM_TRACKS = 35;
88 < const int NUM_SECTORS = 683;
86 > // Interleave of directory and data blocks
87 > const int DIR_INTERLEAVE = 3;
88 > const int DATA_INTERLEAVE = 10;
89 >
90 > // Number of sectors per track, for all tracks
91 > const int num_sectors[41] = {
92 >        0,
93 >        21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
94 >        19,19,19,19,19,19,19,
95 >        18,18,18,18,18,18,
96 >        17,17,17,17,17,
97 >        17,17,17,17,17          // Tracks 36..40
98 > };
99 >
100 > // Accumulated number of sectors
101 > const int accum_num_sectors[41] = {
102 >        0,
103 >        0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
104 >        357,376,395,414,433,452,471,
105 >        490,508,526,544,562,580,
106 >        598,615,632,649,666,
107 >        683,700,717,734,751     // Tracks 36..40
108 > };
109  
110   // Prototypes
111 < static bool match(uint8 *p, uint8 *n);
111 > static bool match(const uint8 *p, int p_len, const uint8 *n);
112 > static FILE *open_image_file(const char *path, bool write_mode);
113 > static bool parse_image_file(FILE *f, image_file_desc &desc);
114  
115  
116   /*
117 < *  Constructor: Prepare emulation, open .d64 file
117 > *  Constructor: Prepare emulation, open image file
118   */
119  
120 < D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec)
120 > ImageDrive::ImageDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL), bam(ram + 0x700), bam_dirty(false)
121   {
122 <        the_file = NULL;
123 <        ram = NULL;
122 >        desc.type = TYPE_D64;
123 >        desc.header_size = 0;
124 >        desc.num_tracks = 35;
125 >        desc.id1 = desc.id2 = 0;
126 >        desc.has_error_info = false;
127  
128 <        Ready = false;
129 <        strcpy(orig_d64_name, filepath);
130 <        for (int i=0; i<=14; i++) {
67 <                chan_mode[i] = CHMOD_FREE;
68 <                chan_buf[i] = NULL;
128 >        for (int i=0; i<18; i++) {
129 >                ch[i].mode = CHMOD_FREE;
130 >                ch[i].buf = NULL;
131          }
132 <        chan_mode[15] = CHMOD_COMMAND;
71 <
72 <        // Open .d64 file
73 <        open_close_d64_file(filepath);
74 <        if (the_file != NULL) {
132 >        ch[15].mode = CHMOD_COMMAND;
133  
134 <                // Allocate 1541 RAM
77 <                ram = new uint8[DRIVE_RAM_SIZE];
78 <                bam = (BAM *)(ram + 0x700);
134 >        Reset();
135  
136 <                Reset();
136 >        // Open image file
137 >        if (change_image(filepath))
138                  Ready = true;
82        }
139   }
140  
141  
# Line 87 | Line 143 | D64Drive::D64Drive(IEC *iec, char *filep
143   *  Destructor
144   */
145  
146 < D64Drive::~D64Drive()
146 > ImageDrive::~ImageDrive()
147   {
148 <        // Close .d64 file
93 <        open_close_d64_file("");
94 <
95 <        delete[] ram;
96 <        Ready = false;
148 >        close_image();
149   }
150  
151  
152   /*
153 < *  Open/close the .d64 file
153 > *  Close the image file
154   */
155  
156 < void D64Drive::open_close_d64_file(char *d64name)
156 > void ImageDrive::close_image(void)
157   {
158 <        long size;
107 <        uint8 magic[4];
108 <
109 <        // Close old .d64, if open
110 <        if (the_file != NULL) {
158 >        if (the_file) {
159                  close_all_channels();
160 +                if (bam_dirty) {
161 +                        write_sector(DIR_TRACK, 0, bam);
162 +                        bam_dirty = false;
163 +                }
164                  fclose(the_file);
165                  the_file = NULL;
166          }
167 + }
168  
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                        }
169  
170 <                        // x64 image?
171 <                        rewind(the_file);
172 <                        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;
170 > /*
171 > *  Open the image file
172 > */
173  
174 <                        // Preset error info (all sectors no error)
175 <                        memset(error_info, 1, NUM_SECTORS);
174 > bool ImageDrive::change_image(const char *path)
175 > {
176 >        // Close old image file
177 >        close_image();
178  
179 <                        // Load sector error info from .d64 file, if present
180 <                        if (!image_header && size == NUM_SECTORS * 257) {
181 <                                fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
182 <                                fread(&error_info, NUM_SECTORS, 1, the_file);
183 <                        }
184 <                }
179 >        // Open new image file (try write access first, then read-only)
180 >        write_protected = false;
181 >        the_file = open_image_file(path, true);
182 >        if (the_file == NULL) {
183 >                write_protected = true;
184 >                the_file = open_image_file(path, false);
185          }
186 +        if (the_file) {
187 +
188 +                // Determine file type and fill in image_file_desc structure
189 +                if (!parse_image_file(the_file, desc)) {
190 +                        fclose(the_file);
191 +                        the_file = false;
192 +                        return false;
193 +                }
194 +
195 +                // Read BAM
196 +                read_sector(DIR_TRACK, 0, bam);
197 +                bam_dirty = false;
198 +                return true;
199 +        } else
200 +                return false;
201   }
202  
203  
# Line 150 | Line 205 | void D64Drive::open_close_d64_file(char
205   *  Open channel
206   */
207  
208 < uint8 D64Drive::Open(int channel, const uint8 *name, int name_len)
208 > uint8 ImageDrive::Open(int channel, const uint8 *name, int name_len)
209   {
210 +        D(bug("ImageDrive::Open channel %d, file %s\n", channel, name));
211 +
212          set_error(ERR_OK);
213  
214          // Channel 15: execute file name as command
# Line 160 | Line 217 | uint8 D64Drive::Open(int channel, const
217                  return ST_OK;
218          }
219  
220 <        if (chan_mode[channel] != CHMOD_FREE) {
220 >        if (ch[channel].mode != CHMOD_FREE) {
221                  set_error(ERR_NOCHANNEL);
222                  return ST_OK;
223          }
224  
225          if (name[0] == '$')
226                  if (channel)
227 <                        return open_file_ts(channel, 18, 0);
227 >                        return open_file_ts(channel, DIR_TRACK, 0);
228                  else
229                          return open_directory(name + 1, name_len - 1);
230  
# Line 182 | Line 239 | uint8 D64Drive::Open(int channel, const
239   *  Open file
240   */
241  
242 < uint8 D64Drive::open_file(int channel, const uint8 *name, int name_len)
242 > uint8 ImageDrive::open_file(int channel, const uint8 *name, int name_len)
243   {
244 <        uint8 plain_name[256];
244 >        uint8 plain_name[NAMEBUF_LENGTH];
245          int plain_name_len;
246          int mode = FMODE_READ;
247 <        int type = FTYPE_PRG;
247 >        int type = FTYPE_DEL;
248          int rec_len = 0;
249          parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len);
250          if (plain_name_len > 16)
251                  plain_name_len = 16;
252  
253 +        D(bug(" plain name %s, type %d, mode %d\n", plain_name, type, mode));
254 +
255          // Channel 0 is READ, channel 1 is WRITE
256          if (channel == 0 || channel == 1) {
257                  mode = channel ? FMODE_WRITE : FMODE_READ;
# Line 200 | Line 259 | uint8 D64Drive::open_file(int channel, c
259                          type = FTYPE_PRG;
260          }
261  
262 <        // Allow only read accesses
263 <        if (mode != FMODE_READ) {
262 >        ch[channel].writing = (mode == FMODE_WRITE || mode == FMODE_APPEND);
263 >
264 >        // Wildcards are only allowed on reading
265 >        if (ch[channel].writing && (strchr((const char *)plain_name, '*') || strchr((const char *)plain_name, '?'))) {
266 >                set_error(ERR_SYNTAX33);
267 >                return ST_OK;
268 >        }
269 >
270 >        // Check for write-protection if writing
271 >        if (ch[channel].writing && write_protected) {
272                  set_error(ERR_WRITEPROTECT);
273                  return ST_OK;
274          }
# Line 212 | Line 279 | uint8 D64Drive::open_file(int channel, c
279                  return ST_OK;
280          }
281  
282 <        // Find file in directory and open it
283 <        int track, sector;
284 <        if (find_file(plain_name, &track, &sector))
285 <                return open_file_ts(channel, track, sector);
286 <        else
287 <                set_error(ERR_FILENOTFOUND);
282 >        // Find file in directory
283 >        int dir_track, dir_sector, entry;
284 >        if (find_first_file(plain_name, plain_name_len, dir_track, dir_sector, entry)) {
285 >
286 >                // File exists
287 >                D(bug(" file exists, dir track %d, sector %d, entry %d\n", dir_track, dir_sector, entry));
288 >                ch[channel].dir_track = dir_track;
289 >                ch[channel].dir_sector = dir_sector;
290 >                ch[channel].entry = entry;
291 >                uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
292  
293 <        return ST_OK;
294 < }
293 >                // Get file type from existing file if not specified in file name
294 >                if (type == FTYPE_DEL)
295 >                        type = de[DE_TYPE] & 7;
296  
297 +                if ((de[DE_TYPE] & 7) != type) {
298  
299 < /*
300 < *  Search file in directory, find first track and sector
228 < *  false: not found, true: found
229 < */
299 >                        // File type doesn't match
300 >                        set_error(ERR_FILETYPE);
301  
302 < bool D64Drive::find_file(const uint8 *pattern, int *track, int *sector)
232 < {
233 <        int i, j;
234 <        const uint8 *p, *q;
235 <        DirEntry *de;
302 >                } else if (mode == FMODE_WRITE) {
303  
304 <        // Scan all directory blocks
238 <        dir.next_track = bam->dir_track;
239 <        dir.next_sector = bam->dir_sector;
304 >                        if (name[0] == '@') {
305  
306 <        while (dir.next_track) {
307 <                if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
243 <                        return false;
306 >                                // Open old file for overwriting (save-replace)
307 >                                return create_file(channel, plain_name, plain_name_len, type, true);
308  
309 <                // 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 <                                }
309 >                        } else {
310  
311 <                                if (i == 16 || *q == 0xa0)
312 <                                        return true;
311 >                                // File to be written already exists, error
312 >                                set_error(ERR_FILEEXISTS);
313                          }
314 < next_entry: ;
314 >
315 >                } else if (mode == FMODE_APPEND) {
316 >
317 >                        // Open old file for appending
318 >                        open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]);
319 >
320 >                        // Seek to end of file
321 >                        int track = 0, sector = 0, num_blocks = 0;
322 >                        while (ch[channel].buf[0]) {
323 >                                if (!read_sector(track = ch[channel].buf[0], sector = ch[channel].buf[1], ch[channel].buf))
324 >                                        return ST_OK;
325 >                                num_blocks++;
326 >                        }
327 >
328 >                        // Change channel mode to writing, adjust buffer pointer
329 >                        ch[channel].writing = true;
330 >                        ch[channel].buf_len = ch[channel].buf[1] + 1;
331 >                        ch[channel].buf_ptr = ch[channel].buf + ch[channel].buf_len;
332 >                        ch[channel].track = track;
333 >                        ch[channel].sector = sector;
334 >                        ch[channel].num_blocks = num_blocks;
335 >
336 >                } else if (mode == FMODE_M) {
337 >
338 >                        // Open old file for reading, even if it is not closed
339 >                        return open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]);
340 >
341 >                } else {
342 >
343 >                        // Open old file for reading, error if file is open
344 >                        if (de[DE_TYPE] & 0x80)
345 >                                return open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]);
346 >                        else
347 >                                set_error(ERR_WRITEFILEOPEN);
348                  }
268        }
349  
350 <        return false;
350 >        } else {
351 >
352 >                // File doesn't exist
353 >                D(bug(" file not found\n"));
354 >
355 >                // Set file type to SEQ if not specified in file name
356 >                if (type == FTYPE_DEL)
357 >                        type = FTYPE_SEQ;
358 >
359 >                if (mode == FMODE_WRITE) {
360 >
361 >                        // Create new file for writing
362 >                        return create_file(channel, plain_name, plain_name_len, type);
363 >
364 >                } else
365 >                        set_error(ERR_FILENOTFOUND);
366 >        }
367 >        return ST_OK;
368   }
369  
370  
371   /*
372 < *  Open file given track/sector of first block
372 > *  Open channel for reading from file given track/sector of first block
373   */
374  
375 < uint8 D64Drive::open_file_ts(int channel, int track, int sector)
375 > uint8 ImageDrive::open_file_ts(int channel, int track, int sector)
376   {
377 <        chan_buf[channel] = new uint8[256];
378 <        chan_mode[channel] = CHMOD_FILE;
377 >        D(bug("open_file_ts track %d, sector %d\n", track, sector));
378 >
379 >        // Allocate buffer and set channel mode
380 >        int buf = alloc_buffer(-1);
381 >        if (buf == -1) {
382 >                set_error(ERR_NOCHANNEL);
383 >                return ST_OK;
384 >        }
385 >        ch[channel].buf_num = buf;
386 >        ch[channel].buf = ram + 0x300 + buf * 0x100;
387 >        ch[channel].mode = CHMOD_FILE;
388  
389          // On the next call to Read, the first block will be read
390 <        chan_buf[channel][0] = track;
391 <        chan_buf[channel][1] = sector;
392 <        buf_len[channel] = 0;
390 >        ch[channel].buf[0] = track;
391 >        ch[channel].buf[1] = sector;
392 >        ch[channel].buf_len = 0;
393  
394          return ST_OK;
395   }
396  
397  
398   /*
399 < *  Prepare directory as BASIC program (channel 0)
399 > *  Create file and open channel for writing to file
400   */
401  
402 < 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)
402 > uint8 ImageDrive::create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite)
403   {
404 <        if (!*p)                // Null pattern matches everything
304 <                return true;
404 >        D(bug("create_file %s, type %d\n", name, type));
405  
406 <        do {
407 <                if (*p == '*')  // Wildcard '*' matches all following characters
408 <                        return true;
409 <                if ((*p != *n) && (*p != '?'))  // Wildcard '?' matches single character
410 <                        return false;
411 <                p++; n++;
412 <        } while (*p);
406 >        // Allocate buffer
407 >        int buf = alloc_buffer(-1);
408 >        if (buf == -1) {
409 >                set_error(ERR_NOCHANNEL);
410 >                return ST_OK;
411 >        }
412 >        ch[channel].buf_num = buf;
413 >        ch[channel].buf = ram + 0x300 + buf * 0x100;
414  
415 <        return *n == 0xa0;
415 >        // Allocate new directory entry if not overwriting
416 >        if (!overwrite) {
417 >                if (!alloc_dir_entry(ch[channel].dir_track, ch[channel].dir_sector, ch[channel].entry)) {
418 >                        free_buffer(buf);
419 >                        return ST_OK;
420 >                }
421 >        }
422 >        uint8 *de = dir + DIR_ENTRIES + ch[channel].entry * SIZEOF_DE;
423 >
424 >        // Allocate first data block
425 >        ch[channel].track = DIR_TRACK - 1;
426 >        ch[channel].sector = -DATA_INTERLEAVE;
427 >        if (!alloc_next_block(ch[channel].track, ch[channel].sector, DATA_INTERLEAVE)) {
428 >                free_buffer(buf);
429 >                return ST_OK;
430 >        }
431 >        ch[channel].num_blocks = 1;
432 >        D(bug(" first data block on track %d, sector %d\n", ch[channel].track, ch[channel].sector));
433 >
434 >        // Write directory entry
435 >        memset(de, 0, SIZEOF_DE);
436 >        de[DE_TYPE] = type;             // bit 7 not set -> open file
437 >        if (overwrite) {
438 >                de[DE_OVR_TRACK] = ch[channel].track;
439 >                de[DE_OVR_SECTOR] = ch[channel].sector;
440 >        } else {
441 >                de[DE_TRACK] = ch[channel].track;
442 >                de[DE_SECTOR] = ch[channel].sector;
443 >        }
444 >        memset(de + DE_NAME, 0xa0, 16);
445 >        memcpy(de + DE_NAME, name, name_len);
446 >        write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir);
447 >
448 >        // Set channel descriptor
449 >        ch[channel].mode = CHMOD_FILE;
450 >        ch[channel].writing = true;
451 >        ch[channel].buf_ptr = ch[channel].buf + 2;
452 >        ch[channel].buf_len = 2;
453 >        return ST_OK;
454   }
455  
456 < uint8 D64Drive::open_directory(const uint8 *pattern, int pattern_len)
456 >
457 > /*
458 > *  Prepare directory as BASIC program (channel 0)
459 > */
460 >
461 > const char type_char_1[] = "DSPUREER";
462 > const char type_char_2[] = "EERSELQG";
463 > const char type_char_3[] = "LQGRL???";
464 >
465 > uint8 ImageDrive::open_directory(const uint8 *pattern, int pattern_len)
466   {
467          // Special treatment for "$0"
468 <        if (pattern[0] == '0' && pattern[1] == 0) {
468 >        if (pattern[0] == '0' && pattern_len == 1) {
469                  pattern++;
470                  pattern_len--;
471          }
# Line 330 | Line 478 | uint8 D64Drive::open_directory(const uin
478                  pattern = t;
479          }
480  
481 <        chan_mode[0] = CHMOD_DIRECTORY;
482 <        uint8 *p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
481 >        ch[0].mode = CHMOD_DIRECTORY;
482 >        uint8 *p = ch[0].buf_ptr = ch[0].buf = new uint8[8192];
483  
484 <        // Create directory title
484 >        // Create directory title with disk name, ID and format type
485          *p++ = 0x01;    // Load address $0401 (from PET days :-)
486          *p++ = 0x04;
487          *p++ = 0x01;    // Dummy line link
# Line 343 | Line 491 | uint8 D64Drive::open_directory(const uin
491          *p++ = 0x12;    // RVS ON
492          *p++ = '\"';
493  
494 <        uint8 *q = bam->disk_name;
494 >        uint8 *q = bam + BAM_DISK_NAME;
495          for (int i=0; i<23; i++) {
496                  int c;
497                  if ((c = *q++) == 0xa0)
# Line 355 | Line 503 | uint8 D64Drive::open_directory(const uin
503          *p++ = 0;
504  
505          // Scan all directory blocks
506 <        dir.next_track = bam->dir_track;
507 <        dir.next_sector = bam->dir_sector;
506 >        dir[DIR_NEXT_TRACK] = DIR_TRACK;
507 >        dir[DIR_NEXT_SECTOR] = 1;
508  
509 <        while (dir.next_track) {
510 <                if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
509 >        int num_dir_blocks = 0;
510 >        while (dir[DIR_NEXT_TRACK] && num_dir_blocks < num_sectors[DIR_TRACK]) {
511 >                if (!read_sector(dir[DIR_NEXT_TRACK], dir[DIR_NEXT_SECTOR], dir))
512                          return ST_OK;
513 +                num_dir_blocks++;
514  
515                  // Scan all 8 entries of a block
516 <                for (int j=0; j<8; j++) {
517 <                        DirEntry *de = &dir.entry[j];
516 >                uint8 *de = dir + DIR_ENTRIES;
517 >                for (int j=0; j<8; j++, de+=SIZEOF_DE) {
518 >                        if (de[DE_TYPE] && (pattern_len == 0 || match(pattern, pattern_len, de + DE_NAME))) {
519  
520 <                        if (de->type && match((uint8 *)pattern, de->name)) {
521 <                                *p++ = 0x01; // Dummy line link
520 >                                // Dummy line link
521 >                                *p++ = 0x01;
522                                  *p++ = 0x01;
523  
524 <                                *p++ = de->num_blocks_l; // Line number
525 <                                *p++ = de->num_blocks_h;
524 >                                // Line number = number of blocks
525 >                                *p++ = de[DE_NUM_BLOCKS_L];
526 >                                *p++ = de[DE_NUM_BLOCKS_H];
527  
528 +                                // Appropriate number of spaces to align file names
529                                  *p++ = ' ';
530 <                                int n = (de->num_blocks_h << 8) + de->num_blocks_l;
530 >                                int n = (de[DE_NUM_BLOCKS_H] << 8) + de[DE_NUM_BLOCKS_L];
531                                  if (n<10) *p++ = ' ';
532                                  if (n<100) *p++ = ' ';
533  
534 +                                // File name enclosed in quotes
535                                  *p++ = '\"';
536 <                                q = de->name;
536 >                                q = de + DE_NAME;
537                                  uint8 c;
538 <                                int m = 0;
538 >                                bool m = false;
539                                  for (int i=0; i<16; i++) {
540                                          if ((c = *q++) == 0xa0) {
541                                                  if (m)
542                                                          *p++ = ' ';                     // Replace all 0xa0 by spaces
543                                                  else
544 <                                                        m = *p++ = '\"';        // But the first by a '"'
544 >                                                        m = (*p++ = '\"');      // But the first by a '"'
545                                          } else
546                                                  *p++ = c;
547                                  }
# Line 397 | Line 551 | uint8 D64Drive::open_directory(const uin
551                                          *p++ = '\"';                    // No 0xa0, then append a space
552  
553                                  // Open files are marked by '*'
554 <                                if (de->type & 0x80)
554 >                                if (de[DE_TYPE] & 0x80)
555                                          *p++ = ' ';
556                                  else
557                                          *p++ = '*';
558  
559                                  // File type
560 <                                *p++ = type_char_1[de->type & 0x0f];
561 <                                *p++ = type_char_2[de->type & 0x0f];
562 <                                *p++ = type_char_3[de->type & 0x0f];
560 >                                *p++ = type_char_1[de[DE_TYPE] & 7];
561 >                                *p++ = type_char_2[de[DE_TYPE] & 7];
562 >                                *p++ = type_char_3[de[DE_TYPE] & 7];
563  
564                                  // Protected files are marked by '<'
565 <                                if (de->type & 0x40)
565 >                                if (de[DE_TYPE] & 0x40)
566                                          *p++ = '<';
567                                  else
568                                          *p++ = ' ';
# Line 424 | Line 578 | uint8 D64Drive::open_directory(const uin
578  
579          // Final line, count number of free blocks
580          int n = 0;
581 <        for (int i=0; i<35; i++) {
582 <                if (i != 17) // exclude directory track
583 <                        n += bam->bitmap[i*4];
581 >        for (int i=1; i<=35; i++) {
582 >                if (i != DIR_TRACK)     // exclude track 18
583 >                        n += num_free_blocks(i);
584          }
585  
586          *p++ = 0x01;            // Dummy line link
# Line 454 | Line 608 | uint8 D64Drive::open_directory(const uin
608          *p++ = 0;
609          *p++ = 0;
610  
611 <        buf_len[0] = p - chan_buf[0];
458 <
611 >        ch[0].buf_len = p - ch[0].buf;
612          return ST_OK;
613   }
614  
# Line 464 | Line 617 | uint8 D64Drive::open_directory(const uin
617   *  Open channel for direct buffer access
618   */
619  
620 < uint8 D64Drive::open_direct(int channel, const uint8 *name)
620 > uint8 ImageDrive::open_direct(int channel, const uint8 *name)
621   {
622          int buf = -1;
623  
# Line 480 | Line 633 | uint8 D64Drive::open_direct(int channel,
633          }
634  
635          // The buffers are in the 1541 RAM at $300 and are 256 bytes each
636 <        chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8);
637 <        chan_mode[channel] = CHMOD_DIRECT;
638 <        chan_buf_num[channel] = buf;
636 >        ch[channel].mode = CHMOD_DIRECT;
637 >        ch[channel].buf = ram + 0x300 + buf * 0x100;
638 >        ch[channel].buf_num = buf;
639  
640          // Store actual buffer number in buffer
641 <        *chan_buf[channel] = buf + '0';
642 <        buf_len[channel] = 1;
641 >        ch[channel].buf[1] = buf + '0';
642 >        ch[channel].buf_len = 1;
643 >        ch[channel].buf_ptr = ch[channel].buf + 1;
644  
645          return ST_OK;
646   }
# Line 496 | Line 650 | uint8 D64Drive::open_direct(int channel,
650   *  Close channel
651   */
652  
653 < uint8 D64Drive::Close(int channel)
653 > uint8 ImageDrive::Close(int channel)
654   {
655 <        if (channel == 15) {
502 <                close_all_channels();
503 <                return ST_OK;
504 <        }
655 >        D(bug("ImageDrive::Close channel %d\n", channel));
656  
657 <        switch (chan_mode[channel]) {
657 >        switch (ch[channel].mode) {
658                  case CHMOD_FREE:
659                          break;
660  
661 +                case CHMOD_COMMAND:
662 +                        close_all_channels();
663 +                        break;
664 +
665                  case CHMOD_DIRECT:
666 <                        free_buffer(chan_buf_num[channel]);
667 <                        chan_buf[channel] = NULL;
668 <                        chan_mode[channel] = CHMOD_FREE;
666 >                        free_buffer(ch[channel].buf_num);
667 >                        ch[channel].buf = NULL;
668 >                        ch[channel].mode = CHMOD_FREE;
669 >                        break;
670 >
671 >                case CHMOD_FILE:
672 >                        if (ch[channel].writing) {
673 >
674 >                                // Current block empty? Then write CR character
675 >                                if (ch[channel].buf_len == 2) {
676 >                                        ch[channel].buf[2] = 0x0d;
677 >                                        ch[channel].buf_len++;
678 >                                }
679 >
680 >                                // Write last data block
681 >                                ch[channel].buf[0] = 0;
682 >                                ch[channel].buf[1] = ch[channel].buf_len - 1;
683 >                                D(bug(" writing last data block\n"));
684 >                                if (!write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf))
685 >                                        goto free;
686 >
687 >                                // Close write file in directory
688 >                                read_sector(ch[channel].dir_track, ch[channel].dir_sector, dir);
689 >                                uint8 *de = dir + DIR_ENTRIES + ch[channel].entry * SIZEOF_DE;
690 >                                de[DE_TYPE] |= 0x80;
691 >                                de[DE_NUM_BLOCKS_L] = ch[channel].num_blocks & 0xff;
692 >                                de[DE_NUM_BLOCKS_H] = ch[channel].num_blocks >> 8;
693 >                                if (de[DE_OVR_TRACK]) {
694 >                                        // Overwriting, free old data blocks and set pointer to new ones
695 >                                        free_block_chain(de[DE_TRACK], de[DE_SECTOR]);
696 >                                        de[DE_TRACK] = de[DE_OVR_TRACK];
697 >                                        de[DE_SECTOR] = de[DE_OVR_SECTOR];
698 >                                        de[DE_OVR_TRACK] = de[DE_OVR_SECTOR] = 0;
699 >                                }
700 >                                write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir);
701 >                                D(bug(" directory entry updated\n"));
702 >                        }
703 > free:           free_buffer(ch[channel].buf_num);
704 >                        ch[channel].buf = NULL;
705 >                        ch[channel].mode = CHMOD_FREE;
706                          break;
707  
708 <                default:
709 <                        delete[] chan_buf[channel];
710 <                        chan_buf[channel] = NULL;
711 <                        chan_mode[channel] = CHMOD_FREE;
708 >                case CHMOD_DIRECTORY:
709 >                        delete[] ch[channel].buf;
710 >                        ch[channel].buf = NULL;
711 >                        ch[channel].mode = CHMOD_FREE;
712                          break;
713          }
714  
# Line 528 | Line 720 | uint8 D64Drive::Close(int channel)
720   *  Close all channels
721   */
722  
723 < void D64Drive::close_all_channels()
723 > void ImageDrive::close_all_channels()
724   {
725          for (int i=0; i<15; i++)
726                  Close(i);
727 +        Close(16);
728 +        Close(17);
729  
730          cmd_len = 0;
731   }
# Line 541 | Line 735 | void D64Drive::close_all_channels()
735   *  Read from channel
736   */
737  
738 < uint8 D64Drive::Read(int channel, uint8 *byte)
738 > uint8 ImageDrive::Read(int channel, uint8 &byte)
739   {
740 <        switch (chan_mode[channel]) {
740 > //      D(bug("ImageDrive::Read channel %d\n", channel));
741 >
742 >        switch (ch[channel].mode) {
743 >                case CHMOD_FREE:
744 >                        if (current_error == ERR_OK)
745 >                                set_error(ERR_FILENOTOPEN);
746 >                        break;
747 >
748                  case CHMOD_COMMAND:
749 <                        *byte = *error_ptr++;
749 >                        // Read error channel
750 >                        byte = *error_ptr++;
751                          if (--error_len)
752                                  return ST_OK;
753                          else {
# Line 555 | Line 757 | uint8 D64Drive::Read(int channel, uint8
757                          break;
758  
759                  case CHMOD_FILE:
760 +                        if (ch[channel].writing)
761 +                                return ST_READ_TIMEOUT;
762 +                        if (current_error != ERR_OK)
763 +                                return ST_READ_TIMEOUT;
764 +
765                          // Read next block if necessary
766 <                        if (chan_buf[channel][0] && !buf_len[channel]) {
767 <                                if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel]))
766 >                        if (ch[channel].buf_len == 0 && ch[channel].buf[0]) {
767 >                                D(bug(" reading next data block track %d, sector %d\n", ch[channel].buf[0], ch[channel].buf[1]));
768 >                                if (!read_sector(ch[channel].buf[0], ch[channel].buf[1], ch[channel].buf))
769                                          return ST_READ_TIMEOUT;
770 <                                buf_ptr[channel] = chan_buf[channel] + 2;
770 >                                ch[channel].buf_ptr = ch[channel].buf + 2;
771  
772                                  // Determine block length
773 <                                buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1;
773 >                                ch[channel].buf_len = ch[channel].buf[0] ? 254 : ch[channel].buf[1] - 1;
774                          }
775  
776 <                        if (buf_len[channel] > 0) {
777 <                                *byte = *buf_ptr[channel]++;
778 <                                if (!--buf_len[channel] && !chan_buf[channel][0])
776 >                        if (ch[channel].buf_len > 0) {
777 >                                byte = *(ch[channel].buf_ptr)++;
778 >                                if (--(ch[channel].buf_len) == 0 && ch[channel].buf[0] == 0)
779                                          return ST_EOF;
780                                  else
781                                          return ST_OK;
# Line 577 | Line 785 | uint8 D64Drive::Read(int channel, uint8
785  
786                  case CHMOD_DIRECTORY:
787                  case CHMOD_DIRECT:
788 <                        if (buf_len[channel] > 0) {
789 <                                *byte = *buf_ptr[channel]++;
790 <                                if (--buf_len[channel])
788 >                        if (ch[channel].buf_len > 0) {
789 >                                byte = *(ch[channel].buf_ptr)++;
790 >                                if (--(ch[channel].buf_len))
791                                          return ST_OK;
792                                  else
793                                          return ST_EOF;
# Line 595 | Line 803 | uint8 D64Drive::Read(int channel, uint8
803   *  Write byte to channel
804   */
805  
806 < uint8 D64Drive::Write(int channel, uint8 byte, bool eoi)
806 > uint8 ImageDrive::Write(int channel, uint8 byte, bool eoi)
807   {
808 <        switch (chan_mode[channel]) {
808 > //      D(bug("ImageDrive::Write channel %d, byte %02x, eoi %d\n", channel, byte, eoi));
809 >
810 >        switch (ch[channel].mode) {
811                  case CHMOD_FREE:
812 <                        set_error(ERR_FILENOTOPEN);
812 >                        if (current_error == ERR_OK)
813 >                                set_error(ERR_FILENOTOPEN);
814                          break;
815  
816                  case CHMOD_COMMAND:
817                          // Collect characters and execute command on EOI
818 <                        if (cmd_len >= 58)
818 >                        if (cmd_len > 58) {
819 >                                set_error(ERR_SYNTAX32);
820                                  return ST_TIMEOUT;
821 +                        }
822  
823                          cmd_buf[cmd_len++] = byte;
824  
# Line 618 | Line 831 | uint8 D64Drive::Write(int channel, uint8
831                  case CHMOD_DIRECTORY:
832                          set_error(ERR_WRITEFILEOPEN);
833                          break;
834 +
835 +                case CHMOD_FILE:
836 +                        if (!ch[channel].writing)
837 +                                return ST_TIMEOUT;
838 +                        if (current_error != ERR_OK)
839 +                                return ST_TIMEOUT;
840 +
841 +                        // Buffer full?
842 +                        if (ch[channel].buf_len >= 256) {
843 +
844 +                                // Yes, allocate new block
845 +                                int track = ch[channel].track, sector = ch[channel].sector;
846 +                                if (!alloc_next_block(track, sector, DATA_INTERLEAVE))
847 +                                        return ST_TIMEOUT;
848 +                                ch[channel].num_blocks++;
849 +                                D(bug("next data block on track %d, sector %d\n", track, sector));
850 +
851 +                                // Write buffer with link to new block
852 +                                ch[channel].buf[0] = track;
853 +                                ch[channel].buf[1] = sector;
854 +                                write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf);
855 +
856 +                                // Reset buffer
857 +                                ch[channel].buf_ptr = ch[channel].buf + 2;
858 +                                ch[channel].buf_len = 2;
859 +                                ch[channel].track = track;
860 +                                ch[channel].sector = sector;
861 +                        }
862 +                        *(ch[channel].buf_ptr)++ = byte;
863 +                        ch[channel].buf_len++;
864 +                        return ST_OK;
865 +
866 +                case CHMOD_DIRECT:
867 +                        if (ch[channel].buf_len < 256) {
868 +                                *(ch[channel].buf_ptr)++ = byte;
869 +                                ch[channel].buf_len++;
870 +                                return ST_OK;
871 +                        } else
872 +                                return ST_TIMEOUT;
873 +                        break;
874          }
875          return ST_TIMEOUT;
876   }
877  
878  
879   /*
880 < *  Execute command string
880 > *  Reset drive
881 > */
882 >
883 > void ImageDrive::Reset(void)
884 > {
885 >        close_all_channels();
886 >
887 >        cmd_len = 0;
888 >        for (int i=0; i<4; i++)
889 >                buf_free[i] = true;
890 >
891 >        if (bam_dirty) {
892 >                write_sector(DIR_TRACK, 0, bam);
893 >                bam_dirty = false;
894 >        }
895 >
896 >        memset(ram, 0, sizeof(ram));
897 >
898 >        read_sector(DIR_TRACK, 0, bam);
899 >
900 >        set_error(ERR_STARTUP);
901 > }
902 >
903 >
904 > /*
905 > *  Allocate floppy buffer
906 > *   -> Desired buffer number or -1
907 > *   <- Allocated buffer number or -1
908 > */
909 >
910 > int ImageDrive::alloc_buffer(int want)
911 > {
912 >        if (want == -1) {
913 >                for (want=3; want>=0; want--)
914 >                        if (buf_free[want]) {
915 >                                buf_free[want] = false;
916 >                                return want;
917 >                        }
918 >                return -1;
919 >        }
920 >
921 >        if (want < 4)
922 >                if (buf_free[want]) {
923 >                        buf_free[want] = false;
924 >                        return want;
925 >                } else
926 >                        return -1;
927 >        else
928 >                return -1;
929 > }
930 >
931 >
932 > /*
933 > *  Free floppy buffer
934 > */
935 >
936 > void ImageDrive::free_buffer(int buf)
937 > {
938 >        buf_free[buf] = true;
939 > }
940 >
941 >
942 > /*
943 > *  Search file in directory, return directory track/sector and entry number
944 > *  false: not found, true: found
945 > */
946 >
947 > // Return true if name 'n' matches pattern 'p'
948 > static bool match(const uint8 *p, int p_len, const uint8 *n)
949 > {
950 >        if (p_len > 16)
951 >                p_len = 16;
952 >
953 >        int c = 0;
954 >        while (p_len-- > 0) {
955 >                if (*p == '*')  // Wildcard '*' matches all following characters
956 >                        return true;
957 >                if ((*p != *n) && (*p != '?'))  // Wildcard '?' matches single character
958 >                        return false;
959 >                p++; n++; c++;
960 >        }
961 >
962 >        return *n == 0xa0 || c == 16;
963 > }
964 >
965 > bool ImageDrive::find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont)
966 > {
967 >        // Counter to prevent cyclic directories from resulting in an infinite loop
968 >        int num_dir_blocks = 0;
969 >
970 >        // Pointer to current directory entry
971 >        uint8 *de = NULL;
972 >        if (cont)
973 >                de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
974 >        else {
975 >                dir[DIR_NEXT_TRACK] = DIR_TRACK;
976 >                dir[DIR_NEXT_SECTOR] = 1;
977 >                entry = 8;
978 >        }
979 >
980 >        while (num_dir_blocks < num_sectors[DIR_TRACK]) {
981 >
982 >                // Goto next entry
983 >                entry++; de += SIZEOF_DE;
984 >                if (entry >= 8) {
985 >
986 >                        // Read next directory block
987 >                        if (dir[DIR_NEXT_TRACK] == 0)
988 >                                return false;
989 >                        if (!read_sector(dir_track = dir[DIR_NEXT_TRACK], dir_sector = dir[DIR_NEXT_SECTOR], dir))
990 >                                return false;
991 >                        num_dir_blocks++;
992 >                        entry = 0;
993 >                        de = dir + DIR_ENTRIES;
994 >                }
995 >
996 >                // Does entry match pattern?
997 >                if (de[DE_TYPE] && match(pattern, pattern_len, de + DE_NAME))
998 >                        return true;
999 >        }
1000 >        return false;
1001 > }
1002 >
1003 > bool ImageDrive::find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry)
1004 > {
1005 >        return find_file(pattern, pattern_len, dir_track, dir_sector, entry, false);
1006 > }
1007 >
1008 > bool ImageDrive::find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry)
1009 > {
1010 >        return find_file(pattern, pattern_len, dir_track, dir_sector, entry, true);
1011 > }
1012 >
1013 >
1014 > /*
1015 > *  Allocate new entry in directory, returns false on error (usually when
1016 > *  all sectors of track 18 are allocated)
1017 > *  The track/sector and entry numbers are returned
1018 > */
1019 >
1020 > bool ImageDrive::alloc_dir_entry(int &track, int &sector, int &entry)
1021 > {
1022 >        // First look for free entry in existing directory blocks
1023 >        dir[DIR_NEXT_TRACK] = DIR_TRACK;
1024 >        dir[DIR_NEXT_SECTOR] = 1;
1025 >        while (dir[DIR_NEXT_TRACK]) {
1026 >                if (!read_sector(track = dir[DIR_NEXT_TRACK], sector = dir[DIR_NEXT_SECTOR], dir))
1027 >                        return false;
1028 >
1029 >                uint8 *de = dir + DIR_ENTRIES;
1030 >                for (entry=0; entry<8; entry++, de+=SIZEOF_DE) {
1031 >                        if (de[DE_TYPE] == 0) {
1032 >                                D(bug(" allocated entry %d in dir track %d, sector %d\n", entry, track, sector));
1033 >                                return true;
1034 >                        }
1035 >                }
1036 >        }
1037 >
1038 >        // No free entry found, allocate new directory block
1039 >        int last_track = track, last_sector = sector;
1040 >        if (!alloc_next_block(track, sector, DIR_INTERLEAVE))
1041 >                return false;
1042 >        D(bug(" new directory block track %d, sector %d\n", track, sector));
1043 >
1044 >        // Write link to new block to last block
1045 >        dir[DIR_NEXT_TRACK] = track;
1046 >        dir[DIR_NEXT_SECTOR] = sector;
1047 >        write_sector(last_track, last_sector, dir);
1048 >
1049 >        // Write new empty directory block and return first entry
1050 >        memset(dir, 0, 256);
1051 >        dir[DIR_NEXT_SECTOR] = 0xff;
1052 >        write_sector(track, sector, dir);
1053 >        entry = 0;
1054 >        return true;
1055 > }
1056 >
1057 > /*
1058 > *  Test if block is free in BAM (track/sector are not checked for validity)
1059 > */
1060 >
1061 > bool ImageDrive::is_block_free(int track, int sector)
1062 > {
1063 >        uint8 *p = bam + BAM_BITMAP + (track - 1) * 4;
1064 >        int byte = sector / 8 + 1;
1065 >        int bit = sector & 7;
1066 >        return p[byte] & (1 << bit);
1067 > }
1068 >
1069 >
1070 > /*
1071 > *  Get number of free blocks on a track
1072 > */
1073 >
1074 > int ImageDrive::num_free_blocks(int track)
1075 > {
1076 >        return bam[BAM_BITMAP + (track - 1) * 4];
1077 > }
1078 >
1079 >
1080 > /*
1081 > *  Clear BAM, mark all blocks as free
1082 > */
1083 >
1084 > static void clear_bam(uint8 *bam)
1085 > {
1086 >        for (int track=1; track<=35; track++) {
1087 >                static const uint8 num2bits[8] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
1088 >                (bam + BAM_BITMAP)[(track-1) * 4 + 0] = num_sectors[track];
1089 >                (bam + BAM_BITMAP)[(track-1) * 4 + 1] = 0xff;
1090 >                (bam + BAM_BITMAP)[(track-1) * 4 + 2] = 0xff;
1091 >                (bam + BAM_BITMAP)[(track-1) * 4 + 3] = num2bits[num_sectors[track] - 16];
1092 >        }
1093 > }
1094 >
1095 >
1096 > /*
1097 > *  Allocate block in BAM, returns error code
1098 > */
1099 >
1100 > int ImageDrive::alloc_block(int track, int sector)
1101 > {
1102 >        if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track])
1103 >                return ERR_ILLEGALTS;
1104 >
1105 >        uint8 *p = bam + BAM_BITMAP + (track - 1) * 4;
1106 >        int byte = sector / 8 + 1;
1107 >        int bit = sector & 7;
1108 >
1109 >        // Block free?
1110 >        if (p[byte] & (1 << bit)) {
1111 >
1112 >                // Yes, allocate and decrement free block count
1113 >                D(bug("allocating block at track %d, sector %d\n", track, sector));
1114 >                p[byte] &= ~(1 << bit);
1115 >                p[0]--;
1116 >                bam_dirty = true;
1117 >                return ERR_OK;
1118 >
1119 >        } else
1120 >                return ERR_NOBLOCK;
1121 > }
1122 >
1123 >
1124 > /*
1125 > *  Free block in BAM, returns error code
1126 > */
1127 >
1128 > int ImageDrive::free_block(int track, int sector)
1129 > {
1130 >        if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track])
1131 >                return ERR_ILLEGALTS;
1132 >
1133 >        uint8 *p = bam + BAM_BITMAP + (track - 1) * 4;
1134 >        int byte = sector / 8 + 1;
1135 >        int bit = sector & 7;
1136 >
1137 >        // Block allocated?
1138 >        if (!(p[byte] & (1 << bit))) {
1139 >
1140 >                // Yes, free and increment free block count
1141 >                D(bug("freeing block at track %d, sector %d\n", track, sector));
1142 >                p[byte] |= (1 << bit);
1143 >                p[0]++;
1144 >                bam_dirty = true;
1145 >        }
1146 >        return ERR_OK;
1147 > }
1148 >
1149 >
1150 > /*
1151 > *  Allocate chain of data blocks in BAM
1152 > */
1153 >
1154 > bool ImageDrive::alloc_block_chain(int track, int sector)
1155 > {
1156 >        uint8 buf[256];
1157 >        while (alloc_block(track, sector) == ERR_OK) {
1158 >                if (!read_sector(track, sector, buf))
1159 >                        return false;
1160 >                track = buf[0];
1161 >                sector = buf[1];
1162 >        }
1163 >        return true;
1164 > }
1165 >
1166 >
1167 > /*
1168 > *  Free chain of data blocks in BAM
1169 > */
1170 >
1171 > bool ImageDrive::free_block_chain(int track, int sector)
1172 > {
1173 >        uint8 buf[256];
1174 >        while (free_block(track, sector) == ERR_OK) {
1175 >                if (!read_sector(track, sector, buf))
1176 >                        return false;
1177 >                track = buf[0];
1178 >                sector = buf[1];
1179 >        }
1180 >        return true;
1181 > }
1182 >
1183 >
1184 > /*
1185 > *  Search and allocate next free block, returns false if no more blocks
1186 > *  are free (ERR_DISKFULL is also set in this case)
1187 > *  "track" and "sector" must be set to the block where the search should
1188 > *  begin
1189 > */
1190 >
1191 > bool ImageDrive::alloc_next_block(int &track, int &sector, int interleave)
1192 > {
1193 >        // Find track with free blocks
1194 >        bool side_changed = false;
1195 >        while (num_free_blocks(track) == 0) {
1196 >                if (track == DIR_TRACK) {       // Directory doesn't grow to other tracks
1197 > full:           track = sector = 0;
1198 >                        set_error(ERR_DISKFULL);
1199 >                        return false;
1200 >                } else if (track > DIR_TRACK) {
1201 >                        track++;
1202 >                        if (track > 35) {
1203 >                                if (!side_changed)
1204 >                                        side_changed = true;
1205 >                                else
1206 >                                        goto full;
1207 >                                track = DIR_TRACK - 1;
1208 >                                sector = 0;
1209 >                        }
1210 >                } else {
1211 >                        track--;
1212 >                        if (track < 1) {
1213 >                                if (!side_changed)
1214 >                                        side_changed = true;
1215 >                                else
1216 >                                        goto full;
1217 >                                track = DIR_TRACK + 1;
1218 >                                sector = 0;
1219 >                        }
1220 >                }
1221 >        }
1222 >
1223 >        // Find next free block on track
1224 >        int num = num_sectors[track];
1225 >        sector = sector + interleave;
1226 >        if (sector >= num) {
1227 >                sector -= num;
1228 >                if (sector)
1229 >                        sector--;
1230 >        }
1231 >        while (!is_block_free(track, sector)) {
1232 >                sector++;
1233 >                if (sector >= num_sectors[track]) {
1234 >                        sector = 0;
1235 >                        while (!is_block_free(track, sector)) {
1236 >                                sector++;
1237 >                                if (sector >= num_sectors[track]) {
1238 >                                        // Something is wrong: the BAM free block count for this
1239 >                                        // track was >0, but we found no free blocks
1240 >                                        track = sector = 0;
1241 >                                        set_error(ERR_DIRERROR);
1242 >                                        return false;
1243 >                                }
1244 >                        }
1245 >                }
1246 >        }
1247 >
1248 >        alloc_block(track, sector);
1249 >        return true;
1250 > }
1251 >
1252 >
1253 > /*
1254 > *  Sector reading/writing routines
1255 > */
1256 >
1257 > static long offset_from_ts(const image_file_desc &desc, int track, int sector)
1258 > {
1259 >        if ((track < 1) || (track > desc.num_tracks)
1260 >         || (sector < 0) || (sector >= num_sectors[track]))
1261 >                return -1;
1262 >
1263 >        return ((accum_num_sectors[track] + sector) << 8) + desc.header_size;
1264 > }
1265 >
1266 > // Get number of sectors per given track
1267 > int sectors_per_track(const image_file_desc &desc, int track)
1268 > {
1269 >        return num_sectors[track];
1270 > }
1271 >
1272 > // Get reference to error info byte of given track/sector
1273 > uint8 &error_info_for_sector(image_file_desc &desc, int track, int sector)
1274 > {
1275 >        return desc.error_info[accum_num_sectors[track] + sector];
1276 > }
1277 >
1278 > static inline const uint8 &error_info_for_sector(const image_file_desc &desc, int track, int sector)
1279 > {
1280 >        return desc.error_info[accum_num_sectors[track] + sector];
1281 > }
1282 >
1283 > const int conv_job_error[16] = {
1284 >        ERR_OK,                         // 0 -> 00 OK
1285 >        ERR_OK,                         // 1 -> 00 OK
1286 >        ERR_READ20,                     // 2 -> 20 READ ERROR
1287 >        ERR_READ21,                     // 3 -> 21 READ ERROR
1288 >        ERR_READ22,                     // 4 -> 22 READ ERROR
1289 >        ERR_READ23,                     // 5 -> 23 READ ERROR
1290 >        ERR_READ24,                     // 6 -> 24 READ ERROR (undetected by 1541)
1291 >        ERR_WRITE25,            // 7 -> 25 WRITE ERROR
1292 >        ERR_WRITEPROTECT,       // 8 -> 26 WRITE PROTECT ON
1293 >        ERR_READ27,                     // 9 -> 27 READ ERROR
1294 >        ERR_WRITE28,            // 10 -> 28 WRITE ERROR
1295 >        ERR_DISKID,                     // 11 -> 29 DISK ID MISMATCH
1296 >        ERR_OK,                         // 12 -> 00 OK
1297 >        ERR_OK,                         // 13 -> 00 OK
1298 >        ERR_OK,                         // 14 -> 00 OK
1299 >        ERR_NOTREADY            // 15 -> 74 DRIVE NOT READY
1300 > };
1301 >
1302 > // Read sector, return error code
1303 > static int read_sector(FILE *f, const image_file_desc &desc, int track, int sector, uint8 *buffer)
1304 > {
1305 >        // Convert track/sector to byte offset in file
1306 >        long offset = offset_from_ts(desc, track, sector);
1307 >        if (offset < 0)
1308 >                return ERR_ILLEGALTS;
1309 >
1310 >        if (f == NULL)
1311 >                return ERR_NOTREADY;
1312 >
1313 >        fseek(f, offset, SEEK_SET);
1314 >        if (fread(buffer, 1, 256, f) != 256)
1315 >                return ERR_READ22;
1316 >        else {
1317 >                unsigned int error = error_info_for_sector(desc, track, sector);
1318 >                return conv_job_error[error & 0x0f];
1319 >        }
1320 > }
1321 >
1322 > // Write sector, return error code
1323 > static int write_sector(FILE *f, const image_file_desc &desc, int track, int sector, uint8 *buffer)
1324 > {
1325 >        // Convert track/sector to byte offset in file
1326 >        long offset = offset_from_ts(desc, track, sector);
1327 >        if (offset < 0)
1328 >                return ERR_ILLEGALTS;
1329 >
1330 >        if (f == NULL)
1331 >                return ERR_NOTREADY;
1332 >
1333 >        fseek(f, offset, SEEK_SET);
1334 >        if (fwrite(buffer, 1, 256, f) != 256)
1335 >                return ERR_WRITE25;
1336 >        else
1337 >                return ERR_OK;
1338 > }
1339 >
1340 > // Read sector and set error message, returns false on error
1341 > bool ImageDrive::read_sector(int track, int sector, uint8 *buffer)
1342 > {
1343 >        int error = ::read_sector(the_file, desc, track, sector, buffer);
1344 >        if (error)
1345 >                set_error(error, track, sector);
1346 >        return error == ERR_OK;
1347 > }
1348 >
1349 > // Write sector and set error message, returns false on error
1350 > bool ImageDrive::write_sector(int track, int sector, uint8 *buffer)
1351 > {
1352 >        int error = ::write_sector(the_file, desc, track, sector, buffer);
1353 >        if (error)
1354 >                set_error(error, track, sector);
1355 >        return error == ERR_OK;
1356 > }
1357 >
1358 > // Write error info back to image file
1359 > static void write_back_error_info(FILE *f, const image_file_desc &desc)
1360 > {
1361 >        if (desc.type == TYPE_D64 && desc.has_error_info) {
1362 >                int num_sectors = desc.num_tracks == 40 ? NUM_SECTORS_40 : NUM_SECTORS_35;
1363 >                fseek(f, num_sectors * 256, SEEK_SET);
1364 >                fwrite(desc.error_info, num_sectors, 1, f);
1365 >        }
1366 > }
1367 >
1368 > // Format disk image
1369 > static bool format_image(FILE *f, image_file_desc &desc, bool lowlevel, uint8 id1, uint8 id2, const uint8 *disk_name, int disk_name_len)
1370 > {
1371 >        uint8 p[256];
1372 >
1373 >        if (lowlevel) {
1374 >
1375 >                // Fill buffer with 1541 empty sector pattern (4b 01 01 ...,
1376 >                // except on track 1 where it's 01 01 01 ...)
1377 >                memset(p, 1, 256);
1378 >
1379 >                // Overwrite all blocks
1380 >                for (int track=1; track<=35; track++) {
1381 >                        if (track == 2)
1382 >                                p[0] = 0x4b;
1383 >                        for (int sector=0; sector<num_sectors[track]; sector++) {
1384 >                                if (write_sector(f, desc, track, sector, p) != ERR_OK)
1385 >                                        return false;
1386 >                        }
1387 >                }
1388 >
1389 >                // Clear and write error info
1390 >                memset(desc.error_info, 1, sizeof(desc.error_info));
1391 >                write_back_error_info(f, desc);
1392 >
1393 >                // Clear BAM
1394 >                memset(p, 0, 256);
1395 >
1396 >        } else {
1397 >
1398 >                // Read BAM
1399 >                if (read_sector(f, desc, DIR_TRACK, 0, p) != ERR_OK)
1400 >                        return false;
1401 >        }
1402 >
1403 >        // Create and write empty BAM
1404 >        p[BAM_DIR_TRACK] = DIR_TRACK;
1405 >        p[BAM_DIR_SECTOR] = 1;
1406 >        p[BAM_FMT_TYPE] = 'A';
1407 >        clear_bam(p);
1408 >        p[BAM_BITMAP + (DIR_TRACK - 1) * 4 + 0] -= 2;   // Allocate BAM and first directory block
1409 >        p[BAM_BITMAP + (DIR_TRACK - 1) * 4 + 1] &= 0xfc;
1410 >        memset(p + BAM_DISK_NAME, 0xa0, 27);
1411 >        if (disk_name_len > 16)
1412 >                disk_name_len = 16;
1413 >        memcpy(p + BAM_DISK_NAME, disk_name, disk_name_len);
1414 >        p[BAM_DISK_ID] = id1;
1415 >        p[BAM_DISK_ID + 1] = id2;
1416 >        p[BAM_FMT_CHAR] = '2';
1417 >        p[BAM_FMT_CHAR + 1] = 'A';
1418 >        if (write_sector(f, desc, DIR_TRACK, 0, p) != ERR_OK)
1419 >                return false;
1420 >
1421 >        // Create and write empty directory
1422 >        memset(p, 0, 256);
1423 >        p[1] = 255;
1424 >        return write_sector(f, desc, DIR_TRACK, 1, p) == ERR_OK;
1425 > }
1426 >
1427 >
1428 > /*
1429 > *  Execute drive commands
1430   */
1431  
1432   // BLOCK-READ:channel,0,track,sector
1433 < void D64Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
1433 > void ImageDrive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
1434   {
1435 <        if (channel >= 16 || chan_mode[channel] != CHMOD_DIRECT) {
1435 >        if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) {
1436                  set_error(ERR_NOCHANNEL);
1437                  return;
1438          }
1439 <        read_sector(track, sector, chan_buf[channel]);
1439 >        if (!read_sector(track, sector, ch[channel].buf))
1440 >                return;
1441          if (user_cmd) {
1442 <                buf_len[channel] = 256;
1443 <                buf_ptr[channel] = chan_buf[channel];
1442 >                ch[channel].buf_len = 256;
1443 >                ch[channel].buf_ptr = ch[channel].buf;
1444          } else {
1445 <                buf_len[channel] = chan_buf[channel][0];
1446 <                buf_ptr[channel] = chan_buf[channel] + 1;
1445 >                ch[channel].buf_len = ch[channel].buf[0];
1446 >                ch[channel].buf_ptr = ch[channel].buf + 1;
1447          }
1448   }
1449  
1450 + // BLOCK-WRITE:channel,0,track,sector
1451 + void ImageDrive::block_write_cmd(int channel, int track, int sector, bool user_cmd)
1452 + {
1453 +        if (write_protected) {
1454 +                set_error(ERR_WRITEPROTECT);
1455 +                return;
1456 +        }
1457 +        if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) {
1458 +                set_error(ERR_NOCHANNEL);
1459 +                return;
1460 +        }
1461 +        if (!user_cmd)
1462 +                ch[channel].buf[0] = ch[channel].buf_len ? ch[channel].buf_len - 1 : 1;
1463 +        if (!write_sector(track, sector, ch[channel].buf))
1464 +                return;
1465 +        if (!user_cmd) {
1466 +                ch[channel].buf_len = 1;
1467 +                ch[channel].buf_ptr = ch[channel].buf + 1;
1468 +        }
1469 + }
1470 +
1471 + // BLOCK-ALLOCATE:0,track,sector
1472 + void ImageDrive::block_allocate_cmd(int track, int sector)
1473 + {
1474 +        int err = alloc_block(track, sector);
1475 +        if (err) {
1476 +                if (err == ERR_NOBLOCK) {
1477 +                        // Find next free block and return its track/sector address in the
1478 +                        // error message (only look on higher tracks)
1479 +                        for (;;) {
1480 +                                sector++;
1481 +                                if (sector >= num_sectors[track]) {
1482 +                                        track++;
1483 +                                        sector = 0;
1484 +                                        if (track > 35) {
1485 +                                                set_error(ERR_NOBLOCK, 0, 0);
1486 +                                                return;
1487 +                                        }
1488 +                                }
1489 +                                if (is_block_free(track, sector)) {
1490 +                                        set_error(ERR_NOBLOCK, track, sector);
1491 +                                        return;
1492 +                                }
1493 +                        }
1494 +                } else
1495 +                        set_error(err, track, sector);
1496 +        }
1497 + }
1498 +
1499 + // BLOCK-FREE:0,track,sector
1500 + void ImageDrive::block_free_cmd(int track, int sector)
1501 + {
1502 +        int err = free_block(track, sector);
1503 +        if (err)
1504 +                set_error(err, track, sector);
1505 + }
1506 +
1507   // BUFFER-POINTER:channel,pos
1508 < void D64Drive::buffer_pointer_cmd(int channel, int pos)
1508 > void ImageDrive::buffer_pointer_cmd(int channel, int pos)
1509   {
1510 <        if (channel >= 16 || chan_mode[channel] != CHMOD_DIRECT) {
1510 >        if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) {
1511                  set_error(ERR_NOCHANNEL);
1512                  return;
1513          }
1514 <        buf_ptr[channel] = chan_buf[channel] + pos;
1515 <        buf_len[channel] = 256 - pos;
1514 >        ch[channel].buf_ptr = ch[channel].buf + pos;
1515 >        ch[channel].buf_len = 256 - pos;
1516   }
1517  
1518   // M-R<adr low><adr high>[<number>]
1519 < void D64Drive::mem_read_cmd(uint16 adr, uint8 len)
1519 > void ImageDrive::mem_read_cmd(uint16 adr, uint8 len)
1520   {
1521          error_len = len;
1522          if (adr >= 0x300 && adr < 0x1000) {
1523                  // Read from RAM
1524                  error_ptr = (char *)ram + (adr & 0x7ff);
1525 +        } else if (adr >= 0xc000) {
1526 +                // Read from ROM
1527 +                error_ptr = (char *)(TheC64->ROM1541) + (adr - 0xc000);
1528          } else {
1529                  unsupp_cmd();
1530                  memset(error_buf, 0, len);
# Line 670 | Line 1533 | void D64Drive::mem_read_cmd(uint16 adr,
1533   }
1534  
1535   // M-W<adr low><adr high><number><data...>
1536 < void D64Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
1536 > void ImageDrive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
1537   {
1538          while (len) {
1539                  if (adr >= 0x300 && adr < 0x1000) {
# Line 684 | Line 1547 | void D64Drive::mem_write_cmd(uint16 adr,
1547          }
1548   }
1549  
1550 + //   COPY:new=file1,file2,...
1551 + //        ^   ^
1552 + // new_file   old_files
1553 + void ImageDrive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len)
1554 + {
1555 +        // Check if destination file is already present
1556 +        int dir_track, dir_sector, entry;
1557 +        if (find_first_file(new_file, new_file_len, dir_track, dir_sector, entry)) {
1558 +                set_error(ERR_FILEEXISTS);
1559 +                return;
1560 +        }
1561 +
1562 +        // Loop for all source files
1563 +        bool first = true;
1564 +        while (old_files_len > 0) {
1565 +                uint8 *comma = (uint8 *)memchr(old_files, ',', old_files_len);
1566 +                int name_len = comma ? comma - old_files : old_files_len;
1567 +
1568 +                // Check if source file is present
1569 +                if (!find_first_file(old_files, name_len, dir_track, dir_sector, entry)) {
1570 +                        set_error(ERR_FILENOTFOUND);
1571 +                        Close(17);
1572 +                        return;
1573 +                }
1574 +                uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1575 +                uint8 type = de[DE_TYPE] & 7, track = de[DE_TRACK], sector = de[DE_SECTOR];
1576 +
1577 +                // If this is the first source file, open internal write channel for destination file
1578 +                if (first) {
1579 +                        create_file(17, new_file, new_file_len, type, false);
1580 +                        if (ch[17].mode == CHMOD_FREE)
1581 +                                return;
1582 +                        first = false;
1583 +                }
1584 +
1585 +                // Open internal read channel for source file
1586 +                open_file_ts(16, track, sector);
1587 +                if (ch[16].mode == CHMOD_FREE) {
1588 +                        Close(17);
1589 +                        return;
1590 +                }
1591 +
1592 +                // Copy file
1593 +                uint8 byte, st;
1594 +                do {
1595 +                        st = Read(16, byte);
1596 +                        Write(17, byte, false);
1597 +                } while (st == ST_OK);
1598 +                Close(16);
1599 +                if (st != ST_EOF) {
1600 +                        Close(17);
1601 +                        return;
1602 +                }
1603 +
1604 +                if (comma) {
1605 +                        old_files_len -= name_len + 1;
1606 +                        old_files = comma + 1;
1607 +                } else
1608 +                        old_files_len = 0;
1609 +        }
1610 +        Close(17);
1611 + }
1612 +
1613 + // RENAME:new=old
1614 + //        ^   ^
1615 + // new_file   old_file
1616 + void ImageDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
1617 + {
1618 +        // Check if destination file is already present
1619 +        int dir_track, dir_sector, entry;
1620 +        if (find_first_file(new_file, new_file_len, dir_track, dir_sector, entry)) {
1621 +                set_error(ERR_FILEEXISTS);
1622 +                return;
1623 +        }
1624 +
1625 +        // Check if source file is present
1626 +        if (!find_first_file(old_file, old_file_len, dir_track, dir_sector, entry)) {
1627 +                set_error(ERR_FILENOTFOUND);
1628 +                return;
1629 +        }
1630 +
1631 +        // Check for write-protection
1632 +        if (write_protected) {
1633 +                set_error(ERR_WRITEPROTECT);
1634 +                return;
1635 +        }
1636 +
1637 +        // Rename file in directory entry
1638 +        uint8 *p = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1639 +        memset(p + DE_NAME, 0xa0, 16);
1640 +        memcpy(p + DE_NAME, new_file, new_file_len);
1641 +        write_sector(dir_track, dir_sector, dir);
1642 + }
1643 +
1644 + // SCRATCH:file1,file2,...
1645 + //         ^
1646 + //         files
1647 + void ImageDrive::scratch_cmd(const uint8 *files, int files_len)
1648 + {
1649 +        // Check for write-protection
1650 +        if (write_protected) {
1651 +                set_error(ERR_WRITEPROTECT);
1652 +                return;
1653 +        }
1654 +
1655 +        // Loop for all files
1656 +        int num_files = 0;
1657 +        while (files_len > 0) {
1658 +                uint8 *comma = (uint8 *)memchr(files, ',', files_len);
1659 +                int name_len = comma ? comma - files : files_len;
1660 +
1661 +                int dir_track, dir_sector, entry;
1662 +                if (find_first_file(files, name_len, dir_track, dir_sector, entry)) {
1663 +                        do {
1664 +                                uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1665 +
1666 +                                // File protected? Then skip
1667 +                                if (de[DE_TYPE] & 0x40)
1668 +                                        continue;
1669 +
1670 +                                // Free allocated data blocks and side sectors
1671 +                                free_block_chain(de[DE_TRACK], de[DE_SECTOR]);
1672 +                                free_block_chain(de[DE_SIDE_TRACK], de[DE_SIDE_SECTOR]);
1673 +
1674 +                                // Clear file type
1675 +                                de[DE_TYPE] = 0;
1676 +
1677 +                                // Write directory block back
1678 +                                write_sector(dir_track, dir_sector, dir);
1679 +                                num_files++;
1680 +                        } while (find_next_file(files, name_len, dir_track, dir_sector, entry));
1681 +                }
1682 +
1683 +                if (comma) {
1684 +                        files_len -= name_len + 1;
1685 +                        files = comma + 1;
1686 +                } else
1687 +                        files_len = 0;
1688 +        }
1689 +
1690 +        // Report number of files scratched
1691 +        set_error(ERR_SCRATCHED, num_files);
1692 + }
1693 +
1694   // INITIALIZE
1695 < void D64Drive::initialize_cmd(void)
1695 > void ImageDrive::initialize_cmd(void)
1696   {
1697          // Close all channels and re-read BAM
1698          close_all_channels();
1699 <        read_sector(18, 0, (uint8 *)bam);
1699 >        if (bam_dirty) {
1700 >                write_sector(DIR_TRACK, 0, bam);
1701 >                bam_dirty = false;
1702 >        }
1703 >        read_sector(DIR_TRACK, 0, bam);
1704 > }
1705 >
1706 > // NEW:name,id
1707 > //     ^   ^
1708 > //  name   comma (or NULL)
1709 > void ImageDrive::new_cmd(const uint8 *name, int name_len, const uint8 *comma)
1710 > {
1711 >        // Check for write-protection
1712 >        if (write_protected) {
1713 >                set_error(ERR_WRITEPROTECT);
1714 >                return;
1715 >        }
1716 >
1717 >        // Remember current ID
1718 >        uint8 id1 = bam[BAM_DISK_ID], id2 = bam[BAM_DISK_ID + 1];
1719 >
1720 >        // Formatting with ID?
1721 >        if (comma) {
1722 >
1723 >                close_all_channels();
1724 >
1725 >                // Clear BAM buffer
1726 >                memset(bam, 0, 256);
1727 >
1728 >                // Get ID from command
1729 >                if (comma[1]) {
1730 >                        id1 = comma[1];
1731 >                        id2 = comma[2] ? comma[2] : ' ';
1732 >                } else {
1733 >                        id1 = id2 = ' ';
1734 >                }
1735 >        }
1736 >
1737 >        // Format disk image
1738 >        format_image(the_file, desc, comma, id1, id2, name, name_len);
1739 >
1740 >        // Re-read BAM
1741 >        read_sector(DIR_TRACK, 0, bam);
1742 >        bam_dirty = false;
1743 > }
1744 >
1745 > // VALIDATE
1746 > void ImageDrive::validate_cmd(void)
1747 > {
1748 >        // Backup of old BAM in case something goes amiss
1749 >        uint8 old_bam[256];
1750 >        memcpy(old_bam, bam, 256);
1751 >
1752 >        // Clear BAM
1753 >        clear_bam(bam);
1754 >        bam_dirty = true;
1755 >
1756 >        // Allocate BAM and directory
1757 >        if (!alloc_block_chain(DIR_TRACK, 0)) {
1758 >                memcpy(bam, old_bam, 256);
1759 >                return;
1760 >        }
1761 >
1762 >        // Allocate all file data and side sector blocks
1763 >        int dir_track, dir_sector, entry;
1764 >        if (find_first_file((uint8 *)"*", 1, dir_track, dir_sector, entry)) {
1765 >                do {
1766 >                        uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE;
1767 >
1768 >                        if (de[DE_TYPE] & 0x80) {
1769 >                                // Closed file, allocate all file data and side sector blocks
1770 >                                if (!alloc_block_chain(de[DE_TRACK], de[DE_SECTOR]) || !alloc_block_chain(de[DE_SIDE_TRACK], de[DE_SIDE_SECTOR])) {
1771 >                                        memcpy(bam, old_bam, 256);
1772 >                                        return;
1773 >                                }
1774 >                        } else {
1775 >                                // Open file, delete it
1776 >                                de[DE_TYPE] = 0;
1777 >                                write_sector(dir_track, dir_sector, dir);
1778 >                        }
1779 >                } while (find_next_file((uint8 *)"*", 1, dir_track, dir_sector, entry));
1780 >        }
1781   }
1782  
1783  
1784   /*
1785 < *  Reset drive
1785 > *  Check whether file with given header (64 bytes) and size looks like one
1786 > *  of the file types supported by this module
1787   */
1788  
1789 < void D64Drive::Reset(void)
1789 > static bool is_d64_file(const uint8 *header, long size)
1790   {
1791 <        close_all_channels();
1791 >        return size == NUM_SECTORS_35 * 256 || size == NUM_SECTORS_35 * 257
1792 >            || size == NUM_SECTORS_40 * 256 || size == NUM_SECTORS_40 * 257;
1793 > }
1794  
1795 <        read_sector(18, 0, (uint8 *)bam);
1795 > static bool is_ed64_file(const uint8 *header, long size)
1796 > {
1797 >        // 35-track d64 file with header ID at the end (only used internally for
1798 >        // converted zipcode files)
1799 >        return size == NUM_SECTORS_35 * 256 + 2;
1800 > }
1801  
1802 <        cmd_len = 0;
1803 <        for (int i=0; i<4; i++)
1804 <                buf_free[i] = true;
1802 > static bool is_x64_file(const uint8 *header, long size)
1803 > {
1804 >        return memcmp(header, "C\x15\x41\x64\x01\x02", 6) == 0;
1805 > }
1806  
1807 <        set_error(ERR_STARTUP);
1807 > static bool is_zipcode_file(const char *path)
1808 > {
1809 > #if 0
1810 >        string base, part;
1811 >        SplitPath(path, base, part);
1812 >        return part.length() > 2 && part[0] >= '1' && part[0] <= '4' && part[1] == '!';
1813 > #else
1814 >        return false;
1815 > #endif
1816 > }
1817 >
1818 > bool IsImageFile(const char *path, const uint8 *header, long size)
1819 > {
1820 >        return is_d64_file(header, size) || is_x64_file(header, size) || is_zipcode_file(path);
1821   }
1822  
1823  
1824 + #if 0
1825   /*
1826 < *  Allocate floppy buffer
716 < *   -> Desired buffer number or -1
717 < *   <- Allocated buffer number or -1
1826 > *  Convert zipcode file to extended d64 file (d64 file with header ID)
1827   */
1828  
1829 < int D64Drive::alloc_buffer(int want)
1829 > static FILE *open_zipcode_file(FILE *old, int num, const string &base, string &part, uint8 &id1, uint8 &id2)
1830   {
1831 <        if (want == -1) {
1832 <                for (want=3; want>=0; want--)
1833 <                        if (buf_free[want]) {
1834 <                                buf_free[want] = false;
1835 <                                return want;
1836 <                        }
1837 <                return -1;
1831 >        if (old)
1832 >                fclose(old);
1833 >        part[0] = num + '1';
1834 >        FILE *f = fopen(AddToPath(base, part).c_str(), "rb");
1835 >        if (f == NULL)
1836 >                return NULL;
1837 >        if (fseek(f, 2, SEEK_SET) < 0) {
1838 >                fclose(f);
1839 >                return NULL;
1840          }
1841 +        if (num == 0) {
1842 +                id1 = getc(f);
1843 +                id2 = getc(f);
1844 +        }
1845 +        return f;
1846 + }
1847  
1848 <        if (want < 4)
1849 <                if (buf_free[want]) {
1850 <                        buf_free[want] = false;
1851 <                        return want;
1852 <                } else
1853 <                        return -1;
1854 <        else
1855 <                return -1;
1848 > static FILE *convert_zipcode_to_ed64(const string &path)
1849 > {
1850 >        FILE *in = NULL, *out = NULL;
1851 >        uint8 id1, id2;
1852 >
1853 >        // Split input file name
1854 >        string base, part;
1855 >        SplitPath(path, base, part);
1856 >
1857 >        // Open output file
1858 >        out = tmpfile();
1859 >        if (out == NULL)
1860 >                goto error;
1861 >
1862 >        // Decode all tracks
1863 >        for (int track=1; track<=35; track++) {
1864 >                int max_sect = 17 + ((track < 31) ? 1 : 0) + ((track < 25) ? 1 : 0) + ((track < 18) ? 2 : 0);
1865 >
1866 >                // Select appropriate input file
1867 >                switch (track) {
1868 >                        case 1:
1869 >                                if ((in = open_zipcode_file(NULL, 0, base, part, id1, id2)) == NULL)
1870 >                                        goto error;
1871 >                                break;
1872 >                        case 9:
1873 >                                if ((in = open_zipcode_file(in, 1, base, part, id1, id2)) == NULL)
1874 >                                        goto error;
1875 >                                break;
1876 >                        case 17:
1877 >                                if ((in = open_zipcode_file(in, 2, base, part, id1, id2)) == NULL)
1878 >                                        goto error;
1879 >                                break;
1880 >                        case 26:
1881 >                                if ((in = open_zipcode_file(in, 3, base, part, id1, id2)) == NULL)
1882 >                                        goto error;
1883 >                                break;
1884 >                }
1885 >
1886 >                // Clear "sector read" flags
1887 >                bool sect_flag[21];
1888 >                for (int i=0; i<max_sect; i++)
1889 >                        sect_flag[i] = false;
1890 >
1891 >                // Read track
1892 >                uint8 act_track[21 * 256];
1893 >                for (int i=0; i<max_sect; i++) {
1894 >
1895 >                        // Read and verify track/sector number
1896 >                        uint8 t = getc(in);
1897 >                        uint8 s = getc(in);
1898 >                        if ((t & 0x3f) != track || s >= max_sect || sect_flag[s] || feof(in))
1899 >                                goto error;
1900 >                        sect_flag[s] = true;
1901 >                        uint8 *p = act_track + s * 256;
1902 >
1903 >                        // Uncompress sector
1904 >                        if (t & 0x80) {
1905 >                                // Run-length encoded sector
1906 >                                uint8 len = getc(in);
1907 >                                uint8 rep = getc(in);
1908 >                                int count = 0;
1909 >                                for (int j=0; j<len; j++) {
1910 >                                        if (feof(in))
1911 >                                                goto error;
1912 >                                        uint8 c = getc(in);
1913 >                                        if (c != rep)
1914 >                                                p[count++] = c;
1915 >                                        else {
1916 >                                                uint8 repnum = getc(in);
1917 >                                                if (feof(in))
1918 >                                                        goto error;
1919 >                                                c = getc(in);
1920 >                                                j += 2;
1921 >                                                for (int k=0; k<repnum; k++)
1922 >                                                        p[count++] = c;
1923 >                                        }
1924 >                                }
1925 >                        } else if (t & 0x40) {
1926 >                                // Sector filled with constant byte
1927 >                                if (feof(in))
1928 >                                        goto error;
1929 >                                uint8 c = getc(in);
1930 >                                memset(p, c, 256);
1931 >                        } else {
1932 >                                // Plain sector
1933 >                                if (fread(p, 1, 256, in) != 256)
1934 >                                        goto error;
1935 >                        }
1936 >                }
1937 >
1938 >                // Write track
1939 >                if (fwrite(act_track, 256, max_sect, out) != (size_t)max_sect)
1940 >                        goto error;
1941 >        }
1942 >
1943 >        // Write header ID
1944 >        putc(id1, out);
1945 >        putc(id2, out);
1946 >
1947 >        // Done
1948 >        fclose(in);
1949 >        fseek(out, 0, SEEK_SET);
1950 >        return out;
1951 >
1952 > error:
1953 >        if (in)
1954 >                fclose(in);
1955 >        if (out)
1956 >                fclose(out);
1957 >        return NULL;
1958   }
1959 + #endif
1960  
1961  
1962   /*
1963 < *  Free floppy buffer
1963 > *  Open disk image file, return file handle
1964   */
1965  
1966 < void D64Drive::free_buffer(int buf)
1966 > static FILE *open_image_file(const char *path, bool write_mode)
1967   {
1968 <        buf_free[buf] = true;
1968 > #if 0
1969 >        if (is_zipcode_file(path)) {
1970 >                if (write_mode)
1971 >                        return NULL;
1972 >                else
1973 >                        return convert_zipcode_to_ed64(path);
1974 >        } else
1975 > #endif
1976 >                return fopen(path, write_mode ? "r+b" : "rb");
1977   }
1978  
1979  
1980   /*
1981 < *  Read sector (256 bytes)
754 < *  true: success, false: error
1981 > *  Parse image file and fill in image_file_desc structure
1982   */
1983  
1984 < bool D64Drive::read_sector(int track, int sector, uint8 *buffer)
1984 > static bool parse_d64_file(FILE *f, image_file_desc &desc, bool has_header_id)
1985   {
1986 <        int offset;
1986 >        // .d64 files have no header
1987 >        desc.type = has_header_id ? TYPE_ED64 : TYPE_D64;
1988 >        desc.header_size = 0;
1989  
1990 <        // Convert track/sector to byte offset in file
1991 <        if ((offset = offset_from_ts(track, sector)) < 0) {
1992 <                set_error(ERR_ILLEGALTS);
1993 <                return false;
1994 <        }
1990 >        // Determine number of tracks
1991 >        fseek(f, 0, SEEK_END);
1992 >        long size = ftell(f);
1993 >        if (size == NUM_SECTORS_40 * 256 || size == NUM_SECTORS_40 * 257)
1994 >                desc.num_tracks = 40;
1995 >        else
1996 >                desc.num_tracks = 35;
1997  
1998 <        if (the_file == NULL) {
1999 <                set_error(ERR_NOTREADY);
1998 >        if (has_header_id) {
1999 >                // Read header ID from image file (last 2 bytes)
2000 >                fseek(f, -2, SEEK_END);
2001 >                desc.id1 = getc(f);
2002 >                desc.id2 = getc(f);
2003 >        } else {
2004 >                // Read header ID from BAM (use error_info as buffer)
2005 >                fseek(f, accum_num_sectors[18] * 256, SEEK_SET);
2006 >                fread(desc.error_info, 1, 256, f);
2007 >                desc.id1 = desc.error_info[BAM_DISK_ID];
2008 >                desc.id2 = desc.error_info[BAM_DISK_ID + 1];
2009 >        }
2010 >
2011 >        // Read error info
2012 >        memset(desc.error_info, 1, sizeof(desc.error_info));
2013 >        if (size == NUM_SECTORS_35 * 257) {
2014 >                fseek(f, NUM_SECTORS_35 * 256, SEEK_SET);
2015 >                fread(desc.error_info, NUM_SECTORS_35, 1, f);
2016 >                desc.has_error_info = true;
2017 >        } else if (size == NUM_SECTORS_40 * 257) {
2018 >                fseek(f, NUM_SECTORS_40 * 256, SEEK_SET);
2019 >                fread(desc.error_info, NUM_SECTORS_40, 1, f);
2020 >                desc.has_error_info = true;
2021 >        } else
2022 >                desc.has_error_info = false;
2023 >
2024 >        return true;
2025 > }
2026 >
2027 > static bool parse_x64_file(FILE *f, image_file_desc &desc)
2028 > {
2029 >        desc.type = TYPE_X64;
2030 >        desc.header_size = 64;
2031 >
2032 >        // Read number of tracks
2033 >        fseek(f, 7, SEEK_SET);
2034 >        desc.num_tracks = getc(f);
2035 >        if (desc.num_tracks < 35 || desc.num_tracks > 40)
2036                  return false;
770        }
2037  
2038 < #ifdef AMIGA
2039 <        if (offset != ftell(the_file))
2040 <                fseek(the_file, offset + image_header, SEEK_SET);
2041 < #else
2042 <        fseek(the_file, offset + image_header, SEEK_SET);
2043 < #endif
2044 <        fread(buffer, 256, 1, the_file);
2038 >        // Read header ID from BAM (use error_info as buffer)
2039 >        fseek(f, desc.header_size + accum_num_sectors[18] * 256, SEEK_SET);
2040 >        fread(desc.error_info, 1, 256, f);
2041 >        desc.id1 = desc.error_info[BAM_DISK_ID];
2042 >        desc.id2 = desc.error_info[BAM_DISK_ID + 1];
2043 >
2044 >        // .x64 files have no error info
2045 >        memset(desc.error_info, 1, sizeof(desc.error_info));
2046 >        desc.has_error_info = false;
2047          return true;
2048   }
2049  
2050 + static bool parse_image_file(FILE *f, image_file_desc &desc)
2051 + {
2052 +        // Read header
2053 +        uint8 header[64];
2054 +        fread(header, 1, sizeof(header), f);
2055 +
2056 +        // Determine file size
2057 +        fseek(f, 0, SEEK_END);
2058 +        long size = ftell(f);
2059 +
2060 +        // Determine file type and fill in image_file_desc structure
2061 +        if (is_x64_file(header, size))
2062 +                return parse_x64_file(f, desc);
2063 +        else if (is_d64_file(header, size))
2064 +                return parse_d64_file(f, desc, false);
2065 +        else if (is_ed64_file(header, size))
2066 +                return parse_d64_file(f, desc, true);
2067 +        else
2068 +                return false;
2069 + }
2070 +
2071  
2072   /*
2073 < *  Convert track/sector to offset
2073 > *  Read directory of disk image file into (empty) c64_dir_entry vector,
2074 > *  returns false on error
2075   */
2076  
2077 < const int num_sectors[41] = {
2078 <        0,
2079 <        21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
2080 <        19,19,19,19,19,19,19,
2081 <        18,18,18,18,18,18,
2082 <        17,17,17,17,17,
2083 <        17,17,17,17,17          // Tracks 36..40
2084 < };
2077 > bool ReadImageDirectory(const char *path, vector<c64_dir_entry> &vec)
2078 > {
2079 >        bool result = false;
2080 >
2081 >        // Open file
2082 >        FILE *f = open_image_file(path, false);
2083 >        if (f) {
2084 >                int num_dir_blocks = 0;
2085 >
2086 >                // Determine file type and fill in image_file_desc structure
2087 >                image_file_desc desc;
2088 >                if (!parse_image_file(f, desc))
2089 >                        goto done;
2090 >
2091 >                // Scan all directory blocks
2092 >                uint8 dir[256];
2093 >                dir[DIR_NEXT_TRACK] = DIR_TRACK;
2094 >                dir[DIR_NEXT_SECTOR] = 1;
2095 >
2096 >                while (dir[DIR_NEXT_TRACK] && num_dir_blocks < num_sectors[DIR_TRACK]) {
2097 >                        if (read_sector(f, desc, dir[DIR_NEXT_TRACK], dir[DIR_NEXT_SECTOR], dir) != ERR_OK)
2098 >                                break;
2099 >                        num_dir_blocks++;
2100 >
2101 >                        // Scan all 8 entries of a block
2102 >                        uint8 *de = dir + DIR_ENTRIES;
2103 >                        for (int j=0; j<8; j++, de+=SIZEOF_DE) {
2104 >
2105 >                                // Skip empty entries
2106 >                                if (de[DE_TYPE] == 0)
2107 >                                        continue;
2108 >
2109 >                                // Convert file name (strip everything after and including the first trailing space)
2110 >                                uint8 name_buf[17];
2111 >                                memcpy(name_buf, de + DE_NAME, 16);
2112 >                                name_buf[16] = 0;
2113 >                                uint8 *p = (uint8 *)memchr(name_buf, 0xa0, 16);
2114 >                                if (p)
2115 >                                        *p = 0;
2116 >
2117 >                                // Convert file type
2118 >                                int type = de[DE_TYPE] & 7;
2119 >                                if (type > 4)
2120 >                                        type = FTYPE_UNKNOWN;
2121 >
2122 >                                // Read start address
2123 >                                uint8 sa_lo = 0, sa_hi = 0;
2124 >                                uint8 buf[256];
2125 >                                if (read_sector(f, desc, de[DE_TRACK], de[DE_SECTOR], buf) == ERR_OK) {
2126 >                                        sa_lo = buf[2];
2127 >                                        sa_hi = buf[3];
2128 >                                }
2129 >
2130 >                                // Add entry
2131 >                                vec.push_back(c64_dir_entry(name_buf, type, !(de[DE_TYPE] & 0x80), de[DE_TYPE] & 0x40, ((de[DE_NUM_BLOCKS_H] << 8) + de[DE_NUM_BLOCKS_L]) * 254, 0, sa_lo, sa_hi));
2132 >                        }
2133 >                }
2134 >
2135 >                result = true;
2136 > done:   fclose(f);
2137 >        }
2138 >        return result;
2139 > }
2140  
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 };
2141  
2142 < int D64Drive::offset_from_ts(int track, int sector)
2142 > /*
2143 > *  Create new blank disk image file, returns false on error
2144 > */
2145 >
2146 > bool CreateImageFile(const char *path)
2147   {
2148 <        if ((track < 1) || (track > NUM_TRACKS) ||
2149 <                (sector < 0) || (sector >= num_sectors[track]))
2150 <                return -1;
2148 >        // Open file for writing
2149 >        FILE *f = fopen(path, "wb");
2150 >        if (f == NULL)
2151 >                return false;
2152  
2153 <        return (sector_offset[track] + sector) << 8;
2153 >        // Create descriptor
2154 >        image_file_desc desc;
2155 >        desc.type = TYPE_D64;
2156 >        desc.header_size = 0;
2157 >        desc.num_tracks = 35;
2158 >        desc.id1 = 'F';
2159 >        desc.id1 = 'R';
2160 >        memset(desc.error_info, 1, sizeof(desc.error_info));
2161 >        desc.has_error_info = false;
2162 >
2163 >        // Format image file
2164 >        if (!format_image(f, desc, true, 'F', 'R', (uint8 *)"D64 FILE", 8)) {
2165 >                fclose(f);
2166 >                remove(path);
2167 >                return false;
2168 >        }
2169 >
2170 >        // Close file
2171 >        fclose(f);
2172 >        return true;
2173   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines