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

decode.c

Go to the documentation of this file.
/**
 * @file decode.c
 * C2N pulse stream decoder
 * @author Marko Mäkelä (msmakela@nic.funet.fi)
 */

/* Copyright © 2001 Marko Mäkelä.

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

   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. */

#include <stdio.h>
#include <stdlib.h>

#include "c2n.h"
#include "decode.h"

/** the pulse stream reader */
00034 static pulse_r_t dec_rd;
/** the decoding error reporter */
00036 static pulse_error_t dec_err;

/** Skip a sync mark (sequence of short pulses and pauses) */
static enum pulse
00040 skip_sync (void)
{
  enum pulse p;
  unsigned count;

  if (verbose)
    fputs ("skipping a sync mark", stderr), fflush (stderr);

  for (count = 0;; count++) {
    p = (*dec_rd) ();
  eval:
    switch (p) {
    case Pause:
      p = (*dec_rd) ();
      if (p == Pause)
      goto done;
      goto eval;
    case Short:
      continue;
    default:
    done:
      if (verbose)
      fprintf (stderr, " (%u pulses)\n", count);
      return p;
    }
  }
}

/** number of the block being decoded */
00069 static unsigned block = 0;
/** length of the decoding buffer */
00071 static unsigned declen = 0;
/** decoding buffer */
00073 static char* decbuf = 0;
/** flag: checking a second copy? */
00075 static unsigned chk = 0;

/** Output a decoded byte
 * @param c the decoded byte
 */
static void
00081 output (char c)
{
  if (!(declen & (declen + 1)))
    decbuf = realloc (decbuf, (declen + 1) << 1);
  decbuf[declen++] = c;
}

/** Decode a block in Commodore PET/VIC-20/C64/C128 format
 * @return  the countdown character (0x81 for 1st copy, 1 for 2nd copy)
 */
static unsigned
00092 decBlock (void)
{
  /** byte count */
  unsigned cnt = 0;
  /** data character being read */
  unsigned c = 0;
  /** last countdown character read */
  unsigned countdown = 0;
  /** data block checksum */
  char chk = 0;
  /** number of subsequent pauses */
  unsigned pauses = 0;

  for (;;) {
    /** a pulse read from the tape */
    enum pulse p = (*dec_rd) ();
  resync:
    if (p != Pause) pauses = 0;
    switch (p) {
    case Pause:
      pauses++;
      p = (*dec_rd) ();
      if (p != Pause)
      goto resync;
      if (!cnt && ++pauses > 5)
      return countdown;
      /* fall through */
    case Short:
      if (chk && dec_err)
      (*dec_err) (Checksum, block, cnt);
      if (cnt)
      return countdown;
      else if ((p = skip_sync ()) != Long)
      goto resync;
      break;
    case Medium:
      if (!dec_err || (*dec_err) (p, block, cnt))
      return countdown;
      continue;
    case Long:
      break;
    }

    p = (*dec_rd) ();
    if (p == Short)
      goto resync;
    else if (p == Medium) {
      /** parity bit */
      register unsigned parity;
      /** bit count */
      register unsigned bitcnt;

      if (cnt)
      output (c);

      for (c = parity = 0, bitcnt = 9; bitcnt--; ) {
      enum pulse p1 = (*dec_rd) ();
      if (p1 != Short && p1 != Medium) {
        if (!dec_err || (*dec_err) (p1, block, cnt))
          return countdown;
        p = p1;
        goto resync;
      }
      resync2:
      p = (*dec_rd) ();
      if ((p != Short && p != Medium) || p == p1) {
        if (!dec_err || (*dec_err) (p, block, cnt))
          return countdown;
        if (p == p1) goto resync2;
        goto resync;
      }
      c >>= 1;
      if (p == Short) {
        c |= 0x100;
        parity = !parity;
      }
      }

      if (!parity) {
      if (!dec_err || (*dec_err) (Parity, block, cnt))
        return countdown;
      }

      c &= 0xff;

      if (!countdown) {
      countdown = c;
      if ((c & 0x7f) != 9)
        if (!dec_err || (*dec_err) (Countdown, block, c))
          return countdown;
      }
      else if (countdown != 1 && countdown != 0x81) {
      if (c != countdown - 1)
        if (!dec_err || (*dec_err) (Countdown, block, c))
          return countdown;
      countdown = c;
      }
      else {
      cnt++;
      chk ^= c;
      }
    }
    else if (!dec_err || (*dec_err) (p, block, cnt))
      return countdown;
  }
}

/** Decode a block in Commodore 264 series format
 * @return  the countdown character (0x81 for 1st copy, 1 for 2nd copy)
 */
static unsigned
00203 dec264Block (void)
{
  /** byte count */
  unsigned cnt = 0;
  /** data character being read */
  unsigned c = 0;
  /** last countdown character read */
  unsigned countdown = 0;
  /** data block checksum */
  char chk = 0;
  /** number of subsequent pauses */
  unsigned pauses = 0;

  for (;;) {
    /** a pulse read from the tape */
    enum pulse p = (*dec_rd) ();
  resync:
    if (p != Pause) pauses = 0;
    switch (p) {
    case Pause:
      pauses++;
      p = (*dec_rd) ();
      if (p != Pause)
      goto resync;
      if (!cnt && ++pauses > 5)
      return countdown;
      /* fall through */
    case Short:
      if (chk && dec_err)
      (*dec_err) (Checksum, block, cnt);
      if (cnt)
      return countdown;
      else if ((p = skip_sync ()) != Long)
      goto resync;
      break;
    case Medium:
      p = (*dec_rd) ();
      if (p == Short)
      goto resync;
      if (!dec_err ||
        (*dec_err) (Medium, block, cnt) ||
        (*dec_err) (p, block, cnt))
      return countdown;
      continue;
    case Long:
      break;
    }

    p = (*dec_rd) ();
    if (p == Medium) {
      /** parity bit */
      register unsigned parity;
      /** bit count */
      register unsigned bitcnt;

      if (cnt)
      output (c);

      for (c = parity = 0, bitcnt = 9; bitcnt--; ) {
      enum pulse p1 = (*dec_rd) ();
      if (p1 != Short && p1 != Medium) {
        if (!dec_err || (*dec_err) (p1, block, cnt))
          return countdown;
        p = p1;
        goto resync;
      }
      resync2:
      p = (*dec_rd) ();
      if ((p != Short && p != Medium) || p == p1) {
        if (!dec_err || (*dec_err) (p, block, cnt))
          return countdown;
        if (p == p1) goto resync2;
        goto resync;
      }
      c >>= 1;
      if (p == Short) {
        c |= 0x100;
        parity = !parity;
      }
      }

      if (!parity) {
      if (!dec_err || (*dec_err) (Parity, block, cnt))
        return countdown;
      }

      c &= 0xff;

      if (!countdown) {
      countdown = c;
      if ((c & 0x7f) != 9)
        if (!dec_err || (*dec_err) (Countdown, block, c))
          return countdown;
      }
      else if (countdown != 1 && countdown != 0x81) {
      if (c != countdown - 1)
        if (!dec_err || (*dec_err) (Countdown, block, c))
          return countdown;
      countdown = c;
      }
      else {
      cnt++;
      chk ^= c;
      }
    }
    else if (!dec_err || (*dec_err) (p, block, cnt))
      return countdown;
  }
}

/** Flag: use Commodore 264 series format */
00314 static unsigned plus4;

/** Decode a block
 * @return  the countdown character (0x81 for 1st copy, 1 for 2nd copy)
 */
static unsigned
00320 decodeBlock (void)
{
  return plus4 ? dec264Block () : decBlock ();
}

/** Decode a block or compare the second copy to the first copy
 * @param len     expected length of the block
 * @return  the length of the decoded block
 */
static unsigned
00330 decodeBytes (unsigned len)
{
  /* first copy of the block */
  char* firstbuf;
  /* length of the first copy of the block */
  unsigned firstlen;

  if (chk) {
    firstbuf = decbuf;
    firstlen = declen;
  }
  else {
    firstbuf = 0;
    firstlen = 0;
    free (decbuf);
  }

  decbuf = 0; declen = 0;

  for (;; block++) {
    /* decode a header block */
    switch (decodeBlock ()) {
    default:
      continue;
    case 0:
      free (firstbuf);
      return 0;
    case 0x81: /* first copy */
      if (verbose)
      fprintf (stderr, "decoded %u bytes (1st copy)\n", declen);
      if (chk) {
      if (!dec_err || (*dec_err) (NoSecond, block, 0))
        return 0;
      }
      free (firstbuf); firstbuf = 0; firstlen = 0;
      chk = 1;
      break;
    case 1: /* second copy */
      if (verbose)
      fprintf (stderr, "decoded %u bytes (2nd copy)\n", declen);
      if (!chk) {
      if (!dec_err || (*dec_err) (NoFirst, block, 0))
        return 0;
      }
      else {
      chk = 0;
      if (declen > firstlen) {
        if (!dec_err || (*dec_err) (LongBlock, block, declen - firstlen)) {
          free (firstbuf);
          return 0;
        }
      }
      else if (declen < firstlen) {
        if (!dec_err || (*dec_err) (ShortBlock, block, firstlen - declen)) {
          free (firstbuf);
          return 0;
        }
      }
      else {
        for (declen = 0; declen < firstlen; declen++) {
          if (decbuf[declen] != firstbuf[declen]) {
            if (!dec_err || (*dec_err) (Mismatch, block, declen)) {
            free (firstbuf);
            return 0;
            }
          }
        }
      }

      free (firstbuf); firstbuf = 0; firstlen = 0;
      free (decbuf); decbuf = 0; declen = 0;
      continue;
      }
    }

    if (declen > len) {
      if (!dec_err || (*dec_err) (LongBlock, block, declen - len))
      return 0;
      continue;
    }
    else if (declen < len) {
      if (!dec_err || (*dec_err) (ShortBlock, block, len - declen))
      return 0;
      continue;
    }

    return declen;
  }
}

/** Pulse stream decoder
 * @param rd      the pulse stream reader
 * @param err     the error reporter
 * @param out     the data output stream
 * @return  number of bytes converted
 */
static unsigned
00427 dec (pulse_r_t rd, pulse_error_t err, FILE* out)
{
  /** number of output bytes */
  unsigned osize = 0;
  /** expected length of next block */
  unsigned length = 192;
  /** flag: expecting a program block? */
  unsigned prog = 0;

  block = 0;
  chk = 0;
  dec_rd = rd;
  dec_err = err;

  free (decbuf); decbuf = 0; declen = 0;

  for (;;) {
    /** size of the decoded block */
    unsigned bsize = decodeBytes (length);
    if (bsize != length)
      break;
    if (prog) {
      prog = 0;
      length = 192;
    }
    else {
      /* interpret the tape header */
      switch (*decbuf) {
      case tBasic:
      case tML:
      prog = 1;
      length = ((unsigned) (unsigned char) decbuf[3]) |
        ((unsigned) (unsigned char) decbuf[4]) << 8;
      length -= ((unsigned) (unsigned char) decbuf[1]) |
        ((unsigned) (unsigned char) decbuf[2]) << 8;
      length &= 0xffff;
      /* fall through */
      case tDataBlock:
      case tDataHeader:
      case tEnd:
      goto expected;
      }

      if (!dec_err || (*dec_err) (Unexpected, block,
                          (unsigned char) *decbuf))
      goto done;
    }

  expected:
    fwrite (decbuf, 1, declen, out);
    osize += declen;
  }

 done:
  free (decbuf); decbuf = 0; declen = 0;
  return osize;
}

/** C2N pulse stream decoder
 * @param rd      the pulse stream reader
 * @param err     the error reporter
 * @param out     the data output stream
 * @return  number of bytes converted
 */
unsigned
00492 decode (pulse_r_t rd, pulse_error_t err, FILE* out)
{
  plus4 = 0;
  return dec (rd, err, out);
}

/** Commodore 1531 (Commodore 264 series) pulse stream decoder
 * @param rd      the pulse stream reader
 * @param err     the error reporter
 * @param out     the data output stream
 * @return  number of bytes converted
 */
unsigned
00505 decode264 (pulse_r_t rd, pulse_error_t err, FILE* out)
{
  plus4 = 1;
  return dec (rd, err, out);
}

Generated by  Doxygen 1.6.0   Back to index