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

x1541.c

Go to the documentation of this file.
/**
 * @file x1541.c
 * Transfer routines for the Commodore serial bus to PC cable
 * designed by Leopoldo Ghielmetti
 * @author Marko Mäkelä (msmakela@nic.funet.fi)
 */

/*
 * Copyright © 1994-1996 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_PC
# include "info.h"
# include "x1541.h"
# include "pcpar.h"

/* X1541 cable constants */
/** the serial bus clock line */
#define CLK 2
/** the serial bus data line */
#define DATA 8

/** 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
x1541_init (const char* dev, struct hostinfo* hostinfo)
{
  unsigned char detectbuf[5];
  if (!open_port (dev))
    return 1;
  out_ctrl (0);
  if (x1541_write ("", 1))
    return 1;
  x1541_sr ();
  if (x1541_read (detectbuf, sizeof detectbuf))
    return 2;
  x1541_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
x1541_close (void)
{
  out_ctrl (0); /* release the serial lines */
  close_port ();
}

/** Switch the data direction from receive to send */
void
x1541_rs (void)
{
  do {
    /* drop CLK and DATA */
    out_ctrl (CLK | DATA);
    /* raise CLK and DATA */
    out_ctrl (0);
  }
  while (!(in_ctrl () & CLK)); /* repeat until CLK==low */

  /* drop CLK */
  out_ctrl (CLK);

  /* wait for DATA==low */
  while (!(in_ctrl () & DATA));

  /* raise CLK */
  out_ctrl (0);

  /* wait for DATA==high */
  while (in_ctrl () & DATA);
}

/** Switch the data direction from send to receive */
void
x1541_sr (void)
{
  /* wait for CLK==DATA==low */
  while ((~in_ctrl ()) & (CLK | DATA));

  do {
    /* drop CLK */
    out_ctrl (CLK);
    /* raise CLK */
    out_ctrl (0);
  } /* repeat until CLK==low && DATA==high */
  while ((in_ctrl () & (CLK | DATA)) != CLK);

  /* drop DATA */
  out_ctrl (DATA);

  /* wait for CLK==high */
  while (in_ctrl () & CLK);

  /* raise DATA */
  out_ctrl (0);
}

#if !defined __BCC__ && (!defined __GNUC__ || defined USE_PPDEV)
/** Send data (unoptimised)
 * @param buf           the data to be sent
 * @param len           length of the data in bytes
 */
static void
x1541_write_slow (const void* buf, unsigned len)
{
  register const unsigned char* buffer = buf;

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

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

      /* wait for CLK==DATA==low */
      while ((~in_ctrl ()) & (CLK | DATA));

      /* raise CLK and DATA */
      out_ctrl (0);

      /* wait for CLK==DATA==high */
      while (in_ctrl () & (CLK | DATA));
    }
  }
}

/** Receive data (unoptimised)
 * @param buf           the data to be received
 * @param len           length of the data in bytes
 */
static void
x1541_read_slow (void* buf, unsigned len)
{
  register unsigned char* buffer = buf;

  while (len--) {
    register int i;
    register unsigned char data = 0;
    for (i = 8; i--; ) {
      register unsigned char regval;
      /* wait for CLK==low or DATA==low */
      while (!((regval = in_ctrl ()) & (CLK | DATA)));

      /* send an acknowledgement */
      out_ctrl (regval ^ (CLK | DATA));

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

      /* wait for acknowledgement (CLK==high or DATA==high) */
      while (!((~in_ctrl ()) & (CLK | DATA)));

      /* raise CLK and DATA */
      out_ctrl (0);
    }

    *buffer++ = data;
  }
}
#endif /* !__BCC__ && (!__GNUC__ || USE_PPDEV) */

/** 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
x1541_write (const void* buf, unsigned len)
{
# ifdef __GNUC__
#  ifdef USE_PPDEV
  if (baseaddr == (unsigned) -1) {
    x1541_write_slow (buf, len);
    return 0;
  }
#  endif /* USE_PPDEV */
  __asm__ volatile ("xorb %%al,%%al\n"
                "0:\t"
                "movb (%0),%%ah\n\t"
                "push %%ecx\n\t"
                "movl $8,%%ecx\n"
                "1:\t"
                "shrb $1,%%ah\n\t"
                "adcb $7,%%al\n\t"
                "andb $10,%%al\n\t"
                "outb %%al,%%dx\n"
                "2:\t"
                "inb %%dx,%%al\n\t"
                "notb %%al\n\t"
                "andb $10,%%al\n\t"
                "jnz 2b\n\t"
                "outb %%al,%%dx\n"
                "3:\t"
                "inb %%dx,%%al\n\t"
                "andb $3,%%al\n\t"
                "andb $10,%%al\n\t"
                "jnz 3b\n\t"
                "loop 1b\n\t"
                "pop %%ecx\n\t"
                "inc %0\n\t"
                "loop 0b"
                : /* no outputs */
                : "b" (buf), "c" (len), "d" (ctrladdr)
                : "ax");
  return 0;
# else /* BCC does not recognize #elif */
# ifdef __BCC__
#  asm
    push bp
    mov bp,sp
    push es
    mov dx,_ctrladdr
    mov cx,len_
    mov bx,buf_
    mov ax,I 0x40
    mov es,ax
    xor al,al

.sloop:
    mov ah,[bx]
    push cx
    mov cx,I 8
.bloop:
    shr ah,I 1
    adc al,I 7    /* CLK==2, DATA==8 */
    and al,I 10
    out dx,al
.owait1:
    in al,dx
    not al
    and al,CLK | DATA
    jz .nowait1
    eseg
    mov al,byte [0x71]  ; is the keyboard interrupt flag set?
    test al,I 0x80
    jz .owait1
    jmp near _abreak
.nowait1:
    out dx,al
.owait2:
    in al,dx
    and al,CLK | DATA
    jz .nowait2
    eseg
    mov al,byte [0x71]  ; is the keyboard interrupt flag set?
    test al,I 0x80
    jz .owait2
    jmp near _abreak
.nowait2:
    loop .bloop
    pop cx

    inc bx
    loop .sloop
    pop es
    pop bp
    xor ax,ax
#  endasm
# else
  x1541_write_slow (buf, len);
  return 0;
# endif
# endif /* BCC does not recognize #elif */
}

/** 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
x1541_read (void* buf, unsigned len)
{
# ifdef __GNUC__
#  ifdef USE_PPDEV
  if (baseaddr == (unsigned) -1) {
    x1541_read_slow (buf, len);
    return 0;
  }
#  endif /* USE_PPDEV */
  __asm__ volatile ("0:\t"
                "push %%ecx\n\t"
                "movl $8,%%ecx\n"
                "1:\t"
                "inb %%dx,%%al\n\t"
                "testb $10,%%al\n\t"
                "jz 1b\n\t"
                "xorb $10,%%al\n\t"
                "outb %%al,%%dx\n\t"
                "rorb $1,%%al\n\t"
                "rorw $1,%%ax\n"
                "2:\t"
                "inb %%dx,%%al\n\t"
                "notb %%al\n\t"
                "andb $10,%%al\n\t"
                "jz 2b\n\t"
                "xorb %%al,%%al\n\t"
                "outb %%al,%%dx\n\t"
                "loop 1b\n\t"
                "pop %%ecx\n\t"
                "movb %%ah,(%0)\n\t"
                "inc %0\n\t"
                "loop 0b"
                : /* no outputs */
                : "b" (buf), "c" (len), "d" (ctrladdr)
                : "ax");
  return 0;
# else /* BCC does not recognize #elif */
# ifdef __BCC__
#  asm
    push bp
    mov bp,sp
    push es
    mov dx,_ctrladdr
    mov cx,len_
    mov bx,buf_
    mov ax,I 0x40
    mov es,ax

.rloop:
    push cx
    mov cx,I 8
.brloop:
    in al,dx
    test al,CLK | DATA
    jnz .niwait1
    eseg
    mov al,byte [0x71]  ; is the keyboard interrupt flag set?
    test al,I 0x80
    jz .brloop
    jmp near _abreak
.niwait1:
    xor al,CLK | DATA
    out dx,al
    ror al,1
    ror ax,1

.iwait2:
    in al,dx
    not al
    and al,CLK | DATA
    jnz .niwait2
    eseg
    mov al,byte [0x71]  ; is the keyboard interrupt flag set?
    test al,I 0x80
    jz .brloop
    jmp near _abreak
.niwait2:
    xor al,al
    out dx,al
    loop .brloop

    pop cx
    mov [bx],ah
    inc bx
    loop .rloop

    pop es
    pop bp
    xor ax,ax
#  endasm
# else
  x1541_read_slow (buf, len);
  return 0;
# endif
# endif /* BCC does not recognize #elif */
}
#endif /* COMM_PC */

Generated by  Doxygen 1.6.0   Back to index