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

emul1541.c

Go to the documentation of this file.
/**
 * @file emul1541.c
 * Transfer routines for the Commodore serial bus to Amiga cable
 * designed for the Emul_1541 package
 * @author Marko Mäkelä (msmakela@nic.funet.fi)
 * @author Olaf Seibert (rhialto@mbfys.kun.nl)
 */

/*
 * Copyright © 1994-1997 Marko Mäkelä and Olaf Seibert
 * Copyright © 2001 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.
 */

#ifdef COMM_AMIGA

# include "info.h"
# include "emul1541.h"
# include "amigapar.h"

/** D3: ATN input line in the Emul_1541 cable (unused) */
# define ATNI     0x08
/** D4: CLK output line in the Emul_1541 cable, CLK in Olaf's cable */
# define CLKO     0x10
/** D5: DATA output line in the Emul_1541 cable, DATA in Olaf's cable */
# define DATAO    0x20
/** D6: CLK input line in the Emul_1541 cable, unused in Olaf's cable */
# define CLKI     0x40
/** D7: DATA input line in the Emul_1541 cable, unused in Olaf's cable */
# define DATAI    0x80

/*
 * The IEC cable for Emul_1541 has inverters installed on output lines
 * and ATN in.  The cable Olaf Seibert (which we call em1541) had around
 * does not.  These macros make swiching easier.
 */

/** @name Macros for accessing the CLK and DATA lines */
# ifdef NO_INVERTERS
#  define DROP_CLK_AND_DATA   (DATA_DDR = CLKO | DATAO)
#  define RAISE_CLK_AND_DATA  (DATA_DDR = 0)
#  define DROP_CLK_DATA_1     (DATA_DDR = CLKO)
#  define RAISE_CLK_DATA_1    RAISE_CLK_AND_DATA
#  define DROP_DATA_CLK_1     (DATA_DDR = DATAO)
#  define RAISE_DATA_CLK_1    RAISE_CLK_AND_DATA
# else /* NO_INVERTERS */
#  define INVERTERO           (DATAO | CLKO)
#  define COMBINE(c,d)        (((c) | (d)) ^ INVERTERO)
#  define DROP_CLK_AND_DATA   (DATA = COMBINE(0, 0))
#  define RAISE_CLK_AND_DATA  (DATA = COMBINE(CLKO, DATAO))
#  define DROP_CLK_DATA_1     (DATA = COMBINE(0, DATAO))
#  define RAISE_CLK_DATA_1    RAISE_CLK_AND_DATA
#  define DROP_DATA_CLK_1     (DATA = COMBINE(CLKO, 0))
#  define RAISE_DATA_CLK_1    RAISE_CLK_AND_DATA
# endif /* NO_INVERTERS */

/** Convert input bits for CLK and DATA to corresponding output bits
 * @param d the input bits (CLKI and DATAI)
 * @return  a corresponding bitmask of CLKO and DATAO
 */
# define I2O(d)               (((d) >> 2) & (CLKO | DATAO))

/** Open the communication channel
 * @param dev           name of the communication device
 * @param hostinfo      (output) the computer model information
 * @return        zero on success, nonzero on failure
 */
int
emul1541_init (const char* dev, struct hostinfo* hostinfo)
{
  unsigned char detectbuf[5];
  if (alloc_port (0))
    return 1;
# ifdef NO_INVERTERS
  /*
   * If we don't have inverters, then we output a 0 by really
   * outputting a 0, but we output a 1 by turning the line to
   * input. This allows the other side to pull the line down
   * itself. Remember, in the DDR 0 = input, 1 = output.
   * This in effect looks a bit like we have inverters again.. ;-)
   */
  DATA = 0;       /* remains 0 all the time */
  DATA_DDR = 0;         /* output neutral highs */
# else /* NO_INVERTERS */
  /* set CLK and DATA to output high */
  DATA = 0xFF;
  DATA_DDR = CLKO | DATAO;
# endif /* NO_INVERTERS */
  if (emul1541_write ("", 1))
    return 1;
  emul1541_sr ();
  if (emul1541_read (detectbuf, sizeof detectbuf))
    return 2;
  emul1541_rs ();
  hostinfo->host = detectbuf[0];
  hostinfo->driver = (unsigned) detectbuf[1] | (unsigned) detectbuf[2] << 8;
  hostinfo->basic = (unsigned) detectbuf[3] | (unsigned) detectbuf[4] << 8;
  return 0;
}

/** Close the communication channel */
void
emul1541_close (void)
{
  free_port ();
}

/** Switch the data direction from receive to send */
void
emul1541_rs (void)
{
  do {
    /* drop CLK and DATA */
    DROP_CLK_AND_DATA;
    /* raise CLK and DATA */
    RAISE_CLK_AND_DATA;
    check_break ();
  } while ((DATA & CLKI) != 0); /* repeat until CLK==low */

  /* drop CLK (DATA remains high) */
  DROP_CLK_DATA_1;

  /* wait for DATA==low */
  while ((DATA & DATAI) != 0)
    check_break ();

  /* raise CLK (DATA remains high) */
  RAISE_CLK_DATA_1;

  /* wait for DATA==high */
  while ((DATA & DATAI) == 0)
    check_break ();
}

/** Switch the data direction from send to receive */
void
emul1541_sr (void)
{
  /* wait for CLK==DATA==low */
  while ((DATA & (CLKI | DATAI)) != 0)
    check_break ();

  do {
    /* drop CLK (DATA remains high) */
    DROP_CLK_DATA_1;
    /* raise CLK */
    RAISE_CLK_DATA_1;
    check_break ();
  } while ((DATA & (CLKI | DATAI)) != DATAI);
  /* repeat until CLK==low && DATA==high */

  /* drop DATA (CLK remains high) */
  DROP_DATA_CLK_1;

  /* wait for CLK==high */
  while ((DATA & CLKI) == 0)
    check_break ();

  /* raise DATA (CLK remains high) */
  RAISE_DATA_CLK_1;
}

/** Send data
 * @param buf           the data to be sent
 * @param len           length of the data in bytes
 * @return        zero on success, nonzero on failure
 */
int
emul1541_write (const void* buf, unsigned len)
{
  register const unsigned char* buffer = buf;

  while (len--) {
    register unsigned char byte = *buffer++;
    int i;

    for (i = 8; i--; byte >>= 1) {
      /* drop CLK or DATA, depending on the bit value */
      (byte & 1) ? DROP_DATA_CLK_1 : DROP_CLK_DATA_1;

      /* wait for CLK==DATA==low */
      while ((DATA & (CLKI | DATAI)) != 0)
      check_break ();

      /* raise CLK and DATA */
      RAISE_CLK_AND_DATA;

      /* wait for CLK==DATA==high */
      while ((DATA & (CLKI | DATAI)) != (CLKI | DATAI))
      check_break ();
    }
  }

  return 0;
}

/** Receive data
 * @param buf           the data to be received
 * @param len           length of the data in bytes
 * @return        zero on success, nonzero on failure
 */
int
emul1541_read (void* buf, unsigned len)
{
  register unsigned char* buffer = buf;

  while (len--) {
    int i;
    unsigned char data = 0;

    for (i = 8; i--; ) {
      register unsigned char regval;

      /* wait for CLK==low or DATA==low */
      do {
      regval = DATA;
      check_break ();
      } while ((regval & (CLKI | DATAI)) == (CLKI | DATAI));

      /* send an acknowledgement: drop the other line */
# ifdef NO_INVERTERS
      DATA_DDR = I2O (regval);
# else /* NO_INVERTERS */
      DATA = I2O (regval);
# endif /* NO_INVERTERS */

      /* read the data */
      data >>= 1;
      if (regval & CLKI) data |= 128;

      /* wait for acknowledgement (CLK==high or DATA==high) */
      while ((DATA & (CLKI | DATAI)) == 0)
      check_break ();

      /* raise CLK and DATA */
      RAISE_CLK_AND_DATA;
    }

    *buffer++ = data;
  }

  return 0;
}

#endif /* COMM_AMIGA */

Generated by  Doxygen 1.6.0   Back to index