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.10 by cebix, 2005-06-27T19:55:48Z

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines