Index: configure.ac
===================================================================
--- configure.ac	(revision 1043)
+++ configure.ac	(working copy)
@@ -115,7 +115,7 @@
             [Define if FTDI support is enabled via libftdi])
 AC_CHECK_LIB([ftdi], [ftdi_usb_get_strings], [have_libftdi=yes], [], [-lusb])
 if test x$have_libftdi = xyes; then
-   LIBFTDI="-lftdi -lusb"
+   LIBFTDI="-lftdi -lusb -lpthread"
    AC_DEFINE([HAVE_LIBFTDI])
 fi
 AC_SUBST(LIBFTDI, $LIBFTDI)
Index: ft245r.c
===================================================================
--- ft245r.c	(revision 0)
+++ ft245r.c	(revision 0)
@@ -0,0 +1,963 @@
+
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2003-2004  Theodore A. Roth  <troth@openavr.org>
+ * some code:
+ * Copyright (C) 2011-2012 Roger E. Wolff <R.E.Wolff@BitWizard.nl> 
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/* $Id$ */
+
+/* ft245r -- FT245R/FT232R Synchronous BitBangMode Programmer
+  default pin assign 
+               FT232R / FT245R
+  miso  = 1;  # RxD   / D1
+  sck   = 0;  # RTS   / D0
+  mosi  = 2;  # TxD   / D2
+  reset = 4;  # DTR   / D4
+*/
+
+/*
+  The ft232r is very similar, or even "identical" in the synchronous
+  bitbang mode that we use here.
+
+  This allows boards that have an ft232r for communication and an avr
+  as the processor to function as their own "ICSP". Boards that fit
+  this description include the Arduino Duemilanove, Arduino Diecimila,
+  Arduino NG (http://arduino.cc/it/main/boards) and the BitWizard
+  ftdi_atmega board (http://www.bitwizard.nl/wiki/index.php/FTDI_ATmega)
+
+  The Arduinos have to be patched to bring some of the control lines
+  to the ICSP header. The BitWizard board already has the neccessary
+  wiring on the PCB. 
+
+  How to add the wires to an arduino is documented here: 
+  http://www.geocities.jp/arduino_diecimila/bootloader/index_en.html
+*/
+
+
+#include "ac_cfg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "avr.h"
+#include "pindefs.h"
+#include "pgm.h"
+#include "bitbang.h"
+#include "ft245r.h"
+
+#include <pthread.h>
+#include <semaphore.h>
+
+#define SUPPORT_FT245R
+
+#if defined(_WIN32) || defined(SUPPORT_FT245R)
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include <ftdi.h>
+
+
+#define FT245R_CYCLES	2
+#define FT245R_FRAGMENT_SIZE  512
+#define REQ_OUTSTANDINGS	10
+//#define USE_INLINE_WRITE_PAGE
+
+#define FT245R_DEBUG	1
+
+extern char * progname;
+extern int do_cycles;
+extern int ovsigck;
+extern int verbose;
+
+static struct ftdi_context *handle;
+
+static unsigned char ft245r_ddr;
+static unsigned char ft245r_sck;
+static unsigned char ft245r_mosi;
+static unsigned char ft245r_reset;
+static unsigned char ft245r_miso;
+
+
+#define BUFSIZE 0x2000
+
+// libftdi / libftd2xx compatibility functions. 
+
+pthread_t readerthread, debugthread;
+sem_t buf_data, buf_space;
+char buffer[BUFSIZE];
+int head, tail;
+
+void add_to_buf (char c)
+{
+  int nh;
+
+  sem_wait (&buf_space);
+  if (head == (BUFSIZE -1)) nh = 0;
+  else                      nh = head + 1;
+
+  if (nh == tail) {
+    fprintf (stderr, "buffer overflow. Cannot happen!\n");
+    //exit (1);
+  }
+  buffer[head] = c;
+  head = nh;  
+  sem_post (&buf_data);
+}
+
+
+
+void *reader (void *arg)
+{
+  struct ftdi_context *handle = (struct ftdi_context *)(arg);
+  unsigned char buf[0x1000];
+  int br, i;
+
+  //printf ("reader starting %p\n", handle);
+
+  while (1) {
+    //printf ("startin ftdi_read\n");
+    br = ftdi_read_data (handle, buf, sizeof(buf));
+    //printf ("R%d ", br);fflush (stdout);
+    for (i=0;i<br;i++) 
+      add_to_buf (buf[i]);
+  }
+}
+
+#if 0
+void *debugger (void *arg)
+{
+  while (1) {
+    printf ("Head=%d, tail=%d.\n", head, tail);
+    usleep (100000);
+  }
+}
+#endif
+
+
+
+
+
+
+static inline void setmybit(unsigned char *data, int pinno, int v)
+{
+	if (v) {
+		*data |= (1 << (pinno));
+	} else {
+		*data &= ~(1 <<(pinno));
+	}
+}
+
+
+
+static int ft245r_send(PROGRAMMER * pgm, char * buf, size_t len)
+{
+  int rv;
+
+  //printf ("W%d ", (int)len); fflush (stdout);
+  rv = ftdi_write_data(handle, buf, len);
+  //if (rv < 0) return -1; // is included below
+  if (len != rv) return -1;
+  return 0;
+}
+
+
+#if 0
+static inline int FT_Read (struct ftdi_context *handle, void *buf, int size, 
+			   unsigned int *bytesread)
+{
+  int i;
+  unsigned char *cbuf = buf;
+
+  for (i=0;i<size;i++) {
+    sem_wait (&buf_data);
+    cbuf[i] = buffer[tail];
+    if (tail == (BUFSIZE -1)) tail = 0;
+    else                      tail++;
+    sem_post (&buf_space);
+  }
+
+  //printf ("r%d ", i, size);
+  *bytesread = i;
+  return 0;
+}
+#endif
+
+static int ft245r_recv(PROGRAMMER * pgm, char * buf, size_t len)
+{
+  int i;
+
+  // Copy over data from the circular buffer.. 
+  // XXX This should timeout, and return error if there isn't enough
+  // data. 
+  for (i=0;i<len;i++) {
+    sem_wait (&buf_data);
+    buf[i] = buffer[tail];
+    if (tail == (BUFSIZE -1)) tail = 0;
+    else                      tail++;
+    sem_post (&buf_space);
+  }
+
+  return 0;
+}
+
+
+static int ft245r_drain(PROGRAMMER * pgm, int display)
+{
+  int r;
+  unsigned char t;
+
+  //printf ("Drain!\n");
+  // flush the buffer in the chip by changing the mode..... 
+  r = ftdi_set_bitmode(handle, 0, BITMODE_RESET); 	// reset 
+  if (r) return -1;
+  r = ftdi_set_bitmode(handle, ft245r_ddr, BITMODE_SYNCBB); // set Synchronuse BitBang
+  if (r) return -1;
+
+  // drain our buffer. 
+  while (head != tail) {
+    ft245r_recv (pgm, &t, 1);
+  }
+#if 0
+  r = FT_GetQueueStatushandle, &n);
+  if (r != FT_OK) return -1;
+  if (n) {
+	fprintf(stderr, "ft245r_drain called but queue is not empty %d \n",
+		(int)n);
+  }
+#endif
+  return 0;
+}
+
+static inline int ft245r_sync(PROGRAMMER * pgm)
+{
+  //printf ("sync.\n");
+  // The old code did something that evaluated to a no-op.
+
+  return 0;
+}
+
+static int ft245r_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char cmd[4];
+  unsigned char res[4];
+
+  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+    fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
+            p->desc);
+    return -1;
+  }
+
+  memset(cmd, 0, sizeof(cmd));
+
+  avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
+  pgm->cmd(pgm, cmd, res);
+  usleep(p->chip_erase_delay);
+  pgm->initialize(pgm, p);
+
+  return 0;
+}
+
+static unsigned char saved_signature[3];
+
+#if 0
+static int valid_rates[] = {
+   2400, 4800, 9600, 14400, 19200, 38400, 57600, 
+   115200, 230400, 460800, 921600, 3000000
+};
+#endif
+
+static void ft245r_set_bitclock(PROGRAMMER * pgm) 
+{
+  int r;
+  int rate = 0;
+
+#if 0
+  int i;
+
+  if (pgm->bitclock == 0.0) { // using default
+	  rate = 235000.0 /2; 
+  } else if (pgm->bitclock >= 0.50 ) {
+	  rate = 500000.0 /2;
+  } else if (pgm->bitclock <  0.01) {
+	  rate =   1000.0 /2;
+  } else {
+	  rate = pgm->bitclock * 1000000.0 /2;
+  }
+  for (i= sizeof(valid_rates)/sizeof(valid_rates[0]) -1; i>=0; --i) 
+  {
+    if (valid_rates[i] <= rate) {
+		rate = valid_rates[i];
+		break;
+    }
+  }
+  if (i<0) rate = valid_rates[0];
+#else
+  /* bitclock is second. 1us = 0.000001. Max rate for ft232r 750000 */
+  if(pgm->bitclock) {
+    rate = (uint32_t)(1.0/pgm->bitclock) * 2;
+  } else if (pgm->baudrate) {
+    rate = pgm->baudrate * 2;
+  } else {
+    rate = 150000; /* should work for all ftdi chips and the avr default internal clock of 1MHz */
+  }
+#endif
+
+  if ((verbose>=1) || FT245R_DEBUG) {
+	fprintf(stderr," ft245r:  spi bitclk %d -> ft baudrate %d\n", 
+			rate / 2, rate);
+  }
+  r = ftdi_set_baudrate(handle, rate);
+  if (r) {
+    fprintf (stderr, "Set baudrate (%d) failed with error '%s'.\n", 
+	    rate, ftdi_get_error_string (handle));
+    exit (1);
+  }
+}
+
+static int set_reset(PROGRAMMER * pgm, int val)
+{
+  unsigned char buf[1];
+
+  buf[0] = 0;
+  if (val) buf[0] |= ft245r_reset;
+
+  ft245r_send (pgm, buf, 1);
+  ft245r_recv (pgm, buf, 1);
+  return 0;
+}
+
+static int ft245r_cmd(PROGRAMMER * pgm, unsigned char cmd[4], 
+                      unsigned char res[4]);
+/*
+ * issue the 'program enable' command to the AVR device
+ */
+static int ft245r_program_enable(PROGRAMMER * pgm, AVRPART * p)
+{
+  int retry_count = 0;
+  unsigned char cmd[4];
+  unsigned char res[4];
+  int i,reset_ok;
+
+  ft245r_set_bitclock(pgm);
+
+retry:
+  reset_ok = 0;
+  set_reset(pgm, 0);
+  usleep(5000); // 5ms
+  set_reset(pgm, 1);
+  usleep(5000); // 5ms
+  set_reset(pgm, 0);
+  usleep(5000); // 5ms
+
+  cmd[0] = 0xAC;
+  cmd[1] = 0x53;
+  cmd[2] = 0;
+  cmd[3] = 0;
+  ft245r_cmd(pgm, cmd, res); 
+  if (res[2] == 0x53 ) reset_ok = 1;
+  for (i=0; i<3; i++) {
+     cmd[0] = 0x30;
+     cmd[1] = 0;
+     cmd[2] = i;
+     cmd[3] = 0;
+     ft245r_cmd(pgm, cmd, res); 
+     saved_signature[i] = res[3];
+  }
+  //printf ("ht=(%d,%d)\n", head, tail);
+  if (reset_ok && (saved_signature[0] == 0x1e)) // success
+     return 0;
+
+  if (retry_count < 5) {
+    // printf ("retrying %d (got %02x)\n", retry_count, saved_signature[0]);
+    if (retry_count == 3) {
+      // printf ("ht=(%d,%d)\n", head, tail);
+      ft245r_drain (pgm, 0);
+      tail = head;
+    }
+	  retry_count++;
+	  goto retry;
+  }
+  if ((verbose>=1) || FT245R_DEBUG) {
+     fprintf(stderr,
+	    "%s: ft245r_program_enable: failed\n", progname);
+     fflush(stderr);
+  }
+  return -1;
+}
+
+static int ft245r_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
+{
+   m->buf[0] = saved_signature[0];
+   m->buf[1] = saved_signature[1];
+   m->buf[2] = saved_signature[2];
+   return 3;
+}
+
+/*
+ * initialize the AVR device and prepare it to accept commands
+ */
+static int ft245r_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  return ft245r_program_enable(pgm, p);
+}
+
+static void ft245r_disable(PROGRAMMER * pgm)
+{
+  return;
+}
+
+
+static void ft245r_enable(PROGRAMMER * pgm)
+{
+  /* Do nothing. */
+
+  return;
+}
+
+static inline int set_data(unsigned char *buf, unsigned char data)
+{
+    int j;
+    int buf_pos = 0;
+    unsigned char bit = 0x80;
+
+    for (j=0; j<8; j++) {
+    	buf[buf_pos] = 0;
+	if (data & bit) buf[buf_pos] |= ft245r_mosi;
+	buf_pos++;
+
+    	buf[buf_pos] = 0;
+	if (data & bit) buf[buf_pos] |= ft245r_mosi;
+    	buf[buf_pos] |= ft245r_sck;
+	buf_pos++;
+
+	bit >>= 1;
+    }
+    return buf_pos;
+}
+
+static inline unsigned char extract_data(unsigned char *buf, int offset)
+{
+    int j;
+    int buf_pos = 1;
+    unsigned char bit = 0x80;
+    unsigned char r = 0;
+
+    buf += offset * (8 * FT245R_CYCLES);
+    for (j=0; j<8; j++) {
+	if (buf[buf_pos] & ft245r_miso) {
+		  r |= bit;
+	}
+	buf_pos += FT245R_CYCLES;
+	bit >>= 1;
+    }
+    return r;
+}
+
+/* to check data */
+static inline unsigned char extract_data_out(unsigned char *buf, int offset)
+{
+    int j;
+    int buf_pos = 1;
+    unsigned char bit = 0x80;
+    unsigned char r = 0;
+
+    buf += offset * (8 * FT245R_CYCLES);
+    for (j=0; j<8; j++) {
+	if (buf[buf_pos] & ft245r_mosi) {
+		  r |= bit;
+	}
+	buf_pos += FT245R_CYCLES;
+	bit >>= 1;
+    }
+    return r;
+}
+
+ 
+/*
+ * transmit an AVR device command and return the results; 'cmd' and
+ * 'res' must point to at least a 4 byte data buffer
+ */
+static int ft245r_cmd(PROGRAMMER * pgm, unsigned char cmd[4], 
+                      unsigned char res[4])
+{
+  int i,buf_pos;
+  unsigned char buf[128];
+
+  buf_pos = 0;
+  for (i=0; i<4; i++) {
+     buf_pos += set_data(buf+buf_pos, cmd[i]);
+  }
+  buf[buf_pos] = 0;
+  buf_pos++;
+
+  ft245r_send (pgm, buf, buf_pos);
+  ft245r_recv (pgm, buf, buf_pos);
+  res[0] = extract_data(buf, 0);
+  res[1] = extract_data(buf, 1);
+  res[2] = extract_data(buf, 2);
+  res[3] = extract_data(buf, 3);
+
+  return 0;
+}
+
+
+static int ft245r_open(PROGRAMMER * pgm, char * port)
+{
+  int rv;
+  int devnum = -1;
+  strcpy(pgm->port, port);
+
+#ifdef _WIN32
+  if (!strncasecmp("ft", port, 2) && '0' <= port[2] && port[2] <= '9') {
+     devnum = port[2] - '0';
+  } else {
+    fprintf(stderr,
+	    "%s: invalid portname  %s: use ft0 - ft9\n",
+	    progname,port);
+    exit(1);
+  }
+  //rv = ftdi_usb_open_sec_index(devnum, &handle);
+
+#else
+  //printf ("Going to open.... \n");
+  // XXX figure out a devnum!!!
+  devnum = 0;
+
+#endif
+
+  handle = malloc (sizeof (struct ftdi_context));
+  ftdi_init(handle);
+  rv = ftdi_usb_open_desc_index(handle, 0x0403, 0x6001, NULL, NULL, devnum);
+  if (rv) {
+    fprintf (stderr, "can't open ftdi device %d. (%s)\n", devnum, ftdi_get_error_string(handle));
+    ftdi_deinit (handle);
+    free(handle);
+    exit (1);
+  }
+
+
+  /* We start a new thread to read the output from the FTDI. This is
+   * necessary because otherwise we'll deadlock. We cannot finish
+   * writing because the ftdi cannot send the results because we 
+   * haven't provided a read buffer yet. */ 
+
+  sem_init (&buf_data, 0, 0);
+  sem_init (&buf_space, 0, BUFSIZE);
+  pthread_create (&readerthread, NULL, reader, handle);
+
+  //pthread_create (&debugthread, NULL, debugger, NULL);
+  //printf ("thread created %p\n", handle);
+
+  //printf ("Bitmode 4\n");
+  rv = ftdi_set_bitmode(handle, 0, BITMODE_SYNCBB); // set Synchronous BitBang
+
+  if (rv) {
+    fprintf(stderr,
+	    "%s: Synchronous BitBangMode is not supported (%s)\n",
+	    progname, ftdi_get_error_string(handle));
+    ftdi_usb_close(handle);
+    ftdi_deinit (handle);
+    free(handle);
+    exit(1);
+  }
+#if FT245R_DEBUG
+  fprintf(stderr, "%s: BitBang OK \n", progname);
+  fflush(stderr);
+#endif
+
+  ft245r_ddr = 0;
+  setmybit(&ft245r_ddr, pgm->pinno[PIN_AVR_SCK], 1);
+  setmybit(&ft245r_ddr, pgm->pinno[PIN_AVR_MOSI], 1);
+  setmybit(&ft245r_ddr, pgm->pinno[PIN_AVR_RESET], 1);
+  ft245r_sck = 0;
+  setmybit(&ft245r_sck, pgm->pinno[PIN_AVR_SCK], 1);
+  ft245r_mosi = 0;
+  setmybit(&ft245r_mosi, pgm->pinno[PIN_AVR_MOSI], 1);
+  ft245r_reset = 0;
+  setmybit(&ft245r_reset, pgm->pinno[PIN_AVR_RESET], 1);
+  ft245r_miso = 0;
+  setmybit(&ft245r_miso, pgm->pinno[PIN_AVR_MISO], 1);
+  if ((verbose>=1) || FT245R_DEBUG) {
+    fprintf(stderr,
+	    "%s: pin assign miso %d sck %d mosi %d reset %d\n",
+	      progname,
+              pgm->pinno[PIN_AVR_MISO],
+              pgm->pinno[PIN_AVR_SCK],
+              pgm->pinno[PIN_AVR_MOSI],
+              pgm->pinno[PIN_AVR_RESET]);
+  }
+
+  /*
+   * drain any extraneous input
+   */
+  ft245r_drain (pgm, 0);
+#if FT245R_DEBUG
+  fprintf(stderr, "%s: drain OK \n", progname);
+  fflush(stderr);
+#endif
+	
+  return 0;
+}
+
+
+static void ft245r_close(PROGRAMMER * pgm)
+{
+  //printf ("Closing... \n");
+
+  // I think the switch to BB mode and back flushes the buffer. 
+  ftdi_set_bitmode(handle, 0, BITMODE_SYNCBB); // set Synchronous BitBang, all in puts
+  ftdi_set_bitmode(handle, 0, BITMODE_RESET); // disable Synchronous BitBang
+  ftdi_usb_close(handle);
+  ftdi_deinit (handle);
+  free(handle);
+}
+
+static void ft245r_display(PROGRAMMER * pgm, const char * p)
+{
+  return;
+}
+
+
+static int ft245r_paged_write_gen(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				  unsigned int page_size, unsigned int addr,  
+				  unsigned int n_bytes)
+{
+  unsigned long i, pa;
+  int rc;
+
+  //printf ("paged_write_gen: %x %x %x (%d)\n", 
+  //  page_size, addr, n_bytes, m->paged);
+  
+  for (i=0; i<n_bytes; i++, addr++) {
+    rc = avr_write_byte_default(pgm, p, m, addr, m->buf[addr]);
+    if (rc != 0) {
+      return -2;
+    }
+
+    if (m->paged) {
+      // Can this piece of code ever be activated?? Do AVRs exist that
+      // have paged non-flash memories? -- REW
+      // XXX Untested code below. 
+      /*
+       * check to see if it is time to flush the page with a page
+       * write
+       */
+      
+      if (((addr % m->page_size) == m->page_size-1) || (i == n_bytes-1)) {
+	pa = addr - (addr % m->page_size);
+
+	// printf ("writing page %x/%x\n", i, pa);
+        rc = avr_write_page(pgm, p, m, pa);
+        if (rc != 0) {
+	  return -2;
+        }
+      }
+    }
+  }
+  return i;
+}
+
+
+static struct ft245r_request {
+	int addr;
+	int bytes;
+	int n;
+	struct ft245r_request *next;
+} *req_head,*req_tail,*req_pool;
+
+static void put_request(int addr, int bytes, int n)
+{
+  struct ft245r_request *p;
+  if (req_pool) {
+     p = req_pool;
+     req_pool = p->next;
+  } else {
+     p = malloc(sizeof(struct ft245r_request));
+     if (!p) {
+       fprintf(stderr, "can't alloc memory\n");
+       exit(1);
+     }
+  }
+  memset(p, 0, sizeof(struct ft245r_request));
+  p->addr = addr;
+  p->bytes = bytes;
+  p->n = n;
+  if (req_tail) {
+     req_tail->next = p;
+     req_tail = p;
+  } else {
+     req_head = req_tail = p;
+  }
+}
+
+static int do_request(PROGRAMMER * pgm, AVRMEM *m)
+{
+  struct ft245r_request *p;
+  int addr, bytes, j, n;
+  char buf[FT245R_FRAGMENT_SIZE+1+128];
+
+  if (!req_head) return 0;
+  p = req_head;
+  req_head = p->next;
+  if (!req_head) req_tail = req_head;
+
+  addr = p->addr;
+  bytes = p->bytes;
+  n = p->n;
+  memset(p, 0, sizeof(struct ft245r_request));
+  p->next = req_pool;
+  req_pool = p;
+
+  ft245r_recv(pgm, buf, bytes);
+  for (j=0; j<n; j++) {
+     m->buf[addr++] = extract_data(buf , (j * 4 + 3));
+  }
+#if 0
+if (n == 0) // paged_write
+fprintf(stderr, "recv addr 0x%04x buf size %d \n",addr, bytes);
+#endif
+  return 1;
+}
+
+static int ft245r_paged_write_flash(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+                                    int page_size, int addr, int n_bytes)
+{
+  unsigned int    i,j;
+  int addr_save,buf_pos,do_page_write,req_count;
+  char buf[FT245R_FRAGMENT_SIZE+1+128];
+
+  //printf ("paged_write_flash: %x %x %x\n", page_size, addr, n_bytes);
+
+  req_count = 0;
+  //  addr = 0;
+  for (i=0; i<n_bytes; ) {
+     addr_save = addr;
+     buf_pos = 0;
+     do_page_write = 0;
+     for (j=0; j< FT245R_FRAGMENT_SIZE/8/FT245R_CYCLES/4; j++) {
+        buf_pos += set_data(buf+buf_pos, (addr & 1)?0x48:0x40 ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 9) & 0xff ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 1) & 0xff );
+        buf_pos += set_data(buf+buf_pos, m->buf[addr]);
+	addr ++;
+	i++;
+	if ( (m->paged) &&
+             (((i % m->page_size) == 0) || (i == n_bytes))) {
+		do_page_write = 1;
+		break;
+	}
+     }
+#if defined(USE_INLINE_WRITE_PAGE)
+     if (do_page_write) {
+        int addr_wk = addr_save - (addr_save % m->page_size);
+        /* If this device has a "load extended address" command, issue it. */
+	if (m->op[AVR_OP_LOAD_EXT_ADDR]) {
+	    unsigned char cmd[4];
+	    OPCODE *lext = m->op[AVR_OP_LOAD_EXT_ADDR];
+
+	    memset(cmd, 0, 4);
+	    avr_set_bits(lext, cmd);
+	    avr_set_addr(lext, cmd, addr_wk/2);
+            buf_pos += set_data(buf+buf_pos, cmd[0]);
+            buf_pos += set_data(buf+buf_pos, cmd[1]);
+            buf_pos += set_data(buf+buf_pos, cmd[2]);
+            buf_pos += set_data(buf+buf_pos, cmd[3]);
+	}
+        buf_pos += set_data(buf+buf_pos, 0x4C); /* Issue Page Write */
+        buf_pos += set_data(buf+buf_pos,(addr_wk >> 9) & 0xff); 
+        buf_pos += set_data(buf+buf_pos,(addr_wk >> 1) & 0xff); 
+        buf_pos += set_data(buf+buf_pos, 0);
+     }
+#endif
+     if (i >= n_bytes) {
+        buf[buf_pos++] = 0; // sck down
+     }
+     ft245r_send(pgm, buf, buf_pos);
+     put_request(addr_save, buf_pos, 0);
+     //ft245r_sync(pgm);
+#if 0
+fprintf(stderr, "send addr 0x%04x bufsize %d [%02x %02x] page_write %d\n",
+		addr_save,buf_pos,
+		extract_data_out(buf , (0*4 + 3) ),
+		extract_data_out(buf , (1*4 + 3) ),
+		do_page_write);
+#endif
+     req_count++;
+     if (req_count > REQ_OUTSTANDINGS)
+        do_request(pgm, m);
+     if (do_page_write) {
+#if defined(USE_INLINE_WRITE_PAGE)
+        while (do_request(pgm, m))
+	     ;
+        usleep(m->max_write_delay);
+#else
+        int addr_wk = addr_save - (addr_save % m->page_size);
+	int rc;
+        while (do_request(pgm, m))
+	     ;
+        rc = avr_write_page(pgm, p, m, addr_wk);
+        if (rc != 0) {
+	  return -2;
+        }
+#endif
+        req_count = 0;
+     }
+  }
+  while (do_request(pgm, m))
+	  ;
+  return i;
+}
+
+
+static int ft245r_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
+                              unsigned int page_size, unsigned int addr, unsigned int n_bytes)
+{
+  //printf ("paged_write: %x %x %x\n", page_size, addr, n_bytes);
+
+  if (strcmp(m->desc, "flash") == 0) {
+    return ft245r_paged_write_flash(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    return ft245r_paged_write_gen(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else {
+    return -2;
+  }
+}
+
+static int ft245r_paged_load_gen(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				 unsigned int page_size, unsigned int addr, 
+				 int n_bytes)
+{
+  unsigned char    rbyte;
+  unsigned long    i;
+  int rc;
+
+  for (i=0; i<n_bytes; i++) {
+     rc = avr_read_byte_default(pgm, p, m, i+addr, &rbyte);
+     if (rc != 0) {
+       return -2;
+     }
+     m->buf[i+addr] = rbyte;
+  }
+  return 0;
+}
+
+
+static int ft245r_paged_load_flash(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+				   unsigned int page_size, unsigned int addr, 
+				   unsigned int n_bytes)
+{
+  unsigned long    i,j,n;
+  //int rc;
+  int addr_save,buf_pos;
+  int req_count = 0;
+  char buf[FT245R_FRAGMENT_SIZE+1];
+
+  for (i=0; i<n_bytes; ) {
+     buf_pos = 0;
+     addr_save = addr;
+     for (j=0; j< FT245R_FRAGMENT_SIZE/8/FT245R_CYCLES/4; j++) {
+	if (i >= n_bytes) break;
+        buf_pos += set_data(buf+buf_pos, (addr & 1)?0x28:0x20 ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 9) & 0xff ); 
+        buf_pos += set_data(buf+buf_pos, (addr >> 1) & 0xff );
+        buf_pos += set_data(buf+buf_pos, 0);
+	addr ++;
+	i++;
+     }
+     if (i >= n_bytes) {
+        buf[buf_pos++] = 0; // sck down
+     }
+     n = j;
+     ft245r_send(pgm, buf, buf_pos);
+     put_request(addr_save, buf_pos, n);
+     req_count++;
+     if (req_count > REQ_OUTSTANDINGS)
+        do_request(pgm, m);
+
+  }
+  while (do_request(pgm, m))
+	  ;
+  return 0;
+}
+
+static int ft245r_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 
+                             unsigned int page_size, unsigned int addr, 
+			     unsigned int n_bytes)
+{
+  if (strcmp(m->desc, "flash") == 0) {
+    return ft245r_paged_load_flash(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else if (strcmp(m->desc, "eeprom") == 0) {
+    return ft245r_paged_load_gen(pgm, p, m, page_size, addr, n_bytes);
+  }
+  else {
+    return -2;
+  }
+}
+
+void ft245r_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "ft245r");
+
+  /*
+   * mandatory functions
+   */
+  pgm->initialize     = ft245r_initialize;
+  pgm->display        = ft245r_display;
+  pgm->enable         = ft245r_enable;
+  pgm->disable        = ft245r_disable;
+  pgm->program_enable = ft245r_program_enable;
+  pgm->chip_erase     = ft245r_chip_erase;
+  pgm->cmd            = ft245r_cmd;
+  pgm->open           = ft245r_open;
+  pgm->close          = ft245r_close;
+  pgm->read_byte      = avr_read_byte_default;
+  pgm->write_byte     = avr_write_byte_default;
+
+  /*
+   * optional functions
+   */
+  pgm->paged_write = ft245r_paged_write;
+  pgm->paged_load = ft245r_paged_load;
+
+  pgm->read_sig_bytes = ft245r_read_sig_bytes;
+}
+
+#else
+static int ft245r_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  //printf ("Blaaaaaaa\n");
+  return -1;
+}
+
+void ft245r_initpgm(PROGRAMMER * pgm)
+{
+  //printf ("Baaa!\n");
+  pgm->initialize     = ft245r_initialize;
+}
+
+#endif
Index: ft245r.h
===================================================================
--- ft245r.h	(revision 0)
+++ ft245r.h	(revision 0)
@@ -0,0 +1,9 @@
+#ifndef ft245r_h
+#define ft245r_h
+
+#include "avrpart.h"
+
+void ft245r_initpgm (PROGRAMMER * pgm);
+
+
+#endif /* ft245r_h */
Index: config_gram.y
===================================================================
--- config_gram.y	(revision 1043)
+++ config_gram.y	(working copy)
@@ -43,6 +43,7 @@
 #include "wiring.h"
 #include "stk500generic.h"
 #include "avr910.h"
+#include "ft245r.h"
 #include "butterfly.h"
 #include "usbasp.h"
 #include "usbtiny.h"
@@ -154,6 +155,7 @@
 %token K_STK600HVSP
 %token K_STK600PP
 %token K_AVR910
+%token K_FT245R
 %token K_USBASP
 %token K_USBDEV
 %token K_USBSN
@@ -484,6 +486,7 @@
   K_STK600HVSP      { stk600hvsp_initpgm(current_prog); } |
   K_STK600PP        { stk600pp_initpgm(current_prog); } |
   K_AVR910          { avr910_initpgm(current_prog); } |
+  K_FT245R          { ft245r_initpgm(current_prog); } |
   K_USBASP          { usbasp_initpgm(current_prog); } |
   K_USBTINY         { usbtiny_initpgm(current_prog); } |
   K_BUTTERFLY       { butterfly_initpgm(current_prog); } |
@@ -1350,10 +1353,10 @@
   value = v->value.number;
   free_token(v);
 
-  if ((value <= 0) || (value >= 18)) {
+  if ((value < 0) || (value >= 18)) {
     fprintf(stderr, 
             "%s: error at line %d of %s: pin must be in the "
-            "range 1-17\n",
+            "range 0-17\n",
             progname, lineno, infile);
     exit(1);
   }
Index: avrdude.conf.in
===================================================================
--- avrdude.conf.in	(revision 1043)
+++ avrdude.conf.in	(working copy)
@@ -17,7 +17,7 @@
 #       desc     = <description> ;                  # quoted string
 #       type     = par | stk500 | stk500v2 | stk500pp | stk500hvsp | stk500generic |
 #                  stk600 | stk600pp | stk600hvsp |
-#                  avr910 | butterfly | usbasp |
+#                  avr910 | butterfly | usbasp | ft245r |
 #                  jtagmki | jtagmkii | jtagmkii_isp | jtagmkii_dw |
 #                  jtagmkII_avr32 | jtagmkii_pdi |
 #                  dragon_dw | dragon_jtag | dragon_isp | dragon_pp |
@@ -567,6 +567,46 @@
 ;
 
 programmer
+  id    = "ft245r";
+  desc  = "FT245R Synchronous BitBang";
+  type  = ft245r;
+  miso  = 1; # D1
+  sck   = 0; # D0
+  mosi  = 2; # D2
+  reset = 4; # D4
+;
+
+programmer
+  id    = "ft232r";
+  desc  = "FT232R Synchronous BitBang";
+  type  = ft245r;
+  miso  = 1;  # RxD
+  sck   = 0;  # RTS
+  mosi  = 2;  # TxD
+  reset = 4;  # DTR
+;
+
+programmer
+  id    = "bwmega";
+  desc  = "BitWizard ftdi_atmega builtin programmer";
+  type  = ft245r;
+  miso  = 5;  # DSR
+  sck   = 6;  # DCD
+  mosi  = 3;  # CTS
+  reset = 7;  # RI
+;
+
+programmer
+  id    = "diecimila";
+  desc  = "Arduino: FT232R connected to ISP";
+  type  = ft245r;
+  miso  = 3;  # CTS X3(1)
+  sck   = 5;  # DSR X3(2)
+  mosi  = 6;  # DCD X3(3)
+  reset = 7;  # RI  X3(4)
+;
+
+programmer
   id    = "usbasp";
   desc  = "USBasp, http://www.fischl.de/usbasp/";
   type  = usbasp;
Index: main.c
===================================================================
--- main.c	(revision 1043)
+++ main.c	(working copy)
@@ -157,8 +157,8 @@
   }
 
   if (percent == 100) {
+    if (!last) fprintf (stderr, "\n\n");
     last = 1;
-    fprintf (stderr, "\n\n");
   }
 
   setvbuf(stderr, (char*)NULL, _IOLBF, 0);
Index: lexer.l
===================================================================
--- lexer.l	(revision 1043)
+++ lexer.l	(working copy)
@@ -120,6 +120,7 @@
 arduino          { yylval=NULL; return K_ARDUINO; }
 avr910           { yylval=NULL; return K_AVR910; }
 avr910_devcode   { yylval=NULL; return K_AVR910_DEVCODE; }
+ft245r           { yylval=NULL; return K_FT245R; }
 avrftdi          { yylval=NULL; return K_AVRFTDI; }
 usbasp           { yylval=NULL; return K_USBASP; }
 usbtiny          { yylval=NULL; return K_USBTINY; }
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 1043)
+++ Makefile.am	(working copy)
@@ -91,6 +91,8 @@
 	avr.h \
 	avr910.c \
 	avr910.h \
+	ft245r.c \
+	ft245r.h \
 	avrdude.h \
 	avrftdi.c \
 	avrftdi.h \
