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

c2n.c

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

/* Copyright © 2001,2002 Marko Mäkelä.

   This file is part of C2N, a program for processing data tapes in
   Commodore C2N format and other formats.

   C2N 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, or (at your option)
   any later version.

   C2N 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.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/** Program version */
00028 #define VERSION "1.1.4"
#ifdef __AMIGA__
/** Release date (day.month.year) */
# define DATE "29.12.2002"
#endif /* __AMIGA__ */

/** Undef this to disable the -E option */
00035 #define USE_PCM
/** Undef this to disable the -D option */
00037 #define USE_TAP
/** Undef this to disable the -c option */
00039 #define USE_SERIAL

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

#ifdef USE_PCM
# include <math.h>
#endif /* USE_PCM */

#undef HAS_SERIAL
#ifdef USE_SERIAL
# if defined __unix || defined __unix__ || defined __APPLE__
#  include <termios.h>
#  include <unistd.h>
#  include <fcntl.h>
#  define HAS_SERIAL
# endif /* defined __unix || defined __unix__ || defined __APPLE__ */

# if defined __WIN32 || defined __WIN32__
#  include <windows.h>
#  define HAS_SERIAL
#  ifndef WIN32
#   define WIN32
#  endif /* WIN32 */
/** The name of the serial device */
static const char* serialdev;
/** Report a Windows error for accessing the serial device
 * @param op      the failed operation
 */
static void
report_error (const char* op)
{
  char* msg;
  long err = GetLastError ();
  FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
             FORMAT_MESSAGE_FROM_SYSTEM | 
             FORMAT_MESSAGE_IGNORE_INSERTS,
             0,
             err,
             0, /* default language */
             (LPTSTR) &msg,
             0,
             0);
  AnsiToOem (msg, msg);
  fputs ("-c ", stderr);
  fputs (serialdev, stderr);
  fputs (": ", stderr);
  fputs (op, stderr);
  fprintf (stderr, " failed with code %ld: ", err);
  fputs (msg, stderr);
  LocalFree (msg);
}
# endif /* __WIN32 */

# if defined __AMIGA__
static const char version[] = "$VER: C2N " VERSION " (" DATE ")";
#  include <proto/exec.h>
#  include <devices/serial.h>
#  include <devices/timer.h>
#  include <dos/dos.h>
#  include <clib/alib_protos.h>
#  define HAS_SERIAL
# endif /* __AMIGA__ */
#endif /* USE_SERIAL */

#include "c2n.h"
#include "decode.h"
#include "encode.h"
#include "oric_d.h"
#include "oric_e.h"

/** Flag: verbose mode */
00114 unsigned verbose = 0;

/** Name of the current file */
00117 static const char* currentFile;
/** Maximum number of encountered errors */
00119 static unsigned error_thresold = 10;
/** Number of encountered errors */
00121 static unsigned error_count;

/** Error reporter
 * @param pe      the unexpected pulse or error code
 * @param block   the block in which the error occurred (0=first header)
 * @param pos     number of bytes decoded from the stream
 * @return  nonzero on fatal error
 */
static unsigned
00130 error_report (unsigned pe, unsigned block, unsigned pos)
{
  if (currentFile)
    fputs (currentFile, stderr), fputs (": ", stderr);
  switch (pe) {
  case Pause:
    fputs ("unexpected break in the pulse stream", stderr);
    break;
  case Short:
    fputs ("unexpected short pulse", stderr);
    break;
  case Medium:
    fputs ("unexpected medium pulse", stderr);
    break;
  case Long:
    fputs ("unexpected long pulse", stderr);
    break;
  case Parity:
    fputs ("parity error", stderr);
    break;
  case Checksum:
    fputs ("block checksum mismatch", stderr);
    break;
  case NoFirst:
    fputs ("first copy is missing", stderr);
    break;
  case NoSecond:
    fputs ("second copy is missing", stderr);
    break;
  case LongBlock:
    fprintf (stderr, "block %u is %u bytes longer than expected\n",
           block, pos);
    return 1;
  case ShortBlock:
    fprintf (stderr, "block %u is %u bytes shorter than expected\n",
           block, pos);
    return 1;
  case Unexpected:
    fprintf (stderr, "unexpected header tag 0x%02x in block %u\n",
           pos, block);
    goto thresold;
  case Mismatch:
    fputs ("mismatch between first and second copy", stderr);
    break;
  case Countdown:
    fprintf (stderr, "unrecognized countdown byte 0x%02x at block %u\n",
           pos, block);
    return 1;
  default:
    fputs ("unexpected internal error", stderr);
    break;
  }
  fprintf (stderr, " at %u decoded bytes of block %u\n", pos, block);

 thresold:
  if (++error_count > error_thresold) {
    fprintf (stderr, "more than %u errors; aborted\n", error_thresold);
    return 1;
  }
  return 0;
}

#ifdef HAS_SERIAL
# ifdef WIN32
/** file handle for the serial device (0=none) */
static HANDLE serialfd = 0;
/** previous terminal input/output settings for the serial line */
static DCB tio;
/** previous timeout settings for the serial line */
static COMMTIMEOUTS tio2;
/** new terminal input/output settings for the serial line */
static DCB newtio;
/** new timeout settings for the serial line */
static COMMTIMEOUTS newtio2;
# elif defined __AMIGA__
/** extended input/output request structure */
static struct IOExtSer ser;
/** timer request */
static struct timerequest* timer;
/** dummy serial file descriptor */
#  define serialfd !!ser.IOSer.io_Device
# else /* !WIN32 && !AMIGA */
/** file descriptor for the serial device (0=none) */
static int serialfd = 0;
/** previous terminal input/output settings for the serial line */
static struct termios tio;
/** new terminal input/output settings for the serial line */
static struct termios newtio;
# endif /* !WIN32 && !AMIGA */
#endif /* HAS_SERIAL */

#ifdef USE_PCM
/** A PCM sample of a pulse */
00223 struct pcm_pulse
{
00225   char* data;           /**< the data bytes */
00226   unsigned length;      /**< length of the data bytes */
};

/** PCM sample types */
00230 enum pcm_type
{
00232   eight,          /**< eight bits */
00233   lsb16,          /**< sixteen bits, least significant byte first */
00234   msb16,          /**< sixteen bits, most significant byte first */
00235   eight_,         /**< eight bits, inverted phase */
00236   lsb16_,         /**< sixteen bits, LSB first, inverted phase */
00237   msb16_          /**< sixteen bits, MSB first, inverted phase */
};

/** The selected PCM sample type */
00241 static enum pcm_type samplebits;

/** sample rate in Hz */
00244 static unsigned samplerate = 0;
/** number of output channels */
00246 static unsigned channels = 1;
/** flag: signed output (as opposed to unsigned) */
00248 static int sign = 1;

/** PCM samples of the pulses */
00251 static struct pcm_pulse pcm_short, pcm_medium, pcm_long;
/** File descriptor for PCM output */
00253 static FILE* pcm_file;

/** Compute a stream of pulse code modulation (PCM) samples for a pulse
 * @param sample  the sample stream to be computed
 * @param hwidth  the high pulse width/16µs
 * @param lwidth  the low pulse width/16µs (0=high is the full width/8µs)
 * @return        the computed sample
 */
static struct pcm_pulse*
00262 pcm_compute (struct pcm_pulse* sample,
           unsigned hwidth,
           unsigned lwidth)
{
  double arg = 0, inc;
  char* c;
  short mult = 0, bias;
  static const double twopi = 2 * 3.14159265358979323846;

  sample->length = lwidth
    ? ((hwidth *= samplerate) / 250000 + (lwidth *= samplerate) / 250000)
    : (hwidth *= samplerate) / 125000;
  sample->length *= channels;

  switch (samplebits) {
  case eight:
    mult = 127; break;
  case eight_:
    mult = -127; break;
  case lsb16: case msb16:
    sample->length *= 2; mult = 32767; break;
  case lsb16_: case msb16_:
    sample->length *= 2; mult = -32767; break;
  }

  if (sign) {
    switch (samplebits) {
    case eight:
    case eight_:
      bias = -128; break;
    default:
      bias = -32768; break;
    }
  }
  else
    bias = 0;

  free (sample->data);
  if (!(sample->data = malloc (sample->length)))
    return 0;

  if (lwidth) {
    const char* const end = sample->data + hwidth / 250000 * channels;
    for (inc = twopi / (hwidth / 125000), c = sample->data;
       c < end; arg += inc) {
      short s = bias + mult * sin (arg);
      unsigned ch = channels;
      switch (samplebits) {
      case eight: case eight_:
      while (ch--) *c++ = s;
      break;
      case lsb16: case lsb16_:
      while (ch--) { *c++ = s; *c++ = s >> 8; }
      break;
      case msb16: case msb16_:
      while (ch--) { *c++ = s >> 8; *c++ = s; }
      break;
      }
    }
  }
  else
    lwidth = hwidth, c = sample->data;

  for (inc = twopi / (lwidth / 125000);
       c < sample->data + sample->length; arg += inc) {
    short s = bias + mult * sin (arg);
    unsigned ch = channels;
    switch (samplebits) {
    case eight: case eight_:
      while (ch--) *c++ = s;
      break;
    case lsb16: case lsb16_:
      while (ch--) { *c++ = s; *c++ = s >> 8; }
      break;
    case msb16: case msb16_:
      while (ch--) { *c++ = s >> 8; *c++ = s; }
      break;
    }
  }
  return sample;
}
#endif /* USE_PCM */

/** Cleanup function */
static void
00347 cleanup (void)
{
#ifdef HAS_SERIAL
# ifdef WIN32
  if (serialfd && serialfd != INVALID_HANDLE_VALUE) {
    PurgeComm (serialfd, PURGE_TXCLEAR | PURGE_RXCLEAR);
    SetCommState (serialfd, &tio);
    SetCommTimeouts (serialfd, &tio2);
    SetCommBreak (serialfd);
    Sleep (20); /* send break signal for at least 20 milliseconds */
    ClearCommBreak (serialfd);
    CloseHandle (serialfd);
    serialfd = 0;
  }
# elif defined __AMIGA__
  if (ser.IOSer.io_Device && ser.IOSer.io_Message.mn_ReplyPort) {
    ser.IOSer.io_Command = CMD_CLEAR;
    if (DoIO ((struct IORequest*) &ser))
      fprintf (stderr, "cleanup: DoIO (CMD_CLEAR) returned %d\n",
             ser.IOSer.io_Error);
    else {
      ser.IOSer.io_Command = SDCMD_BREAK;
      if (DoIO ((struct IORequest*) &ser))
      fprintf (stderr, "cleanup: DoIO (SDCMD_BREAK) returned %d\n",
             ser.IOSer.io_Error);
    }
  }
  if (ser.IOSer.io_Device)
    CloseDevice ((struct IORequest*) &ser);
  if (ser.IOSer.io_Message.mn_ReplyPort)
    DeletePort (ser.IOSer.io_Message.mn_ReplyPort);
  memset (&ser, 0, sizeof ser);
  if (timer) {
    if (timer->tr_node.io_Message.mn_ReplyPort)
      DeletePort (timer->tr_node.io_Message.mn_ReplyPort);
    CloseDevice ((struct IORequest*) timer);
    DeleteExtIO ((struct IORequest*) timer);
    timer = 0;
  }
# else /* !WIN32 && !AMIGA */
  if (serialfd > 0) {
    tcsetattr (serialfd, TCSADRAIN, &tio);
    tcsendbreak (serialfd, 0);
    close (serialfd);
    serialfd = 0;
  }
# endif /* !WIN32 && !AMIGA */
#endif /* HAS_SERIAL */

#ifdef USE_PCM
  if (pcm_file)
    fclose (pcm_file);
  free (pcm_short.data), free (pcm_medium.data), free (pcm_long.data);
#endif /* USE_PCM */
}

/** Signal handler
 * @param num     the signal number
 */
static void
00407 sig (int num)
{
  cleanup ();
  signal (num, SIG_DFL);
  raise (num);
}

#ifdef HAS_SERIAL
/** input/output buffer */
static char buf[8192];
/** number of bytes read from or written to buf */
static unsigned datain;
# if defined __AMIGA__
/** second output buffer */
static char buf2[sizeof buf];
/** pointer to the active output buffer */
static char* actbuf = buf;
/** flag: read or write pending */
static int pending = 0;

/** Flush the input/output operation on the serial line
 * @return  the WaitIO exit status
 */
static BYTE
flushout (void)
{
  const long waitsigs = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
    1L << ser.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
  for (;;) {
    if (CheckIO ((struct IORequest*) &ser))
      return WaitIO ((struct IORequest*) &ser);
    if (Wait (waitsigs) & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)) {
      AbortIO ((struct IORequest*) &ser);
      WaitIO ((struct IORequest*) &ser);
      raise (SIGINT);
    }
  }
}
#  define datalen ser.IOSer.io_Actual
# else /* !AMIGA */
/** number of bytes in buf */
static unsigned datalen;
# endif /* !AMIGA */

/** Read a pulse from the serial line
 * @return  the read pulse
 */
static enum pulse
sp_read (void)
{
  char c;
  if (!serialfd)
    return Pause;
 again:
  if (datain < datalen)
    c = buf[datain++];
  else {
# ifdef WIN32
    unsigned long len;
    if (!ReadFile (serialfd, buf, sizeof buf, &len, 0) || !len) {
      cleanup ();
      return Pause;
    }
    datalen = len;
    datain = 0;
# elif defined __AMIGA__
    const long waitsigs = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
      1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit |
      1L << ser.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
    if (pending)
      goto pending;
    datain = 0;
    ser.IOSer.io_Command = CMD_READ;
    ser.IOSer.io_Data = &buf;
    ser.IOSer.io_Length = sizeof buf;
    ser.IOSer.io_Actual = 0;
    ser.IOSer.io_Flags = IOF_QUICK;
    BeginIO ((struct IORequest*) &ser);
    if (ser.IOSer.io_Flags & IOF_QUICK)
      ser.IOSer.io_Flags = 0;
    else {
      pending = 1;
    pending:
      timer->tr_node.io_Command = TR_ADDREQUEST;
      timer->tr_time.tv_secs = 2;
      timer->tr_time.tv_micro = 0;
      SendIO ((struct IORequest*) timer);
      for (;;) {
      if (Wait (waitsigs) & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)) {
        pending = 0;
        AbortIO ((struct IORequest*) timer);
        WaitIO ((struct IORequest*) timer);
        AbortIO ((struct IORequest*) &ser);
        WaitIO ((struct IORequest*) &ser);
        raise (SIGINT);
      }
      if (CheckIO ((struct IORequest*) &ser)) {
        pending = 0;
        AbortIO ((struct IORequest*) timer);
        WaitIO ((struct IORequest*) timer);
        if (WaitIO ((struct IORequest*) &ser)) {
          fprintf (stderr, "sp_read: WaitIO (CMD_READ) returned %d\n",
                 ser.IOSer.io_Error);
          goto clean;
        }
        break;
      }
      else if (CheckIO ((struct IORequest*) timer)) {
        WaitIO ((struct IORequest*) timer);
        if (datain < datalen)
          break;
        AbortIO ((struct IORequest*) &ser);
        WaitIO ((struct IORequest*) &ser);
        pending = 0;
      clean:
        cleanup ();
        return Pause;
      }
      }
    }
# else /* !WIN32 && !AMIGA */
    if (!(datalen = read (serialfd, buf, 1))) {
      cleanup ();
      return Pause;
    }
    datain = 0;
# endif /* !WIN32 && !AMIGA */
    goto again;
  }
  switch (c) {
  case 'A':
    return Pause;
  case 'B':
    return Short;
  case 'C':
    return Medium;
  case 'D':
    return Long;
  }
  fprintf (stderr, "unrecognized character 0x%02x in pulse input stream\n",
         (unsigned char) c);
  goto again;
}
#endif /* HAS_SERIAL */

/** Read a pulse from standard input
 * @return  the read pulse
 */
static enum pulse
00556 p_read (void)
{
  int c = getchar ();
  switch (c) {
  case EOF:
  case 'A':
    return Pause;
  case 'B':
    return Short;
  case 'C':
    return Medium;
  case 'D':
    return Long;
  }
  fprintf (stderr, "unrecognized character 0x%02x in pulse input stream\n", c);
  return Pause;
}

#ifdef USE_TAP
/** short pulse width in a tape image (unit=8/985248 seconds) */
00576 static unsigned tap_short;
/** medium pulse width in a tape image (unit=8/985248 seconds) */
00578 static unsigned tap_medium;
/** long pulse width in a tape image (unit=8/985248 seconds) */
00580 static unsigned tap_long;

/** Read a pulse from a tape image
 * @return  the read pulse
 */
static enum pulse
00586 tap_read (void)
{
  int c = getchar ();
  if (!c || c == EOF)
    return Pause;
  if (c <= tap_short)
    return Short;
  if (c <= tap_medium)
    return Medium;
  return Long;
}
#endif /* USE_TAP */

/** pulse look-up table */
00600 static char pulses[] = { 'A', 'B', 'C', 'D' };

/** Write a pulse to the serial line or to standard output
 * @param p the pulse to be written
 */
static void
00606 p_write (enum pulse p)
{
  if (p >= sizeof pulses)
    return;
#ifdef HAS_SERIAL
  if (serialfd) {
# if defined __AMIGA__
    actbuf[datain++] = pulses[p];
# else /* !AMIGA */
    buf[datain++] = pulses[p];
# endif /* !AMIGA */
    if (p == Pause || datain == sizeof buf) {
# ifdef WIN32
      unsigned long len;
      WriteFile (serialfd, buf, datain, &len, 0);
# elif defined __AMIGA__
      if (pending)
      flushout ();
      pending = 1;
      ser.IOSer.io_Command = CMD_WRITE;
      ser.IOSer.io_Length = datain;
      ser.IOSer.io_Data = actbuf;
      SendIO ((struct IORequest*) &ser);
      actbuf = actbuf != buf ? buf : buf2;
# else /* !WIN32 && !AMIGA */
      write (serialfd, buf, datain);
# endif /* !WIN32 && !AMIGA */
      datain = 0;
    }
  }
  else
#endif /* HAS_SERIAL */
    putchar (pulses[p]);
}

#ifdef USE_PCM
/** Write audio samples corresponding to a pulse to standard output
 * @param p the pulse to be written
 */
static void
00646 p_write_pcm (enum pulse p)
{
  const struct pcm_pulse* pl;
  switch (p) {
  default:
    return;
  case Short:
    pl = &pcm_short;
    break;
  case Medium:
    pl = &pcm_medium;
    break;
  case Long:
    pl = &pcm_long;
    break;
  }

  fwrite (pl->data, pl->length, 1, pcm_file ? pcm_file : stdout);
}
#endif /* USE_PCM */

/** The main function
 * @param argc    argument count
 * @param argv    argument vector
 * @return  0 if successful
 */
int
00673 main (int argc, char** argv)
{
  /** mode of operation */
  enum { Encode,
#ifdef USE_TAP
       DecodeTAP,
#endif /* USE_TAP */
       Decode
  } mode = Encode;
  /** tape format type */
  enum { CBM = 0, PLUS4, ORIC1 } format = CBM;
  /** flag: raw operation (input and output pulse streams) */
  int raw = 0;
  /** program name */
  const char* prog = *argv;
  /** short pulse width (unit=8 microseconds) */
  static unsigned pulse_short = 0;
  /** medium pulse width (unit=8 microseconds) */
  static unsigned pulse_medium = 0;
  /** long pulse width (unit=8 microseconds) */
  static unsigned pulse_long = 0;
  /** initial sync pulse count */
  static unsigned sync_begin = 0;
  /** intra-block sync pulse count */
  static unsigned sync_intra = 0;

  atexit (cleanup);
  signal (SIGINT, sig);

  /* process the options */
  for (argv++; argc > 1 && **argv == '-'; argv++, argc--) {
    const char* opts = *argv;

    if (!strcmp (opts, "--")) { /* disable processing further options */
      argv++;
      argc--;
      break;
    }

    while (*++opts) /* process all flags */
      switch (*opts) {
#ifdef HAS_SERIAL
      case 'c':
      if (argc <= 2 || serialfd)
        goto Usage;
# ifdef WIN32
      if ((serialfd = CreateFile (serialdev = *++argv,
                            GENERIC_WRITE | GENERIC_READ,
                            0, 0, OPEN_EXISTING, 0, 0)) ==
          INVALID_HANDLE_VALUE) {
        report_error ("CreateFile");
        return 2;
      }

      if (!GetCommState (serialfd, &tio)) {
        report_error ("GetCommState");
      c2n232fail:
        CloseHandle (serialfd);
        serialfd = INVALID_HANDLE_VALUE;
        return 2;
      }
      else if (!GetCommTimeouts (serialfd, &tio2)) {
        report_error ("GetCommTimeouts");
        goto c2n232fail;
      }
      else {
        memcpy (&newtio, &tio, sizeof newtio);
        memcpy (&newtio2, &tio2, sizeof newtio2);
        newtio.BaudRate = CBR_38400;
        newtio.fBinary = 1;
        newtio.fParity = newtio.fOutxCtsFlow = newtio.fOutxDsrFlow = 0;
        newtio.fDtrControl = DTR_CONTROL_DISABLE;
        newtio.fDsrSensitivity = 0;
        newtio.fOutX = 1, newtio.fInX = 0;
        newtio.fErrorChar = newtio.fNull = 0;
        newtio.fRtsControl = RTS_CONTROL_DISABLE;
        newtio.fAbortOnError = 0;
        newtio.ByteSize = 8;
        newtio.Parity = NOPARITY;
        newtio.StopBits = ONESTOPBIT;
        newtio.XonChar = 021; /* DC1, ctrl-q */
        newtio.XoffChar = 023; /* DC3, ctrl-s */

        newtio2.ReadIntervalTimeout = 100; /* 1/10th second */
        newtio2.ReadTotalTimeoutConstant = 100; /* 1/10th second */
        newtio2.ReadTotalTimeoutMultiplier =
          newtio2.WriteTotalTimeoutMultiplier =
          newtio2.WriteTotalTimeoutConstant = 0;

        if (!SetCommState (serialfd, &newtio)) {
          report_error ("SetCommState");
          goto c2n232fail;
        }
        else if (!SetCommTimeouts (serialfd, &newtio2)) {
          report_error ("SetCommTimeouts");
          goto c2n232fail;
        }
        else {
          unsigned long len;
          /* send a NUL character and await a response */
          if (!WriteFile (serialfd, "", 1, &len, 0)) {
            report_error ("WriteFile");
            goto c2n232fail;
          }
          if (!ReadFile (serialfd, buf, sizeof buf, &len, 0)) {
            report_error ("ReadFile");
            goto c2n232fail;
          }
          if (!len || buf[len - 1] != '0') {
            fputs ("-c ", stderr);
            fputs (*argv, stderr);
            fputs (len
                 ? ": unexpected response from C2N232 device\n"
                 : ": no response from C2N232 device\n",
                 stderr);
            goto c2n232fail;
          }

          newtio2.ReadIntervalTimeout = 100; /* 1/10th second */
          newtio2.ReadTotalTimeoutConstant = 2000; /* 2 seconds */
          if (!SetCommTimeouts (serialfd, &newtio2)) {
            report_error ("SetCommTimeouts");
            goto c2n232fail;
          }
        }
      }
# elif defined __AMIGA__
      memset (&ser, 0, sizeof ser);
      if (!(ser.IOSer.io_Message.mn_ReplyPort =
            (struct MsgPort*) CreatePort (0, 0))) {
      createport:
        fputs ("CreatePort failed\n", stderr);
      c2n232fail:
        cleanup ();
        return 2;
      }
      if (OpenDevice ((const unsigned char*) *++argv, 0,
                  (struct IORequest*) &ser, 0)) {
        fprintf (stderr, "-c %s: OpenDevice returned %d\n",
               *argv, ser.IOSer.io_Error);
        goto c2n232fail;
      }
      else {
        struct MsgPort* timerport = CreatePort (0, 0);
        if (!timerport)
          goto createport;
        if (!(timer =
            (struct timerequest*) CreateExtIO (timerport, sizeof timer))) {
          DeletePort (timerport);
          fputs ("CreateExtIO failed\n", stderr);
          goto c2n232fail;
        }
        if (OpenDevice ((const unsigned char*) "timer.device", UNIT_VBLANK,
                    (struct IORequest*) timer, 0L)) {
          fprintf (stderr, "OpenDevice for timer.device returned %d\n",
                 timer->tr_node.io_Error);
          goto c2n232fail;
        }
      }
      ser.IOSer.io_Command = SDCMD_SETPARAMS;
      ser.io_Baud = 38400;
      ser.io_ExtFlags = 0;
      ser.io_RBufLen = 2 * sizeof buf;
      ser.io_ReadLen = ser.io_WriteLen = 8;
      ser.io_StopBits = 1;
      ser.io_SerFlags = SERF_RAD_BOOGIE | SERF_XDISABLED;
      if (DoIO ((struct IORequest*) &ser)) {
        fprintf (stderr, "-c %s: DoIO (SDCMD_SETPARAMS) returned %d\n",
               *argv, ser.IOSer.io_Error);
        goto c2n232fail;
      }
      /* send a NUL character and await a response */
      ser.IOSer.io_Command = CMD_WRITE;
      ser.IOSer.io_Length = 1;
      ser.IOSer.io_Data = buf;
      *buf = 0;
      SendIO ((struct IORequest*) &ser);
      if (flushout ()) {
        fprintf (stderr, "-c %s: WaitIO (CMD_WRITE) returned %d\n",
               *argv, ser.IOSer.io_Error);
        goto c2n232fail;
      }
      else {
        /* await a response */
        const long waitsigs = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
          1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit |
          1L << ser.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
        ser.IOSer.io_Command = CMD_READ;
        ser.IOSer.io_Length = sizeof buf;
        ser.IOSer.io_Data = buf;
        SendIO ((struct IORequest*) &ser);
        timer->tr_node.io_Command = TR_ADDREQUEST;
        timer->tr_time.tv_secs = 0;
        timer->tr_time.tv_micro = 500000L;
        SendIO ((struct IORequest*) timer);
        for (;;) {
          if (Wait (waitsigs) & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)) {
            AbortIO ((struct IORequest*) timer);
            WaitIO ((struct IORequest*) timer);
            AbortIO ((struct IORequest*) &ser);
            WaitIO ((struct IORequest*) &ser);
            raise (SIGINT);
          }
          if (CheckIO ((struct IORequest*) &ser)) {
            AbortIO ((struct IORequest*) timer);
            WaitIO ((struct IORequest*) timer);
            if (WaitIO ((struct IORequest*) &ser)) {
            fprintf (stderr, "-c %s: WaitIO (CMD_READ) returned %d\n",
                   *argv, ser.IOSer.io_Error);
            goto c2n232fail;
            }
            break;
          }
          else if (CheckIO ((struct IORequest*) timer)) {
            WaitIO ((struct IORequest*) timer);
            AbortIO ((struct IORequest*) &ser);
            WaitIO ((struct IORequest*) &ser);
            if (ser.IOSer.io_Actual > 0)
            break;
            fputs ("-c ", stderr);
            fputs (*argv, stderr);
            fputs (": no response from C2N232 device\n", stderr);
            goto c2n232fail;
          }
        }
        if (buf[ser.IOSer.io_Actual - 1] != '0') {
          fputs ("-c ", stderr);
          fputs (*argv, stderr);
          fputs (": unexpected response from C2N232 device\n", stderr);
          goto c2n232fail;
        }
      }
# else /* !WIN32 && !AMIGA */
      if ((serialfd = open (*++argv, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
        fputs ("-c ", stderr);
        fputs (*argv, stderr);
        perror (": open");
        return 2;
      }
      else {
        int flags = fcntl (serialfd, F_GETFL);
        if (flags < 0) {
          fputs ("-c ", stderr);
          fputs (*argv, stderr);
          perror (": fcntl(GETFL)");
          close (serialfd);
          serialfd = -1;
          return 2;
        }
        else if (flags & O_NONBLOCK &&
               fcntl (serialfd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
          fputs ("-c ", stderr);
          fputs (*argv, stderr);
          perror (": fcntl(SETFL)");
          close (serialfd);
          serialfd = -1;
          return 2;
        }
      }

      if (tcgetattr (serialfd, &tio)) {
        fputs ("-c ", stderr);
        fputs (*argv, stderr);
        perror (": tcgetattr");
        close (serialfd);
        serialfd = -1;
        return 2;
      }
      else {
        memcpy (&newtio, &tio, sizeof newtio);
        newtio.c_iflag = IGNBRK | IGNPAR | IXON;
        newtio.c_oflag = 0;
        newtio.c_cflag = CS8 | CLOCAL | CREAD;
        newtio.c_lflag = 0;
        memset (newtio.c_cc, 0, sizeof newtio.c_cc);
        newtio.c_cc[VSTART] = 021; /* DC1, ctrl-q */
        newtio.c_cc[VSTOP] = 023; /* DC3, ctrl-s */
        newtio.c_cc[VMIN] = 0; /* non-blocking read */
        newtio.c_cc[VTIME] = 1; /* 1/10th second timeout */
        cfsetispeed (&newtio, B38400), cfsetospeed (&newtio, B38400);
        tcflush (serialfd, TCIOFLUSH);
        tcsendbreak (serialfd, 0);
        if (tcsetattr (serialfd, TCSANOW, &newtio)) {
          fputs ("-c ", stderr);
          fputs (*argv, stderr);
          perror (": tcsetattr");
        c2n232fail:
          close (serialfd);
          serialfd = -1;
          return 2;
        }
        else {
          char last = 0;
          /* send a NUL character and await a response */
          write (serialfd, "", 1);
          for (;;) {
            int len = read (serialfd, buf, sizeof buf);
            if (!len) {
            if (last == '0')
              break;
            fputs ("-c ", stderr);
            fputs (*argv, stderr);
            if (last)
              fputs (": unexpected response from C2N232 device\n", stderr);
            else if (errno)
              perror (": read");
            else
              fputs (": no response from C2N232 device\n", stderr);
            goto c2n232fail;
            }
            last = buf[len - 1];
          }

          newtio.c_cc[VMIN] = 0; /* read at least 0 characters */
          newtio.c_cc[VTIME] = 20; /* wait 2 seconds for end of stream */
          tcsetattr (serialfd, TCSANOW, &newtio);
        }
      }
# endif /* !WIN32 && !AMIGA */
      argc--;
      break;
#endif /* HAS_SERIAL */
      case 'd':
      mode = Decode;
      break;
#ifdef USE_TAP
      case 'D':
      mode = DecodeTAP;
      break;
#endif /* USE_TAP */
      case 'e':
      mode = Encode;
      break;
#ifdef USE_PCM
      case 'E':
      if (argc <= 2)
        goto Usage;
      else {
        char* r;
        int rate = strtol (*++argv, &r, 0);
        samplebits = rate > 0 ? eight : eight_;
        if (pcm_file)
          fclose (pcm_file);
        pcm_file = 0;
        argc--;
        if (!rate) {
        EUsage:
          fprintf (stderr, "-E: [+-]rate[blsu]*[,file] expected, got %s\n",
                 *argv);
          goto Usage;
        }
      Enext:
        switch (*r++) {
        case 0:
          break;
        case ',':
          if (!*r)
            goto EUsage;
          if (!(pcm_file = fopen (r, "wb"))) {
            fputs ("-E ...,", stderr);
            fputs (r, stderr);
            perror (": fopen");
            return 2;
          }
          break;
        case 'b':
          samplebits = rate > 0 ? msb16 : msb16_;
          goto Enext;
        case 'l':
          samplebits = rate > 0 ? lsb16 : lsb16_;
          goto Enext;
        case 's':
          channels = 2;
          goto Enext;
        case 'u':
          sign = 0;
          goto Enext;
        default:
          goto EUsage;
        }

        samplerate = rate < 0 ? -rate : rate;
        mode = Encode;
      }
      break;
#endif /* USE_PCM */
      case 'h':
      goto Usage;
      case '1':
      format = ORIC1;
      break;
      case '2':
      format = PLUS4;
      break;
      case 'p':
      if (argc <= 2)
        goto Usage;
      else {
        char* r;
        char* s = *++argv;
        argc--;
        pulse_short = strtoul (s, &r, 0);
        if (*r != ',') {
        pUsage:
          fprintf (stderr, "-p: short,medium,long expected, got %s\n",
                 *argv);
          goto Usage;
        }
        s = r + 1;
        pulse_medium = strtoul (s, &r, 0);
        if (*r != ',')
          goto pUsage;
        s = r + 1;
        pulse_long = strtoul (s, &r, 0);
        if (*r)
          goto Usage;
      }
      break;
      case 'r':
      raw = 1;
      break;
      case 's':
      if (argc <= 2)
        goto Usage;
      else {
        char* r;
        char* s = *++argv;
        argc--;
        sync_begin = strtoul (s, &r, 0);
        if (*r != ',') {
        sUsage:
          fprintf (stderr, "-s: begin,intra expected, got %s\n",
                 *argv);
          goto Usage;
        }
        s = r + 1;
        sync_intra = strtoul (s, &r, 0);
        if (*r)
          goto sUsage;
      }
      break;
      case 't':
      if (argc <= 2)
        goto Usage;
      else {
        char* s;
        error_thresold = strtoul (*++argv, &s, 0);
        argc--;
        if (*s) {
          fprintf (stderr, "-t: non-numeric argument %s\n", *argv);
          goto Usage;
        }
      }
      break;
      case 'v':
      verbose++;
      break;

      default:
      goto Usage;
      }
  }

  if (argc < 2) {
  Usage:
    fprintf (stderr,
           "C2N " VERSION
           " - Commodore C2N tape format encoder and decoder\n"
           "Usage: %s [options] file(s)\n", prog);
    fputs ("Options: -1: use Oric-1 pulse widths and format\n"
         "         -2: use Commodore 264 series pulse widths and format\n"
#ifdef HAS_SERIAL
         "         -c device: specify the name of the C2N232 device\n"
#endif /* HAS_SERIAL */
         "         -d: decode pulses to C2N binary stream\n"
#ifdef USE_TAP
         "         -D: decode tape image to C2N binary stream\n"
#endif /* USE_TAP */
         "         -e: encode C2N binary stream to pulses (default)\n"
#ifdef USE_PCM
         "         -E [+-]rate[blsu]*[,file]: encode C2N binary stream to PCM\n"
#endif /* USE_PCM */
         "         -h: request this help text\n"
         "         -p short,medium,long: specify pulse lengths for C2N232\n"
         "         -r: enable raw operation (input and output pulses)\n"
         "         -s begin,intra: specify lengths of sync pulse streams\n"
         "         -t number: specify maximum number of errors\n"
         "         -v: enable verbose operation\n"
         "         --: Stop processing any further options.\n",
         stderr);
    return 1;
  }

#ifdef USE_PCM
  /* Compute the sample sequences for the pulses */
  if (samplerate) {
    switch (format) {
    case CBM:
      pcm_compute (&pcm_short, pulse_short ? pulse_short : 45, 0);
      pcm_compute (&pcm_medium, pulse_medium ? pulse_medium : 63, 0);
      pcm_compute (&pcm_long, pulse_long ? pulse_long : 83, 0);
      break;
    case PLUS4:
      pcm_compute (&pcm_short, pulse_short ? pulse_short : 60, 0);
      pcm_compute (&pcm_medium, pulse_medium ? pulse_medium : 120, 0);
      pcm_compute (&pcm_long, pulse_long ? pulse_long : 240, 0);
    case ORIC1:
      pcm_compute (&pcm_short, pulse_short ? pulse_short : 52, 0);
      pcm_compute (&pcm_medium,
               pulse_short ? pulse_short : 52,
               pulse_medium ? pulse_medium : 104);
    }
  }
#endif /* USE_PCM */

#ifdef HAS_SERIAL
  /* Set the pulse widths */
  if (serialfd) {
    unsigned char cmd[5], *op = cmd;
    switch (mode) {
    case Encode:
      *op++ = 3;
      *op++ = 255; /* pause duration */
      switch (format) {
      case CBM:
      *op++ = pulse_short ? pulse_short >> 1 : 22;
      *op++ = pulse_medium ? pulse_medium >> 1 : 31;
      *op++ = pulse_long ? pulse_long >> 1 : 41;
      break;
      case PLUS4:
      *op++ = pulse_short ? pulse_short >> 1 : 30;
      *op++ = pulse_medium ? pulse_medium >> 1 : 60;
      *op++ = pulse_long ? pulse_long >> 1 : 120;
      break;
      case ORIC1:
      *op++ = pulse_short ? pulse_short >> 1 : 26;
      *op++ = pulse_medium ? pulse_medium >> 1 : 39;
      *op++ = 255; /* no long pulses */
      break;
      }
      break;
# ifdef USE_TAP
    case DecodeTAP:
      fputs ("interpreting -D as -d in the presence of -c\n", stderr);
      mode = Decode;
# endif /* USE_TAP */
    case Decode:
      *op++ = 2;
      switch (format) {
      case CBM:
      *op++ = pulse_short ? pulse_short : 54;
      *op++ = pulse_medium ? pulse_medium : 74;
      *op++ = pulse_long ? pulse_long : 94;
      break;
      case PLUS4:
      *op++ = pulse_short ? pulse_short : 100;
      *op++ = pulse_medium ? pulse_medium : 180;
      *op++ = pulse_long ? pulse_long : 250;
      break;
      case ORIC1:
      *op++ = pulse_short ? pulse_short >> 1 : 54;
      *op++ = pulse_medium ? pulse_medium >> 1 : 80;
      *op++ = 255; /* no long pulses */
      break;
      }
      break;
    }

# ifdef WIN32
    {
      unsigned long len;
      if (!WriteFile (serialfd, cmd, op - cmd, &len, 0)) {
      report_error ("WriteFile");
      return 2;
      }
      if (!ReadFile (serialfd, cmd + 1, 1, &len, 0)) {
      report_error ("ReadFile");
      return 2;
      }
    }
# elif defined __AMIGA__
    ser.IOSer.io_Command = CMD_WRITE;
    ser.IOSer.io_Length = op - cmd;
    ser.IOSer.io_Data = cmd;
    if (DoIO ((struct IORequest*) &ser)) {
      fprintf (stderr, "-c %s: DoIO (CMD_WRITE) returned %d\n",
             *argv, ser.IOSer.io_Error);
      return 2;
    }
    ser.IOSer.io_Command = CMD_READ;
    ser.IOSer.io_Length = 1;
    ser.IOSer.io_Data = cmd + 1;
    if (DoIO ((struct IORequest*) &ser)) {
      fprintf (stderr, "-c %s: DoIO (CMD_READ) returned %d\n",
             *argv, ser.IOSer.io_Error);
      return 2;
    }
# else /* !WIN32 && !AMIGA */
    if (op - cmd != write (serialfd, cmd, op - cmd)) {
      perror ("write");
      return 2;
    }

    if (!read (serialfd, cmd + 1, 1)) {
      if (errno)
      perror ("read");
      else
      fputs ("no response from C2N232 device\n", stderr);
      return 2;
    }
# endif /* !WIN32 && !AMIGA */

    if ((cmd[1] & ~0x30) != cmd[0]) {
      fprintf (stderr, "C2N232 responded 0x%02x; expected 0x%02x\n",
             cmd[1], cmd[0] | 0x30);
      return 2;
    }

    if (mode == Encode) {
# ifdef WIN32
      newtio2.ReadIntervalTimeout = 0; /* no waiting at reading */
      if (!SetCommTimeouts (serialfd, &newtio2)) {
      report_error ("SetCommTimeouts");
      return 2;
      }
# elif defined __AMIGA__
      ser.IOSer.io_Command = SDCMD_SETPARAMS;
      ser.io_SerFlags = 0;
      if (DoIO ((struct IORequest*) &ser)) {
      fprintf (stderr, "-c %s: DoIO (SDCMD_SETPARAMS) returned %d\n",
             *argv, ser.IOSer.io_Error);
      goto c2n232fail;
      }
# else /* !WIN32 && !AMIGA */
      newtio.c_cc[VTIME] = 0; /* no waiting at reading */
      if (tcsetattr (serialfd, TCSANOW, &newtio)) {
      perror ("tcsetattr");
      return 2;
      }
# endif /* !WIN32 && !AMIGA */
    }
  }
#endif /* HAS_SERIAL */

  if (format == ORIC1)
    pulses[Medium] = 'J';

  /* Process the files. */

  for (; --argc; argv++) {
    FILE* df;
    switch (mode) {
    case Encode:
      if (!(df = fopen (currentFile = *argv, "rb"))) {
      fputs (currentFile, stderr);
      perror (": fopen (read)");
      continue;
      }

#ifdef HAS_SERIAL
      datain = 0;
      datalen = 0;
#endif /* HAS_SERIAL */

      if (raw) {
      int c;
      while ((c = fgetc (df)) != EOF) {
        enum pulse p = Pause;
        switch (c) {
        case 'A': break;
        case 'B': p = Short; break;
        case 'C': p = Medium; break;
        case 'D': if (format == CBM || format == PLUS4) { p = Long; break; }
        default:
          fprintf (stderr, "%s: unrecognized pulse character 0x%02x\n",
                 currentFile, c);
          continue;
        }
#ifdef USE_PCM
        if (samplerate)
          p_write_pcm (p);
        else
#endif /* USE_PCM */
          p_write (p);
      }
      }
      else if (format == ORIC1)
      encode_oric (df, error_report,
#ifdef USE_PCM
                 samplerate ? p_write_pcm :
#endif /* USE_PCM */
                 p_write,
                 sync_begin ? sync_begin : 50,
                 sync_intra ? sync_intra : 100);
      else if (format == PLUS4)
      encode264 (df, error_report,
#ifdef USE_PCM
               samplerate ? p_write_pcm :
#endif /* USE_PCM */
               p_write,
               sync_begin ? sync_begin : 1500,
               sync_intra ? sync_intra : 16384);
      else
      encode (df, error_report,
#ifdef USE_PCM
            samplerate ? p_write_pcm :
#endif /* USE_PCM */
            p_write,
            sync_begin ? sync_begin : 1500,
            sync_intra ? sync_intra : 5376);
      fclose (df);
#ifdef HAS_SERIAL
# if defined __AMIGA__
      if (serialfd && pending) {
      flushout ();
      pending = 0;
      }
# endif /* AMIGA */
#endif /* HAS_SERIAL */
      break;
#ifdef USE_TAP
    case DecodeTAP:
      switch (format) {
      case ORIC1:
      fputs ("Ignoring option -1 in mode -D\n", stderr);
      format = CBM;
      pulses[Medium] = 'C';
      /* fall through */
      case CBM:
      tap_short = pulse_short ? pulse_short : 54;
      tap_medium = pulse_medium ? pulse_medium : 74;
      tap_long = pulse_long ? pulse_long : 94;
      break;
      case PLUS4:
      tap_short = pulse_short ? pulse_short : 100;
      tap_medium = pulse_medium ? pulse_medium : 180;
      tap_long = pulse_long ? pulse_long : 250;
      break;
      }
      /* fall through */
#endif /* USE_TAP */
    case Decode:
      if (!(df = fopen (currentFile = *argv, "wb"))) {
      fputs (currentFile, stderr);
      perror (": fopen (write)");
      continue;
      }

      error_count = 0;
#ifdef HAS_SERIAL
# if defined __AMIGA__
      if (!pending)
# endif /* AMIGA */
      datain = 0, datalen = 0;
#endif /* HAS_SERIAL */

      if (raw) {
      /* maximum number of successive pauses that will be tolerated */
      static const int maxpauses = 5;
      /* number of pauses that will be tolerated */
      int pauses = maxpauses;
      for (;;) {
        enum pulse p =
#ifdef HAS_SERIAL
          serialfd ? sp_read () :
#endif /* HAS_SERIAL */
          (
#ifdef USE_TAP
           mode == DecodeTAP ? tap_read () :
#endif /* USE_TAP */
           p_read ());
        if (p == Pause) {
          if (pauses--)
            continue;
          break;
        }
        else if (p < sizeof pulses) {
          if (pauses != maxpauses)
            fputc (pulses[Pause], df);
          pauses = maxpauses;
          fputc (pulses[p], df);
        }
      }
      }
      else if (format == ORIC1)
      fprintf (stderr, "%s: decoded %u bytes (ORIC-1)\n", currentFile,
             decode_oric (
#ifdef HAS_SERIAL
                        serialfd ? sp_read :
#endif /* HAS_SERIAL */
                        p_read, error_report, df));
      else if (format == PLUS4)
      fprintf (stderr, "%s: decoded %u bytes (plus/4)\n", currentFile,
             decode264 (
#ifdef HAS_SERIAL
                      serialfd ? sp_read :
#endif /* HAS_SERIAL */
                      (
#ifdef USE_TAP
                       mode == DecodeTAP ? tap_read :
#endif /* USE_TAP */
                       p_read),
                      error_report, df));
      else
      fprintf (stderr, "%s: decoded %u bytes\n", currentFile,
             decode (
#ifdef HAS_SERIAL
                   serialfd ? sp_read :
#endif /* HAS_SERIAL */
                   (
#ifdef USE_TAP
                    mode == DecodeTAP ? tap_read :
#endif /* USE_TAP */
                    p_read),
                   error_report, df));
      fclose (df);
      break;
    }
  }

#ifdef HAS_SERIAL
  if (serialfd) {
# ifdef WIN32
    unsigned long len;
    if (!FlushFileBuffers (serialfd))
      report_error ("FlushFileBuffers");
    WriteFile (serialfd, "", 1, &len, 0);
# elif defined __AMIGA__
    if (pending)
      flushout ();
    ser.IOSer.io_Command = CMD_WRITE;
    ser.IOSer.io_Length = 1;
    ser.IOSer.io_Data = buf;
    *buf = 0;
    DoIO ((struct IORequest*) &ser);
# else /* !WIN32 && !AMIGA */
    if (tcdrain (serialfd))
      perror ("tcdrain");
    write (serialfd, "", 1);
# endif /* !WIN32 && !AMIGA */
  }
#endif /* HAS_SERIAL */

  return 0;
}

Generated by  Doxygen 1.6.0   Back to index