Logo Search packages:      
Sourcecode: cbmlink version File versions  Download package

cbmlink.c

Go to the documentation of this file.
/**
 * @file cbmlink.c
 * The main program
 * @author Marko Mäkelä (msmakela@nic.funet.fi)
 */

/*
 * Copyright © 1994-1996 Marko Mäkelä and Olaf Seibert
 * Copyright © 2001,2002 Marko Mäkelä
 * Original Linux and Commodore 64/128/Vic-20 version by Marko Mäkelä
 * Ported to the PET and the Amiga series by Olaf Seibert
 * Restructured by Marko Mäkelä
 * 
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/** Version number of the program */
00030 #define VERSION "0.9.6"
/** Release date (day.month.year) */
00032 #define DATE "19.1.2003"

#if defined __AMIGA__
static const char version[] = "$VER: CBMLINK " VERSION " (" DATE ")";
#endif /* __AMIGA__ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_SIGNAL
# include <signal.h>
#endif /* NO_SIGNAL */
#include <errno.h>

#include "info.h"
#include "comm.h"
#include "commsel.h"

#include "mem.h"
#include "run.h"

#include "rdfile.h"
#include "wrfile.h"
#include "disk.h"
#include "qdisk.h"

#ifndef NO_SIGNAL
/** Signal handler
 * @param num     the signal number (usually SIGINT or SIGTERM)
 */
static void
00063 sig (int num)
{
  terminate ();
  signal (num, SIG_DFL);
  raise (num);
}
#endif /* NO_SIGNAL */

#ifdef __BCC__
void
perror (const char* s)
{
  fputs (s, stderr);
  fputs (": an error occurred\n", stderr);
}
# ifndef SMALLBUF
#  define SMALLBUF
# endif /* SMALLBUF */
#endif /* __BCC__ */

/** Pointer to host information */
00084 static const struct hostinfo* hostinfo = 0;
/** Transfer buffer */
static char buffer[
#ifdef SMALLBUF
               32768
#else /* SMALLBUF */
               65536
#endif /* SMALLBUF */
00092 ];

/** the memory bank */
00095 static unsigned bank = 0;
/** the communication primitives */
00097 static const struct comm* comm = 0;
/** the device number */
00099 static unsigned device = 8;
/** the secondary address */
00101 static int secondary = -1;
/** interleave factor */
00103 static unsigned interleave;

/** Resolve an address
 * @param addr    a character string specifying the address
 * @param endp    (output) pointer to the first non-number in addr
 * @return  the resolved address
 */
static unsigned
00111 resolve_addr (const char* addr, char** endp)
{
  unsigned a;
  const char* s = addr;
  if (*s == '@') s++;
  a = strtoul (s, endp, 0);
  if (!*s || (endp && *endp && **endp && **endp != ',')) {
    fprintf (stderr, "malformed address: %s\n", addr);
    exit (1);
  }

  if (s != addr) {
    unsigned char buf[2];
    if (save (comm, 
            hostinfo->host == P500 ||
            hostinfo->host == B128 ||
            hostinfo->host == B256
            ? 0x0f
            : 0, a, a + 2, buf))
      exit (2);
    a = (unsigned) buf[0] | (unsigned) buf[1] << 8;
  }

  return a;
}

/** redirected cartridge reset vector in jump_cart
 * @return the address of the reset handler code
 */
00140 #define CARTRESET (addr + 32)
/** alternative SYS start address for the cartridge
 * @return the address of the alternative entry point
 */
00144 #define ALTJUMP (addr + 18)

/** download code for starting a cartridge in jump_cart
 * @param addr    the desired address for the start-up code
 * @return  zero on success, nonzero on error
 */
static int
00151 jump_cart (unsigned addr)
{
  /** Vic-20 cartridge autostart signature */
  static const unsigned char autostart[5] = { 0x41, 0x30, 0xc3, 0xc2, 0xcd };
  /** Vic-20 cartridge bootstrapper code */
  static unsigned char auxcode[] = {
    /* this code starts the cartridge */
    0xa9, 0x7f,       /* lda #$7f  ; initialize the VIAs */
    0x8d, 0x3e, 0x91, /* sta $913e */
    0x8d, 0x3d, 0x91, /* sta $913d */
    0xa9, 0x00,       /* lda #$00 */
    0xa2, 0x0c,       /* ldx #$0c */
    0x9d, 0x10, 0x90, /* sta $9130,x */
    0xca,             /* dex */
    0x10, 0xfa,       /* bpl . - 4 */
    /* ALTJUMP: */
    0x78,             /* sei */
    0xa2, 0xff,       /* ldx #$ff */
    0x9a,             /* txs */
    0xd8,             /* cld */
    0xe8,             /* inx */
    0xa9, 0x41,       /* lda #$41 */
    0x8d, 0x04, 0xa0, /* sta $a004 (validate the boot signature) */
    /* auxstart - 1: */
    0x4c, 0, 0,       /* jmp $0000 (placeholder: original cart start) */
    /* CARTRESET: (executed when the game is reset) */
    0xa2, 0x0d,       /* ldx #$0d */
    0x8d, 0x04, 0xa0, /* stx $a004 (invalidate the boot signature) */
    0x20, 0x8d, 0xfd, /* jsr $fd8d (copied from the reset routine) */
    0x20, 0x52, 0xfd, /* jsr $fd52 */
    0x20, 0xf9, 0xfd, /* jsr $fdf9 */
    /* srvstart - 1: */
    0x20, 0, 0,       /* jsr $0000 (placeholder for server install) */
    0x4c, 0x38, 0xfd  /* jmp $fd38 */
  };

  /** address of the cartridge start routine */
  static unsigned char* const auxstart = &auxcode[30];
  /** address of the server start routine */
  static unsigned char* const srvstart = &auxcode[47];

  if (!hostinfo || hostinfo->host != Vic) {
    fputs ("jump_cart: target must be a Vic-20\n", stderr);
    return 1;
  }

  if (save (comm, 0, 0xa000, 0xa004 + sizeof autostart, buffer))
    return 2;
  if (memcmp (buffer + 4, autostart, sizeof autostart)) {
    fputs ("jump_cart: no autostart signature found\n", stderr);
    return 1;
  }
  /* patch the start-up code */
  memcpy (auxstart, buffer, 2);
  srvstart[0] = hostinfo->driver;
  srvstart[1] = hostinfo->driver >> 8;
  /* load the start-up code */
  if (load (comm, 0, addr, addr + sizeof auxcode, auxcode))
    return 2;
  /* patch the cartridge RESET vector */
  buffer[0] = CARTRESET;
  buffer[1] = CARTRESET >> 8;
  if (load (comm, 0, 0xa000, 0xa002, buffer))
    return 2;
  fprintf (stderr,
         "jump_cart: Restart with SYS%u or SYS%u after reset.\n",
         addr, ALTJUMP);
  /* start the cartridge */
  return jump (comm, 0, addr);
}

/** load a file to the memory of the remote host
 * @param cmd           the -l command
 * @param filename      name of the file to be loaded, "-" for stdin
 * @return        zero on success, nonzero on error
 */
static int
00228 load_file (const char* cmd, const char* filename)
{
  /** converted start address */
  unsigned addr = 0;
  /** length of transferred block */
  unsigned length;
  /** input file */
  FILE* f;
  /** address specifier string (NULL=none) */
  const char* addrspec = 0;
  /** cursor to command switch */
  const char* cmdc = cmd + 3;

  for (;;) {
    switch (cmdc[-1]) {
    case 0: /* load program to default address */
      break;
    case 'p':
      if (cmdc != cmd + 3)
      return -1;
      cmdc++;
      continue;
    case 'b': /* load program to the start of BASIC */
      if (*cmdc)
      return -1;
      break;
    case 'r': /* load program to relocated address */
    case 'o': /* load binary object to specified address */
      if (*cmdc == ',' && cmdc[1]) {
      addrspec = cmdc + 1;
      break;
      }
    default:
      return -1;
    }
    break;
  }

  f = *filename == '-' && !filename[1] ? stdin : fopen (filename, "rb");
  if (!f) {
    fputs (filename, stderr), perror (": fopen(reading)");
    return -2;
  }
  if (cmdc != cmd + 3) {
    /* skip the P00 header */
    if (26 != (length = fread (buffer, 1, 26, f)))
      goto loadEOF;
    if (memcmp (buffer, "C64File", 8)) {
      fprintf (stderr, "load %s: not a P00 file\n", filename);
      goto loadError;
    }
  }
  if (*cmdc != 'o') {
    /* read the start address from the file */
    addr = (unsigned) fgetc (f);
    addr |= (unsigned) (unsigned char) fgetc (f) << 8;
  }
  if (feof (f) || ferror (f)) {
  loadEOF:
    fprintf (stderr, "load %s: unexpected end of file\n", filename);
  loadError:
    if (f != stdin)
      fclose (f);
    return -2;
  }

  if (*cmdc == 'b')
    addr = hostinfo->basic;
  else if (addrspec) { /* override the load address */
    char* e;
    addr = resolve_addr (addrspec, &e);
    if (*e) {
      fprintf (stderr, "load %s: invalid address %s\n", filename, addrspec);
      goto loadError;
    }
  }
  length = fread (buffer, 1, sizeof buffer, f);
  if (!length || ferror (f))
    goto loadEOF;
#ifdef SMALLBUF
  else {
    int st = load (comm, bank, addr, (addr + length) & 0xffff, buffer);
    if (st) {
    loadDone:
      if (f != stdin) fclose (f);
      return st;
    }
    addr += length, addr &= 0xffff;
    length = fread (buffer, 1, sizeof buffer, f);
    if (ferror (f))
      goto loadEOF;
    if (!length)
      goto loadDone;
  }
#endif /* SMALLBUF */
  if (!feof (f)) {
    fprintf (stderr, "load %s: more than 64 kilobytes\n", filename);
    goto loadError;
  }
  if (f != stdin)
    fclose (f);
  return load (comm, bank, addr, (addr + length) & 0xffff, buffer);
}

/** save the memory of the remote host to a file
 * @param cmd           the -s command
 * @param filename      name of the file to be saved, "-" for stdout
 * @return        zero on success, nonzero on error
 */
static int
00338 save_file (const char* cmd, const char* filename)
{
  /** converted start address */
  unsigned start;
  /** converted end address */
  unsigned end;
  /** length of the data */
  unsigned length;
  /** output file */
  FILE* f;
  /** the address specifier string */
  const char* addrspec = cmd + 2;
  /** end-of-address pointer */
  char* e;

  switch (*addrspec++) {
  case ',': /* save program file */
    break;
  case 'o': /* save object file */
    if (*addrspec++ == ',')
      break;
    /* fall through */
  default:
    return -1;
  }

  f = filename[0] == '-' && !filename[1] ? stdout : fopen (filename, "wb");

  if (!f) {
    fputs (filename, stderr), perror (": fopen(writing)");
    return 2;
  }

  start = resolve_addr (addrspec, &e);
  if (*e != ',') {
  saveAddress:
    fprintf (stderr, "save %s: invalid address %s\n", filename, addrspec);
    if (f != stdout)
      fclose (f);
    return -1;
  }
  else
    addrspec = e + 1;

  end = resolve_addr (addrspec, &e);
  if (*e)
    goto saveAddress;

  length = end == start ? 0x10000 : (end - start) & 0xffff;
#ifdef SMALLBUF
  if (!length || length > sizeof buffer)
    end = (start + sizeof buffer) & 0xffff;
 nextHalf:
#endif /* SMALLBUF */
  if (save (comm, bank, start, end, buffer)) {
  saveError:
    if (f != stdout)
      fclose (f);
    return 2;
  }
  if (cmd[2] == ',') {
    fputc (start & 0xff, f);
    fputc (start >> 8, f);
  }
#ifdef SMALLBUF
  if (!length || length > sizeof buffer) {
    if (sizeof buffer != fwrite (buffer, 1, length, f))
      goto writeError;
    length -= sizeof buffer;
    goto nextHalf;
  }
#endif /* SMALLBUF */
  if (length != fwrite (buffer, 1, length, f)) {
#ifdef SMALLBUF
  writeError:
#endif /* SMALLBUF */
    fputs (filename, stderr), perror (": fwrite");
    goto saveError;
  }
  if (f != stdout)
    fclose (f);
  return 0;
}

/** load a file to the memory of a disk drive connected to the remote host
 * @param cmd           the -dml command
 * @param filename      name of the file to be loaded, "-" for stdin
 * @return        zero on success, nonzero on error
 */
static int
00428 drive_memory_load (const char* cmd, const char* filename)
{
  /** converted start address */
  unsigned addr = 0;
  /** input file */
  FILE* f;

  switch (cmd[4]) {
  case 0:
    break;
  case 'r': /* load program to relocated address */
  case 'o': /* load binary to specified address */
    if (cmd[5] == ',' && cmd[6]) {
      char* e;
      addr = resolve_addr (cmd + 6, &e);
      if (!*e)
      break;
      fprintf (stderr, "%s %s: invalid address %s\n",
             cmd, filename, cmd + 6);
    }
  default:
    return -1;
  }

  f = *filename == '-' && !filename[1] ? stdin : fopen (filename, "rb");
  if (!f) {
    fputs (filename, stderr), perror (": fopen(reading)");
    return -2;
  }
  if (cmd[4] != 'o') {
    /* read the start address from the file */
    addr = (unsigned) fgetc (f);
    addr |= (unsigned) (unsigned char) fgetc (f) << 8;
  }
  if (feof (f) || ferror (f)) {
    fprintf (stderr, "%s %s: unexpected end of file\n", cmd, filename);
    if (f != stdin)
      fclose (f);
    return -2;
  }

  if (disk_install (comm, hostinfo, device)) {
    if (f != stdin) fclose (f);
    fputs ("disk: installation failed\n", stderr);
    return 2;
  }

  if (disk_mwrite (comm, f, addr, buffer)) {
    fputs ("disk: mwrite failed\n", stderr);
    if (f != stdin) fclose (f);
    if (disk_remove (comm)) {
    diskRemove:
      fputs ("disk: removal failed\n", stderr);
      return 2;
    }
    return 1;
  }

  if (f != stdin)
    fclose (f);

  if (disk_remove (comm))
    goto diskRemove;

  return 0;
}

/** save the memory of a disk drive of the remote host to a file
 * @param cmd           the -dms or -dmc command
 * @param filename      name of the file to be saved, "-" for stdout
 * @return        zero on success, nonzero on error
 */
static int
00501 drive_memory_save (const char* cmd, const char* filename)
{
  /** converted start address */
  unsigned start = 0;
  /** converted end address */
  unsigned end = 0;
  /** output file */
  FILE* f;
  /** address specifier string */
  const char* addrspec = 0;
  /** first non-numeric character in the address specifier */
  char* e;

  switch (cmd[4]) {
  case ',':
    if (!cmd[5])
      return -1;
    addrspec = cmd + 5;
    break;
  case 'o': /* save memory to a program file */
    if (cmd[5] == ',' && cmd[6]) {
      addrspec = cmd + 6;
      break;
    }
  default:
    return -1;
  }

  start = resolve_addr (addrspec, &e);
  if (*e != ',') {
  saveAddress:
    fprintf (stderr, "%s %s: invalid address %s\n", cmd, filename, addrspec);
    return -1;
  }
  else
    addrspec = e + 1;

  end = resolve_addr (addrspec, &e);
  if (*e)
    goto saveAddress;

  f = *filename == '-' && !filename[1] ? stdout : fopen (filename, "wb");
  if (!f) {
    fputs (filename, stderr), perror (": fopen(writing)");
    return -2;
  }

  if (cmd[4] == ',') {
    fputc (start & 0xff, f);
    fputc (start >> 8, f);
  }

  if (disk_install (comm, hostinfo, device)) {
    if (f != stdout) fclose (f);
    fputs ("disk: installation failed\n", stderr);
    return 2;
  }

  if (cmd[3] == 'c') {
    if (disk_cread (comm, f, start, end, buffer)) {
      fputs ("disk: cread failed\n", stderr);
    disk_failed:
      if (f != stdout) fclose (f);
      if (disk_remove (comm)) {
      diskRemove:
      fputs ("disk: removal failed\n", stderr);
      return 2;
      }
      return 1;
    }
  }
  else {
    if (disk_mread (comm, f, start, end, buffer)) {
      fputs ("disk: mread failed\n", stderr);
      goto disk_failed;
    }
  }

  if (f != stdout)
    fclose (f);

  if (disk_remove (comm))
    goto diskRemove;
  return 0;
}

/** read the disk parameters
 * @param cmd           the -dr or -dw command
 * @param unit          (output) the drive unit number
 * @param track         (output) number of the start track
 * @param track_end     (output) number of the last track, plus 1
 * @return        0 on success; nonzero on failure
 */
static int
00595 disk_params (const char* cmd, unsigned* unit,
           unsigned* track, unsigned* track_end)
{
  /** end-of-number pointer for strtoul */
  char* endp = 0;
  /** start-of-number pointer for strtoul */
  const char* s = cmd + 3;

  if (*s && *s != ',') {
    *unit = strtoul (s, &endp, 0);
    if ((*endp && *endp != ',') || *unit > 1) {
      fputs (cmd, stderr);
      fputs (": invalid unit number: ", stderr);
      fputs (s, stderr);
      fputc ('\n', stderr);
      return -1;
    }
    if (*(s = endp) == ',') s++;
  }
  else {
    *unit = 0;
    if (*s == ',') s++;
  }

  if (*s && *s != ',') {
    interleave = strtoul (s, &endp, 0);
    if ((*endp && *endp != ',') || interleave > 999) {
      fputs (cmd, stderr);
      fputs (": invalid interleave factor: ", stderr);
      fputs (s, stderr);
      fputc ('\n', stderr);
      return -1;
    }
    if (*(s = endp) == ',') s++;
  }
  else {
    interleave = 5;
    if (*s == ',') s++;
  }

  if (*s && *s != ',') {
    *track = strtoul (s, &endp, 0);
    if ((*endp && *endp != ',') || *track > 999) {
      fputs (cmd, stderr);
      fputs (": invalid start track number: ", stderr);
      fputs (s, stderr);
      fputc ('\n', stderr);
      return -1;
    }
    if (*(s = endp) == ',') s++;
  }
  else {
    *track = 1;
    if (*s == ',') s++;
  }

  if (*s) {
    *track_end = strtoul (s, &endp, 0);
    if (*endp || *track_end > 1000 || *track_end <= *track) {
      fputs (cmd, stderr);
      fputs (": invalid end track number: ", stderr);
      fputs (s, stderr);
      fputc ('\n', stderr);
      return -1;
    }
    if (*(s = endp) == ',') s++;
  }
  else
    *track_end = 1000;

  return 0;
}

/** copy the contents of a disk accessible by the remote host to a file
 * @param cmd           the -dr command
 * @param filename      name of the file to be saved, "-" for stdout
 * @return        zero on success, nonzero on error
 */
static int
00674 drive_image_read (const char* cmd, const char* filename)
{
  /** output file */
  FILE* f;
  /** drive unit */
  unsigned unit;
  /** start and end track */
  unsigned track, track_end;

  if (disk_params (cmd, &unit, &track, &track_end))
    return -1;

  f = *filename == '-' && !filename[1] ? stdout : fopen (filename, "wb");
  if (!f) {
    fputs (filename, stderr), perror (": fopen(writing)");
    return -2;
  }

  if (disk_install (comm, hostinfo, device)) {
    fputs ("disk: installation failed\n", stderr);
    if (f != stdout) fclose (f);
    return 2;
  }

  if (disk_read (comm, unit, interleave, track, track_end, f, buffer)) {
    fputs ("disk: read failed\n", stderr);
    if (f != stdout)
      fclose (f);
    if (disk_remove (comm)) {
    diskRemove:
      fputs ("disk: removal failed\n", stderr);
      return 2;
    }
    return 1;
  }

  if (disk_remove (comm))
    goto diskRemove;

  return 0;
}

/** copy a file to a disk accessible by the remote host
 * @param cmd           the -dr command
 * @param filename      name of the file to be saved, "-" for stdout
 * @return        zero on success, nonzero on error
 */
static int
00722 drive_image_write (const char* cmd, const char* filename)
{
  /** output file */
  FILE* f;
  /** drive unit */
  unsigned unit;
  /** start and end track */
  unsigned track, track_end;

  if (disk_params (cmd, &unit, &track, &track_end))
    return -1;

  f = *filename == '-' && !filename[1] ? stdin : fopen (filename, "rb");
  if (!f) {
    fputs (filename, stderr), perror (": fopen(reading)");
    return -2;
  }

  if (disk_install (comm, hostinfo, device)) {
    fputs ("disk: installation failed\n", stderr);
    if (f != stdout) fclose (f);
    return 2;
  }

  if (disk_write (comm, unit, interleave, track, track_end, f, buffer)) {
    fputs ("disk: write failed\n", stderr);
    if (f != stdout)
      fclose (f);
    if (disk_remove (comm)) {
    diskRemove:
      fputs ("disk: removal failed\n", stderr);
      return 2;
    }
    return 1;
  }

  if (disk_remove (comm))
    goto diskRemove;

  return 0;
}

/** The main function
 * @param argc    argument count
 * @param argv    argument vector
 * @return  0 if successful
 */
int
00770 main (int argc, char** argv)
{
  char** param;

  atexit (terminate);
#ifndef NO_SIGNAL
  signal (SIGINT, sig);
  signal (SIGTERM, sig);
#endif /* NO_SIGNAL */

  /* process the command-line parameters */
  for (param = argv; ++param < &argv[argc]; ) {
    /** end-of-data pointer for strtoul calls */
    char* endp;
    if (**param != '-')
      goto Unrecognized;

    switch ((*param)[1]) {
    case 'b':
      if ((*param)[2])
      goto Unrecognized;
      if (param + 1 >= &argv[argc]) {
      Missing:
      fprintf (stderr, "%s: argument missing\n", *param);
      goto Usage;
      }
      bank = strtoul (*++param, &endp, 0);
      if (!*param || *endp || bank > 255) {
      fprintf (stderr, "-b %s: memory bank out of range\n", *param);
      return 1;
      }
      break;
    case 'c':
      if ((*param)[2])
      goto Unrecognized;
      if (param + 2 >= &argv[argc])
      goto Missing;
      if (hostinfo)
      terminate ();
      if (!(hostinfo = establish (param[1], param[2], &comm)))
      return 2;
      if (!bank && (hostinfo->host == B128 || hostinfo->host == B256))
      bank = 1; /* the default bank is 1 for the CBM 600/700 series */
      param += 2;
      fputs ("cbmlink: Commodore ", stderr);
      fputs (cbmname (hostinfo->host), stderr);
      fputs (" detected.\n", stderr);
      fprintf (stderr,
             "cbmlink: Driver address %#04x, BASIC start address %#04x.\n",
             hostinfo->driver, hostinfo->basic);

      break;
    case 'l':
      if (param + 1 >= &argv[argc])
      goto Missing;
      if (!hostinfo)
      goto Disconnected;
      switch (load_file (*param, param[1])) {
      case 0:
      break;
      case -1:
      goto Unrecognized;
      default:
      return 2;
      }
      param++;
      break;
    case 's':
      if (param + 1 >= &argv[argc])
      goto Missing;
      if (!hostinfo)
      goto Disconnected;
      switch (save_file (*param, param[1])) {
      case 0:
      break;
      case -1:
      goto Unrecognized;
      default:
      return 2;
      }
      param++;
      break;
    case 'r':
      if ((*param)[2])
      goto Unrecognized;
      if (!hostinfo) {
      Disconnected:
      fprintf (stderr, "%s: connection not established\n", *param);
      goto Usage;
      }
      if (run (comm))
      return 2;
      break;
    case 'j':
      switch ((*param)[2]) {
      case ',': /* jump to specified address */
      break;
      case 'c': /* jump to cartridge, specify restart address */
      if ((*param)[3] == ',')
        break;
      /* fall through */
      default:
      goto Unrecognized;
      }
      if (!hostinfo)
      goto Disconnected;
      else {
      unsigned addr;
      const char* addrspec = (*param)[2] == ','
        ? (*param) + 3
        : (*param) + 4;
      addr = resolve_addr (addrspec, &endp);
      if (*endp) {
        fprintf (stderr, "%s: invalid address\n", *param);
        return 1;
      }
      if (((*param)[2] == 'c') ? jump_cart (addr) : jump (comm, bank, addr))
        return 2;
      }
      break;
    case 'd':
      if ((*param)[2] != 's' && param + 1 >= &argv[argc])
      goto Missing;
      switch ((*param)[2]) {
      case 0: /* specify device number and secondary address */
      device = strtoul (param[1], &endp, 0);
      if (!param[1] || (*endp && *endp != ',') || device > 31) {
        fprintf (stderr, "-d %s: device number out of range\n", param[1]);
        return 1;
      }
      else if (*endp) {
        if (!*++endp || (secondary = strtol (endp + 1, &endp, 0)) < 0 ||
            secondary > 15) {
          fprintf (stderr, "-d %s: secondary address out of range\n",
                 param[1]);
          return 1;
        }
      }
      param++;
      continue;
      case 'm':
      switch ((*param)[3]) {
      default:
        goto Unrecognized;
      case 'l':
        if (!hostinfo)
          goto Disconnected;
        switch (drive_memory_load (*param, param[1])) {
        case 0:
          break;
        case -1:
          goto Unrecognized;
        default:
          return 2;
        }
      case 's':
      case 'c':
        if (!hostinfo)
          goto Disconnected;
        switch (drive_memory_save (*param, param[1])) {
        case 0:
          break;
        case -1:
          goto Unrecognized;
        default:
          return 2;
        }
      }
      param++;
      continue;
      case 'r':
      case 'w':
      if (!hostinfo)
        goto Disconnected;
      switch ((*param)[2] == 'r'
            ? drive_image_read (*param, param[1])
            : drive_image_write (*param, param[1])) {
      case 0:
        break;
      case -1:
        goto Unrecognized;
      default:
        return 2;
      }
      param++;
      continue;
      case 's':
      if ((*param)[3])
        goto Unrecognized;
      break;
      case 'c':
      case 'd':
      if ((*param)[3])
        goto Unrecognized;
      break;
      default:
      goto Unrecognized;
      }

      if (!hostinfo)
      goto Disconnected;

      if (rdfile_install (comm, hostinfo, device,
                    (*param)[2] == 'd' ? 0 : 15)) {
      fputs ("rdfile: installation failed\n", stderr);
      return 2;
      }

      switch ((*param)[2]) {
      case 's':
      if (rdfile (comm, "", stdout, buffer)) {
      rdfile_fail:
        fputs ("rdfile: rdfile failed\n", stderr);
        goto rdfile_failed;
      }
      putchar ('\n');
      break;
      case 'c':
      if (rdfile (comm, param[1], stdout, buffer))
        goto rdfile_fail;
      putchar ('\n');
      param++;
      break;
      case 'd':
      if (rdfile_directory (comm, param[1], stdout, buffer)) {
        fputs ("rdfile: directory failed\n", stderr);
      rdfile_failed:
        if (rdfile_remove (comm)) {
          fputs ("rdfile: removal failed\n", stderr);
          return 2;
        }
        return 1;
      }
      param++;
      break;
      }

    rdfile_remove:
      if (rdfile_remove (comm)) {
      fputs ("rdfile: removal failed\n", stderr);
      return 2;
      }
      break;
    case 'f':
      switch ((*param)[2]) {
      case 'r':
      case 'w':
      if (!(*param)[3])
        break;
      default:
      goto Unrecognized;
      }

      if (param + 1 >= &argv[argc])
      goto Missing;

      if (!hostinfo)
      goto Disconnected;

      switch ((*param)[2]) {
      case 'r':
      if (rdfile_install (comm, hostinfo, device,
                      secondary == -1 ? 0 : secondary)) {
        fputs ("rdfile: installation failed\n", stderr);
        return 2;
      }

      while (++param < &argv[argc]) {
        FILE* f = fopen (*param, "wb");
        if (!f) {
          fputs (*param, stderr), perror (": fopen");
          goto rdfile_failed;
        }
        else if (rdfile (comm, *param, f, buffer)) {
          fclose (f);
          goto rdfile_fail;
        }
        fclose (f);
      }
      goto rdfile_remove;
      case 'w':
      if (wrfile_install (comm, hostinfo, device,
                      secondary == -1 ? 1 : secondary)) {
        fputs ("wrfile: installation failed\n", stderr);
        return 2;
      }

      while (++param < &argv[argc]) {
        FILE* f = fopen (*param, "rb");
        if (!f) {
          fputs (*param, stderr), perror (": fopen");
        wrfile_failed:
          if (wrfile_remove (comm)) {
            fputs ("wrfile: removal failed\n", stderr);
            return 2;
          }
          return 1;
        }
        else if (wrfile (comm, *param, f, buffer)) {
          fclose (f);
          fputs ("wrfile: wrfile failed\n", stderr);
          goto wrfile_failed;
        }
        fclose (f);
      }

      if (wrfile_remove (comm)) {
        fputs ("wrfile: removal failed\n", stderr);
        return 2;
      }

      return 0;
      }

      break;
    case 'q':
      switch ((*param)[2]) {
      default:
      goto Unrecognized;
      case 'f':
      case 'r':
      if ((*param)[3])
        goto Unrecognized;
      break;
      case 'w':
      switch ((*param)[3]) {
      default:
        goto Unrecognized;
      case 0:
        interleave = 5;
        break;
      case ',':
        interleave = strtoul ((*param) + 4, &endp, 0);
        if (*endp || interleave > 999) {
          fputs (*param, stderr);
          fputs (": invalid interleave factor\n", stderr);
          goto Unrecognized;
        }
        break;
      }
      break;
      }
      if (param + 1 >= &argv[argc])
      goto Missing;
      if (!hostinfo)
      goto Disconnected;

      if (rdfile_install (comm, hostinfo, device, 15)) {
      fputs (*param, stderr);
      fputs (": rdfile installation failed\n", stderr);
      return 2;
      }
      else if ((*param)[2] == 'f') {
      /* quick format */
      unsigned i;
      char name[16], id1, id2;
      /* fill the name with shifted spaces */
      memset (name, 0xa0, sizeof name);
      for (i = 0; param[1][i] && param[1][i] != ','; i++);
      memcpy (name, param[1], i < 16 ? i : 16);
      if ((id2 = id1 = param[1][i] == ',' ? param[1][++i] : 0))
        id2 = param[1][++i];
      if (qdisk_format (comm, stdout, name, id1, id2)) {
        fputs ("-qf: communication failure\n", stderr);
        goto rdfile_failed;
      }
      putchar ('\n');
      param++;
      goto rdfile_remove;
      }
      else {
      FILE* f = fopen (param[1], (*param)[2] == 'r' ? "wb" : "rb");
      if (!f) {
        fputs (param[1], stderr), perror (": fopen");
        goto rdfile_failed;
      }
      if (qdisk_install (comm, hostinfo)) {
        fclose (f);
        goto rdfile_failed;
      }
      if ((*param)[2] == 'r'
          ? qdisk_read (comm, f, buffer)
          : qdisk_write (comm, f, interleave, buffer)) {
        fclose (f);
        if (qdisk_remove (comm))
          fputs ("qdisk: removal failed\n", stderr);
        return 2;
      }
      if (qdisk_remove (comm)) {
        fputs ("qdisk: removal failed\n", stderr);
        return 2;
      }
      fclose (f);
      param++;
      }
      break;

    case '?':
    case 'h':
      if ((*param)[2])
      goto Unrecognized;
      fputs ("CBMLINK " VERSION ": data communications and remote management "
           "for Commodore computers.\n"
           "Options:\n"
           "-h\t"
           "Get this help message\n"
           "-c protocol device\t"
           "Specify the communication protocol and interface\n"
           "-b bank\t"
           "Specify the memory bank (default=1 for 600/700, 0 for others)\n"
           "-l[p] file.prg\t"
           "Load a program at its specified absolute address\n"
           "-l[p]b file.prg\t"
           "Load a program, relocated to the start of BASIC\n"
           "-l[p]r,address file.prg\t"
           "Load and relocate a program to the specified address\n"
           "-l[p]o,address file.bin\t"
           "Load a binary file to the specified address\n"
           "-s,address,address file.prg\t"
           "Save memory area to a program file\n"
           "-so,address,address file.bin\t"
           "Save memory area to a binary file\n"
           "-r\t"
           "Disable the server and perform a RUN command\n"
           "-j,address\t"
           "Disable the server and jump to the specified address\n"
           "-jc,restart_address\t"
           "Launch a VIC-20 cartridge\n"
           "-d drive[,secondary]\t"
           "Specify the device number and secondary address (default=8)\n"
           "-dr[0|1][,interleave] file.d64\t"
           "Read a disk in unit 0 or 1 to an image file\n"
           "-dw[0|1][,interleave] file.d64\t"
           "Write an image file to a disk in unit 0 or 1\n"
           "-dmc,address,address file.prg\t"
           "Save drive controller memory area to a program file\n"
           "-dmco,address,address file.bin\t"
           "Save drive controller memory area to a binary file\n"
           "-dml file.prg\t"
           "Load a program to drive memory\n"
           "-dmlr,address file.prg\t"
           "Load a program to the specified address in drive memory\n"
           "-dmlo,address file.bin\t"
           "Load a binary file to the specified address in drive memory\n"
           "-dms,address,address file.prg\t"
           "Save drive memory area to a program file\n"
           "-dmso,address,address file.bin\t"
           "Save drive memory area to a binary file\n"
           "-dc command\t"
           "Execute a disk drive command\n"
           "-ds\t"
           "Query disk drive status\n"
           "-dd pattern\t"
           "Fetch the disk directory\n"
           "-fr file ...\t"
           "Read files from disk\n"
           "-fw file ...\t"
           "Write files to disk\n"
           "-qf name,id\t"
           "Quick format a 1541 disk\n"
           "-qr file.d64\t"
           "Quick read a 1541 disk\n"
           "-qw[,interleave] file.d64\t"
           "Quick write a 1541 disk\n"
           "\n"
           "Indirect addresses are prefixed with @, e.g. @0x2d or @45.\n",
           stderr);
      return 0;

    default:
    Unrecognized:
      fprintf (stderr, "cbmlink: unrecognized option `%s'\n", *param);
    Usage:
      fputs ("Type \"cbmlink -h\" to get help.\n", stderr);
      return 1;
    }
  }

  if (!hostinfo)
    goto Usage;

  return 0;
}

Generated by  Doxygen 1.6.0   Back to index