Main Page | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

d64copy.c

00001 /*
00002  *      This program is free software; you can redistribute it and/or
00003  *      modify it under the terms of the GNU General Public License
00004  *      as published by the Free Software Foundation; either version
00005  *      2 of the License, or (at your option) any later version.
00006  *
00007  *  Copyright 1999-2001 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
00008  *  Modifications for cbm4win Copyright 2001-2004 Spiro Trikaliotis
00009 */
00010 
00011 #ifdef SAVE_RCSID
00012 static char *rcsid =
00013     "@(#) $Id: d64copy.c,v 1.19 2006/04/11 15:29:15 trikalio Exp $";
00014 #endif
00015 
00016 #include "d64copy_int.h"
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include <assert.h>
00020 
00021 #include "arch.h"
00022 
00023 
00024 static const char d64_sector_map[MAX_TRACKS+1] =
00025 { 0,
00026   21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
00027   21, 21, 21, 21, 21, 21, 21, 19, 19, 19,
00028   19, 19, 19, 19, 18, 18, 18, 18, 18, 18,
00029   17, 17, 17, 17, 17,
00030   17, 17, 17, 17, 17, 17, 17
00031 };
00032 
00033 static const char d71_sector_map[D71_TRACKS+1] =
00034 { 0,
00035   21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
00036   21, 21, 21, 21, 21, 21, 21, 19, 19, 19,
00037   19, 19, 19, 19, 18, 18, 18, 18, 18, 18,
00038   17, 17, 17, 17, 17,
00039   21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
00040   21, 21, 21, 21, 21, 21, 21, 19, 19, 19,
00041   19, 19, 19, 19, 18, 18, 18, 18, 18, 18,
00042   17, 17, 17, 17, 17,
00043 };
00044 
00045 static const unsigned char warp_read_1541[] =
00046 {
00047 #include "warpread1541.inc"
00048 };
00049 
00050 static const unsigned char warp_write_1541[] =
00051 {
00052 #include "warpwrite1541.inc"
00053 };
00054 
00055 static const unsigned char warp_read_1571[] =
00056 {
00057 #include "warpread1571.inc"
00058 };
00059 
00060 static const unsigned char warp_write_1571[] =
00061 {
00062 #include "warpwrite1571.inc"
00063 };
00064 
00065 static const unsigned char turbo_read_1541[] =
00066 {
00067 #include "turboread1541.inc"
00068 };
00069 
00070 static const unsigned char turbo_write_1541[] =
00071 {
00072 #include "turbowrite1541.inc"
00073 };
00074 
00075 static const unsigned char turbo_read_1571[] =
00076 {
00077 #include "turboread1571.inc"
00078 };
00079 
00080 static const unsigned char turbo_write_1571[] =
00081 {
00082 #include "turbowrite1571.inc"
00083 };
00084 
00085 static const struct drive_prog
00086 {
00087     int size;
00088     const unsigned char *prog;
00089 } drive_progs[] =
00090 {
00091     {sizeof(turbo_read_1541), turbo_read_1541},
00092     {sizeof(turbo_write_1541), turbo_write_1541},
00093     {sizeof(warp_read_1541), warp_read_1541},
00094     {sizeof(warp_write_1541), warp_write_1541},
00095     {sizeof(turbo_read_1571), turbo_read_1571},
00096     {sizeof(turbo_write_1571), turbo_write_1571},
00097     {sizeof(warp_read_1571), warp_read_1571},
00098     {sizeof(warp_write_1571), warp_write_1571}
00099 };
00100 
00101 
00102 static const int default_interleave[] = { -1, 17, 4, 13, 7, -1 };
00103 static const int warp_write_interleave[] = { -1, 0, 6, 12, 4, -1 };
00104 
00105 
00106 /*
00107  * Variables to make sure writing a block is an atomary process
00108  */
00109 static int atom_mustcleanup = 0;
00110 static const transfer_funcs *atom_dst;
00111 
00112 
00113 static int send_turbo(CBM_FILE fd, unsigned char drv, int write, int warp, int drv_type)
00114 {
00115     const struct drive_prog *prog;
00116 
00117     prog = &drive_progs[drv_type * 4 + warp * 2 + write];
00118 
00119     return cbm_upload(fd, drv, 0x500, prog->prog, prog->size);
00120 }
00121 
00122 extern transfer_funcs d64copy_fs_transfer,
00123                       d64copy_std_transfer,
00124                       d64copy_pp_transfer,
00125                       d64copy_s1_transfer,
00126                       d64copy_s2_transfer;
00127 
00128 static d64copy_message_cb message_cb;
00129 static d64copy_status_cb status_cb;
00130 
00131 int d64copy_sector_count(int two_sided, int track)
00132 {
00133     if(two_sided)
00134     {
00135         if(track >= 1 && track <= D71_TRACKS)
00136         {
00137             return d71_sector_map[track];
00138         }
00139     }
00140     else
00141     {
00142         if(track >= 1 && track <= TOT_TRACKS)
00143         {
00144             return d64_sector_map[track];
00145         }
00146     }
00147     return -1;
00148 }
00149 
00150 d64copy_settings *d64copy_get_default_settings(void)
00151 {
00152     d64copy_settings *settings;
00153 
00154     settings = malloc(sizeof(d64copy_settings));
00155 
00156     if(NULL != settings)
00157     {
00158         settings->warp        = -1;
00159         settings->retries     = 0;
00160         settings->bam_mode    = bm_ignore;
00161         settings->interleave  = -1; /* set later on */
00162         settings->start_track = 1;
00163         settings->end_track   = -1; /* set later on */
00164         settings->drive_type  = cbm_dt_unknown; /* auto detect later on */
00165         settings->two_sided   = 0;
00166         settings->error_mode  = em_on_error;
00167     }
00168     return settings;
00169 }
00170 
00171 
00172 static int start_turbo(CBM_FILE fd, unsigned char drive)
00173 {
00174     return cbm_exec_command(fd, drive, "U4:", 3);
00175 }
00176 
00177 
00178 static int copy_disk(CBM_FILE fd_cbm, d64copy_settings *settings,
00179               const transfer_funcs *src, const void *src_arg,
00180               const transfer_funcs *dst, const void *dst_arg, unsigned char cbm_drive)
00181 {
00182     unsigned char tr = 0;
00183     unsigned char se = 0;
00184     int st;
00185     int cnt  = 0;
00186     unsigned char scnt = 0;
00187     unsigned char errors;
00188     int retry_count;
00189     int resend_trackmap;
00190     int max_tracks;
00191     char trackmap[MAX_SECTORS+1];
00192     char buf[40];
00193     unsigned const char *bam_ptr;
00194     unsigned char bam[BLOCKSIZE];
00195     unsigned char bam2[BLOCKSIZE];
00196     unsigned char block[BLOCKSIZE];
00197     unsigned char gcr[GCRBUFSIZE];
00198     const transfer_funcs *cbm_transf = NULL;
00199     d64copy_status status;
00200     const char *sector_map;
00201     const char *type_str = "*unknown*";
00202 
00203     if(settings->two_sided)
00204     {
00205         max_tracks = D71_TRACKS;
00206     }
00207     else
00208     {
00209         max_tracks = TOT_TRACKS;
00210     }
00211 
00212     if(settings->interleave != -1 &&
00213            (settings->interleave < 1 || settings->interleave > 17))
00214     {
00215         message_cb(0,
00216                 "invalid value (%d) for interleave", settings->interleave);
00217         return -1;
00218     }
00219 
00220     if(settings->start_track < 1 || settings->start_track > max_tracks)
00221     {
00222         message_cb(0,
00223                 "invalid value (%d) for start track", settings->start_track);
00224         return -1;
00225     }
00226 
00227     if(settings->end_track != -1 && 
00228        (settings->end_track < settings->start_track ||
00229         settings->end_track > max_tracks))
00230     {
00231         message_cb(0,
00232                 "invalid value (%d) for end track", settings->end_track);
00233         return -1;
00234     }
00235 
00236     if(settings->interleave == -1)
00237     {
00238         settings->interleave = (dst->is_cbm_drive && settings->warp) ?
00239             warp_write_interleave[settings->transfer_mode] :
00240             default_interleave[settings->transfer_mode];
00241 
00242         assert(settings->interleave >= 0);
00243     }
00244 
00245 
00246     if(settings->drive_type == cbm_dt_unknown )
00247     {
00248         message_cb( 2, "Trying to identify drive type" );
00249         if( cbm_identify( fd_cbm, cbm_drive, &settings->drive_type, NULL ) )
00250         {
00251             message_cb( 0, "could not identify device" );
00252         }
00253 
00254         switch( settings->drive_type )
00255         {
00256             case cbm_dt_cbm1541:
00257             case cbm_dt_cbm1570:
00258             case cbm_dt_cbm1571:
00259                 /* fine */
00260                 break;
00261             case cbm_dt_cbm1581:
00262                 message_cb( 0, "1581 drives are not supported" );
00263                 return -1;
00264             default:
00265                 message_cb( 1, "Unknown drive, assuming 1541" );
00266                 settings->drive_type = cbm_dt_cbm1541;
00267                 break;
00268         }
00269     }
00270 
00271     sector_map = settings->two_sided ? d71_sector_map : d64_sector_map;
00272 
00273     cbm_exec_command(fd_cbm, cbm_drive, "I0:", 0);
00274     cnt = cbm_device_status(fd_cbm, cbm_drive, buf, sizeof(buf));
00275 
00276     switch( settings->drive_type )
00277     {
00278         case cbm_dt_cbm1541: type_str = "1541"; break;
00279         case cbm_dt_cbm1570: type_str = "1570"; break;
00280         case cbm_dt_cbm1571: type_str = "1571"; break;
00281         default: /* impossible */ break;
00282     }
00283 
00284     message_cb(cnt != 0 ? 0 : 2, "drive %02d (%s): %s",
00285                cbm_drive, type_str, buf );
00286 
00287     if(cnt)
00288     {
00289         return -1;
00290     }
00291 
00292     if(settings->two_sided)
00293     {
00294         if(settings->drive_type != cbm_dt_cbm1571)
00295         {
00296             message_cb(0, ".d71 transfer requires a 1571 drive");
00297             return -1;
00298         }
00299         if(settings->warp)
00300         {
00301             if(settings->warp>0)
00302                 message_cb(1, "`-w' for .d71 transfer in warp mode ignored");
00303             settings->warp = 0;
00304         }
00305         cbm_exec_command(fd_cbm, cbm_drive, "U0>M1", 0);
00306     }
00307 
00308     cbm_transf = src->is_cbm_drive ? src : dst;
00309 
00310     if(settings->warp && (cbm_transf->read_gcr_block == NULL))
00311     {
00312         if(settings->warp>0)
00313             message_cb(1, "`-w' for this transfer mode ignored");
00314         settings->warp = 0;
00315     }
00316 
00317     settings->warp = settings->warp ? 1 : 0;
00318 
00319     if(cbm_transf->needs_turbo)
00320     {
00321         send_turbo(fd_cbm, cbm_drive, dst->is_cbm_drive, settings->warp,
00322                    settings->drive_type == cbm_dt_cbm1541 ? 0 : 1);
00323     }
00324 
00325     if(src->open_disk(fd_cbm, settings, src_arg, 0,
00326                       start_turbo, message_cb) == 0)
00327     {
00328         if(settings->end_track == -1)
00329         {
00330             settings->end_track = 
00331                 settings->two_sided ? D71_TRACKS : STD_TRACKS;
00332         }
00333         if(dst->open_disk(fd_cbm, settings, dst_arg, 1,
00334                           start_turbo, message_cb) != 0)
00335         {
00336             message_cb(0, "can't open destination");
00337             return -1;
00338         }
00339     }
00340     else
00341     {
00342         message_cb(0, "can't open source");
00343         return -1;
00344     }
00345 
00346     memset(status.bam, bs_invalid, MAX_TRACKS * MAX_SECTORS);
00347 
00348     if(settings->bam_mode != bm_ignore)
00349     {
00350         if(settings->warp && src->is_cbm_drive)
00351         {
00352             memset(trackmap, bs_dont_copy, sector_map[18]);
00353             trackmap[0] = bs_must_copy;
00354             scnt = 1;
00355             src->send_track_map(18, trackmap, scnt);
00356             st = src->read_gcr_block(&se, gcr);
00357             if(st == 0) st = gcr_decode(gcr, bam);
00358         }
00359         else
00360         {
00361             st = src->read_block(18, 0, bam);
00362             if(settings->two_sided && (st == 0))
00363             {
00364                 st = src->read_block(53, 0, bam2);
00365             }
00366         }
00367         if(st)
00368         {
00369             message_cb(1, "failed to read BAM (%d)", st);
00370             settings->bam_mode = bm_ignore;
00371         }
00372     }
00373 
00374     memset(&status, 0, sizeof(status));
00375 
00376     /* setup BAM */
00377     for(tr = 1; tr <= max_tracks; tr++)
00378     {
00379         if(tr < settings->start_track || tr > settings->end_track)
00380         {
00381             memset(status.bam[tr-1], bs_dont_copy, sector_map[tr]);
00382         }
00383         else if(settings->bam_mode == bm_allocated ||
00384                 (settings->bam_mode == bm_save && (tr % 35 != 18)))
00385         {
00386             for(se = 0; se < sector_map[tr]; se++)
00387             {
00388                 if(settings->two_sided && tr > STD_TRACKS)
00389                 {
00390                     bam_ptr = &bam2[3*(tr - STD_TRACKS - 1)];
00391                 }
00392                 else
00393                 {
00394                     bam_ptr = &bam[4*tr + 1 + (tr > STD_TRACKS ? 48 : 0)];
00395                 }
00396                 if(bam_ptr[se/8]&(1<<(se&0x07)))
00397                 {
00398                     status.bam[tr-1][se] = bs_dont_copy;
00399                 }
00400                 else
00401                 {
00402                     status.bam[tr-1][se] = bs_must_copy;
00403                     status.total_sectors++;
00404                 }
00405             }
00406         }
00407         else
00408         {
00409             status.total_sectors += sector_map[tr];
00410             memset(status.bam[tr-1], bs_must_copy, sector_map[tr]);
00411         }
00412     }
00413 
00414     status.settings = settings;
00415 
00416     status_cb(status);
00417 
00418     message_cb(2, "copying tracks %d-%d (%d sectors)",
00419             settings->start_track, settings->end_track, status.total_sectors);
00420 
00421     for(tr = 1; tr <= max_tracks; tr++)
00422     {
00423         if(tr >= settings->start_track && tr <= settings->end_track)
00424         {
00425             scnt = sector_map[tr];
00426             memcpy(trackmap, status.bam[tr-1], scnt);
00427             if(settings->bam_mode != bm_ignore)
00428             {
00429                 for(se = 0; se < sector_map[tr]; se++)
00430                 {
00431                     if(trackmap[se] != bs_must_copy)
00432                     {
00433                         scnt--;
00434                     }
00435                 }
00436             }
00437 
00438             retry_count = settings->retries;
00439             do
00440             {
00441                 errors = resend_trackmap = 0;
00442                 if(scnt && settings->warp && src->is_cbm_drive)
00443                 {
00444                     src->send_track_map(tr, trackmap, scnt);
00445                 }
00446                 else
00447                 {
00448                     se = 0;
00449                 }
00450                 while(scnt && !resend_trackmap)
00451                 {
00452                     if(settings->warp && src->is_cbm_drive)
00453                     {
00454                         status.read_result = src->read_gcr_block(&se, gcr);
00455                         if(status.read_result == 0)
00456                         {
00457                             status.read_result = gcr_decode(gcr, block);
00458                         }
00459                         else
00460                         {
00461                             /* mark all sectors not received so far */
00462                             /* ugly */
00463                             errors = 0;
00464                             for(scnt = 0; scnt < sector_map[tr]; scnt++)
00465                             {
00466                                 if(NEED_SECTOR(trackmap[scnt]) && scnt != se)
00467                                 {
00468                                     trackmap[scnt] = bs_error;
00469                                     errors++;
00470                                 }
00471                             }
00472                             resend_trackmap = 1;
00473                         }
00474                     }
00475                     else
00476                     {
00477                         while(!NEED_SECTOR(trackmap[se]))
00478                         {
00479                             if(++se >= sector_map[tr]) se = 0;
00480                         }
00481                         status.read_result = src->read_block(tr, se, block);
00482                     }
00483 
00484                     if(settings->warp && dst->is_cbm_drive)
00485                     {
00486                         gcr_encode(block, gcr);
00487                         status.write_result = 
00488                             dst->write_block(tr, se, gcr, GCRBUFSIZE-1,
00489                                              status.read_result);
00490                     }
00491                     else
00492                     {
00493                         status.write_result = 
00494                             dst->write_block(tr, se, block, BLOCKSIZE,
00495                                              status.read_result);
00496                     }
00497 
00498                     if(status.read_result)
00499                     {
00500                         /* read error */
00501                         trackmap[se] = bs_error;
00502                         errors++;
00503                         if(retry_count == 0)
00504                         {
00505                             status.sectors_processed++;
00506                             /* FIXME: shall we get rid of this? */
00507                             message_cb( 1, "read error: %02x/%02x: %d",
00508                                         tr, se, status.read_result );
00509                         }
00510                     }
00511                     else
00512                     {
00513                         /* successfull read */
00514                         if(status.write_result)
00515                         {
00516                             /* write error */
00517                             trackmap[se] = bs_error;
00518                             errors++;
00519                             if(retry_count == 0)
00520                             {
00521                                 status.sectors_processed++;
00522                                 /* FIXME: shall we get rid of this? */
00523                                 message_cb(1, "write error: %02x/%02x: %d",
00524                                            tr, se, status.write_result);
00525                             }
00526                         }
00527                         else
00528                         {
00529                             /* successfull read and write, mark sector */
00530                             trackmap[se] = bs_copied;
00531                             cnt++;
00532                             status.sectors_processed++;
00533                         }
00534                     }
00535                     /* remaining sectors on this track */
00536                     if(!resend_trackmap)
00537                     {
00538                         scnt--;
00539                     }
00540 
00541                     status.track = tr;
00542                     status.sector= se;
00543 
00544                     status_cb(status);
00545 
00546                     if(dst->is_cbm_drive || !settings->warp)
00547                     {
00548                         se += (unsigned char) settings->interleave;
00549                         if(se >= sector_map[tr]) se -= sector_map[tr];
00550                     }
00551                 }
00552                 if(errors > 0 && settings->retries >= 0)
00553                 {
00554                     retry_count--;
00555                     scnt = errors;
00556                 }
00557             }
00558             while(retry_count >= 0 && errors > 0);
00559             if(errors)
00560             {
00561                 message_cb(1, "giving up...");
00562             }
00563         }
00564         if(settings->two_sided)
00565         {
00566             if(tr <= STD_TRACKS)
00567             {
00568                 if(tr + STD_TRACKS <= D71_TRACKS)
00569                 {
00570                     tr += (STD_TRACKS - 1);
00571                 }
00572             }
00573             else if(tr != D71_TRACKS)
00574             {
00575                 tr -= STD_TRACKS;
00576             }
00577         }
00578     }
00579 
00580     dst->close_disk();
00581     src->close_disk();
00582 
00583     return cnt;
00584 }
00585 
00586 
00587 static struct _transfers
00588 {
00589     const transfer_funcs *trf;
00590     const char *name, *abbrev;
00591 }
00592 transfers[] =
00593 {
00594     { &d64copy_std_transfer, "auto", "a%" },
00595     { &d64copy_std_transfer, "original", "o%" },
00596     { &d64copy_s1_transfer, "serial1", "s1" },
00597     { &d64copy_s2_transfer, "serial2", "s2" },
00598     { &d64copy_pp_transfer, "parallel", "p%" },
00599     { NULL, NULL, NULL }
00600 };
00601 
00602 char *d64copy_get_transfer_modes()
00603 {
00604     const struct _transfers *t;
00605     int size;
00606     char *buf;
00607     char *dst;
00608 
00609     size = 1; /* for terminating '\0' */
00610     for(t = transfers; t->trf; t++)
00611     {
00612         size += (strlen(t->name) + 1);
00613     }
00614 
00615     buf = malloc(size);
00616 
00617     if(buf)
00618     {
00619         dst = buf;
00620         for(t = transfers; t->trf; t++)
00621         {
00622             strcpy(dst, t->name);
00623             dst += (strlen(t->name) + 1);
00624         }
00625         *dst = '\0';
00626     }
00627 
00628     return buf;
00629 }
00630 
00631 
00632 int d64copy_get_transfer_mode_index(const char *name)
00633 {
00634     const struct _transfers *t;
00635     int i;
00636     int abbrev_len;
00637     int tm_len;
00638 
00639     if(NULL == name)
00640     {
00641         /* default transfer mode */
00642         return 0;
00643     }
00644 
00645     tm_len = strlen(name);
00646     for(i = 0, t = transfers; t->trf; i++, t++)
00647     {
00648         if(arch_strcasecmp(name, t->name) == 0)
00649         {
00650             /* full match */
00651             return i;
00652         }
00653         if(t->abbrev[strlen(t->abbrev)-1] == '%')
00654         {
00655             abbrev_len = strlen(t->abbrev) - 1;
00656             if(abbrev_len <= tm_len && arch_strncasecmp(t->name, name, tm_len) == 0)
00657             {
00658                 return i;
00659             }
00660         }
00661         else
00662         {
00663             if(strcmp(name, t->abbrev) == 0)
00664             {
00665                 return i;
00666             }
00667         }
00668     }
00669     return -1;
00670 }
00671 
00672 int d64copy_check_auto_transfer_mode(CBM_FILE cbm_fd, int auto_transfermode, int drive)
00673 {
00674     int transfermode = auto_transfermode;
00675 
00676     /* We assume auto is the first transfer mode */
00677     assert(strcmp(transfers[0].name, "auto") == 0);
00678 
00679     if (auto_transfermode == 0)
00680     {
00681         do {
00682             enum cbm_cable_type_e cable_type;
00683             unsigned char testdrive;
00684 
00685             /*
00686              * Test the cable
00687              */
00688 
00689             if (cbm_identify_xp1541(cbm_fd, (unsigned char)drive, NULL, &cable_type) == 0)
00690             {
00691                 if (cable_type == cbm_ct_xp1541)
00692                 {
00693                     /*
00694                      * We have a parallel cable, use that
00695                      */
00696                     transfermode = d64copy_get_transfer_mode_index("parallel");
00697                     break;
00698                 }
00699             }
00700 
00701             /*
00702              * We do not have a parallel cable. Check if we are the only drive
00703              * on the bus, so we can use serial2, at least.
00704              */
00705 
00706             for (testdrive = 4; testdrive < 31; ++testdrive)
00707             {
00708                 enum cbm_device_type_e device_type;
00709 
00710                 /* of course, the drive to be transfered to is present! */
00711                 if (testdrive == drive)
00712                     continue;
00713 
00714                 if (cbm_identify(cbm_fd, testdrive, &device_type, NULL) == 0)
00715                 {
00716                     /*
00717                      * My bad, there is another drive -> only use serial1
00718                      */
00719                     transfermode = d64copy_get_transfer_mode_index("serial1");
00720                     break;
00721                 }
00722             }
00723 
00724             /*
00725              * If we reached here with transfermode 0, we are the only
00726              * drive, thus, use serial2.
00727              */
00728             if (transfermode == 0)
00729                 transfermode = d64copy_get_transfer_mode_index("serial2");
00730 
00731         } while (0);
00732     }
00733 
00734     return transfermode;
00735 }
00736 
00737 int d64copy_read_image(CBM_FILE cbm_fd,
00738                        d64copy_settings *settings,
00739                        int src_drive,
00740                        const char *dst_image,
00741                        d64copy_message_cb msg_cb,
00742                        d64copy_status_cb stat_cb)
00743 {
00744     const transfer_funcs *src;
00745     const transfer_funcs *dst;
00746     int ret;
00747 
00748     message_cb = msg_cb;
00749     status_cb = stat_cb;
00750 
00751     src = transfers[settings->transfer_mode].trf;
00752     dst = &d64copy_fs_transfer;
00753 
00754     atom_dst = dst;
00755     atom_mustcleanup = 1;
00756 
00757     ret = copy_disk(cbm_fd, settings,
00758             src, (void*)(ULONG_PTR)src_drive, dst, (void*)dst_image, (unsigned char) src_drive);
00759 
00760     atom_mustcleanup = 0;
00761 
00762     return ret;
00763 }
00764 
00765 int d64copy_write_image(CBM_FILE cbm_fd,
00766                         d64copy_settings *settings,
00767                         const char *src_image,
00768                         int dst_drive,
00769                         d64copy_message_cb msg_cb,
00770                         d64copy_status_cb stat_cb)
00771 {
00772     const transfer_funcs *src;
00773     const transfer_funcs *dst;
00774 
00775     message_cb = msg_cb;
00776     status_cb = stat_cb;
00777 
00778     src = &d64copy_fs_transfer;
00779     dst = transfers[settings->transfer_mode].trf;
00780 
00781     return copy_disk(cbm_fd, settings,
00782             src, (void*)src_image, dst, (void*)(ULONG_PTR)dst_drive, (unsigned char) dst_drive);
00783 }
00784 
00785 void d64copy_cleanup(void)
00786 {
00787     /* if we were interrupted writing to the fs, make sure to
00788      * write anything that has already been started
00789      */
00790 
00791     if (atom_mustcleanup)
00792     {
00793         atom_dst->close_disk();
00794         atom_mustcleanup = 0;
00795     }
00796 }

Generated on Sun Apr 30 18:45:46 2006 for opencbm by  doxygen 1.4.2