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

oric_d.c

Go to the documentation of this file.
/**
 * @file oric_d.c
 * Oric tape decoding functions
 * @author Marko Mäkelä (msmakela@nic.funet.fi)
 */

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

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

#include "c2n.h"
#include "oric_d.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)
 * @return  the following pulse (normally Medium)
 */
static enum pulse
00042 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;
    }
  }
}

/** Decode a block of data
 * @param block         number of the block being decoded
 * @param header  header byte to wait for (0x16, 0x24 or 0 for none)
 * @param length  expected length of the block
 * @param buf           output buffer for the decoded bytes
 * @return        number of bytes decoded
 */
static unsigned
00078 decodeBlock (unsigned block, unsigned header, unsigned length, char* buf)
{
  /** header sync byte count (for diagnostics) */
  unsigned synccnt = 0;
  /** byte count */
  unsigned cnt = 0;
  /** number of subsequent pauses */
  unsigned pauses = 0;
  /** flag: has a sync pulse been seen? */
  unsigned sync = !header;
  if (sync)
    goto decode;
  for (;;) {
    /** bit count */
    unsigned bitcnt;
    /** parity bit */
    unsigned parity;
    /** a decoded character */
    unsigned c;
    /** a pulse read from the tape */
    enum pulse p = (*dec_rd) ();

  resync:
    /* read the byte synchronization mark first */
    if (p != Pause) pauses = 0;
    switch (p) {
    case Pause:
      if (++pauses > 5)
      return cnt;
      if ((cnt || !header) &&
        (!dec_err || (*dec_err) (p, block, cnt)))
      return cnt;
      sync = 0;
      continue;
    case Short:
      sync = 1;
      continue;
    case Medium:
      if (sync)
      break;
    case Long:
      if (!dec_err || (*dec_err) (p, block, cnt))
      return cnt;
      sync = 0;
      continue;
    }

  decode:
    /* decode a byte */
    for (c = parity = 0, bitcnt = 9; bitcnt--; ) {
      p = (*dec_rd) ();
      if (p != Short && p != Medium) {
      if (!dec_err || (*dec_err) (p, block, cnt))
        return cnt;
      goto resync;
      }
      c >>= 1;
      if (p == Short) {
      c |= 0x100;
      parity = !parity;
      }
    }

    if (!parity) {
      if (header && !cnt); /* do not complain about parity error at start */
      else if (!dec_err || (*dec_err) (Parity, block, cnt))
      return cnt;
    }

    c &= 0xff;

    switch (header) {
    case 0: /* this is not a header */
    case 1: /* this is a header block; read until NUL byte in file name */
      buf[cnt++] = c;
      if (cnt == length ||
        (cnt > 9 && !c && header == 1))
      return cnt;
      break;
    case 0x16:
      if (c == 0x16)
      header = 0x24;
      synccnt++;
      break;
    case 0x24:
      synccnt++;
      if (c == 0x24) {
      header = 1;
      if (verbose)
        fprintf (stderr, "skipped %u header sync bytes\n", synccnt);
      }
      else if (c != 0x16) {
      if (!dec_err || (*dec_err) (Unexpected, block, c))
        return cnt;
      }
    }
  }
}

/** Oric 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
00184 decode_oric (pulse_r_t rd, pulse_error_t err, FILE* out)
{
  /** number of output bytes */
  unsigned osize;
  /** block being read */
  unsigned block;

  dec_rd = rd;
  dec_err = err;

  for (block = osize = 0;; ) {
    /** header block */
    static char header[9 + 16];
    /** header block length */
    unsigned headerlength = decodeBlock (block, 0x16, sizeof header, header);
    if (!headerlength)
      break;
    else if (headerlength < 10) {
      if (dec_err)
      (*dec_err) (ShortBlock, block, 10 - headerlength);
      break;
    }
    else if (header[headerlength - 1]) {
      if (dec_err)
      (*dec_err) (LongBlock, block, 1);
      break;
    }
    else {
      /** program data buffer */
      char* buf;
      /** program length in bytes */
      unsigned length;
      /** read program length */
      unsigned readlength;
      block++;

      length = ((unsigned) (unsigned char) header[5]) |
      ((unsigned) (unsigned char) header[4]) << 8;
      length -= ((unsigned) (unsigned char) header[7]) |
      ((unsigned) (unsigned char) header[6]) << 8;
      length &= 0xffff;
      length++;

      if (!(buf = malloc (length))) {
      fputs ("out of memory\n", stderr);
      break;
      }
      else {
      enum pulse p = skip_sync ();
      if (p != Medium) {
        if (!dec_err || (*dec_err) (p, block, 0)) {
          free (buf);
          break;
        }
      }
      }

      readlength = decodeBlock (block++, 0, length, buf);
      if (readlength == length) {
      fwrite ("\26\26\26\44", 1, 4, out);
      fwrite (header, 1, headerlength, out);
      fwrite (buf, 1, length, out);
      osize += sizeof header + 4 + length;
      free (buf);
      }
      else {
      free (buf);
      break;
      }
    }
  }

  return osize;
}

Generated by  Doxygen 1.6.0   Back to index