diff --git a/ChangeLog b/ChangeLog
index 3d468759..d576dfcf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2012-02-01  Rene Liebscher <R.Liebscher@gmx.de>
+
+	bug #30559 Ft232 bit-bang support
+	* ft245r.[ch]: new programmer type implementation
+	* configure.ac: add pthread as link library
+	* avrdude.conf.in: added some new programmers
+	* Makefile.am: added new source files to compile
+	* pindefs.h: change PIN_MASK, PIN_INVERSE to highest bit of unsigned int
+	* pgm.[ch]: added generic function to print pin assignments (taken from par.c)
+	* par.c: moved pin assigment print function to pgm.c
+
 2012-02-01  Joerg Wunsch <j.gnu@uriah.heep.sax.de>
 
 	* lexer.l: Sort keyword tokens into alphabetic order.
diff --git a/Makefile.am b/Makefile.am
index f4ef4db3..4e05bb44 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -91,6 +91,8 @@ libavrdude_a_SOURCES = \
 	avr.h \
 	avr910.c \
 	avr910.h \
+	ft245r.c \
+	ft245r.h \
 	avrdude.h \
 	avrftdi.c \
 	avrftdi.h \
diff --git a/avrdude.conf.in b/avrdude.conf.in
index 5f5c9532..a8d7da1e 100644
--- a/avrdude.conf.in
+++ b/avrdude.conf.in
@@ -16,13 +16,8 @@
 #       parent <id>                                 # optional parent
 #       id       = <id1> [, <id2> [, <id3>] ...] ;  # <idN> are quoted strings
 #       desc     = <description> ;                  # quoted string
-#       type     = par | stk500 | stk500v2 | stk500pp | stk500hvsp | stk500generic |
-#                  stk600 | stk600pp | stk600hvsp |
-#                  avr910 | butterfly | usbasp |
-#                  jtagmki | jtagmkii | jtagmkii_isp | jtagmkii_dw |
-#                  jtagmkII_avr32 | jtagmkii_pdi |
-#                  dragon_dw | dragon_jtag | dragon_isp | dragon_pp |
-#                  dragon_hvsp | dragon_pdi | arduino | wiring; # programmer type
+#       type     = <type>;                          # programmer type, quoted string
+#                          # supported programmer types can be listed by "-c ?type"
 #       connection_type = parallel | serial | usb
 #       baudrate = <num> ;                          # baudrate for avr910-programmer
 #       vcc      = <num1> [, <num2> ... ] ;         # pin number(s)
@@ -576,6 +571,59 @@ programmer
   connection_type = serial;
 ;
 
+programmer
+  id    = "ft245r";
+  desc  = "FT245R Synchronous BitBang";
+  type  = "ftdi_syncbb";
+  connection_type = usb;
+  miso  = 2; # D1
+  sck   = 1; # D0
+  mosi  = 3; # D2
+  reset = 5; # D4
+;
+
+programmer
+  id    = "ft232r";
+  desc  = "FT232R Synchronous BitBang";
+  type  = "ftdi_syncbb";
+  connection_type = usb;
+  miso  = 2;  # RxD
+  sck   = 1;  # RTS
+  mosi  = 3;  # TxD
+  reset = 5;  # DTR
+;
+
+# see http://www.bitwizard.nl/wiki/index.php/FTDI_ATmega
+programmer
+  id    = "bwmega";
+  desc  = "BitWizard ftdi_atmega builtin programmer";
+  type  = "ftdi_syncbb";
+  connection_type = usb;
+  miso  = 6;  # DSR
+  sck   = 7;  # DCD
+  mosi  = 4;  # CTS
+  reset = 8;  # RI
+;
+
+# see http://www.geocities.jp/arduino_diecimila/bootloader/index_en.html
+# Note: pins are numbered from 1!
+programmer
+  id    = "arduino-ft232r";
+  desc  = "Arduino: FT232R connected to ISP";
+  type  = "ftdi_syncbb";
+  connection_type = usb;
+  miso  = 4;  # CTS X3(1)
+  sck   = 6;  # DSR X3(2)
+  mosi  = 7;  # DCD X3(3)
+  reset = 8;  # RI  X3(4)
+;
+
+# website mentioned above uses this id
+programmer parent "arduino-ft232r"
+  id    = "diecimila";
+  desc  = "alias for arduino-ft232r";
+;
+
 programmer
   id    = "usbasp";
   desc  = "USBasp, http://www.fischl.de/usbasp/";
diff --git a/configure.ac b/configure.ac
index fc36c85b..70c39674 100644
--- a/configure.ac
+++ b/configure.ac
@@ -135,7 +135,7 @@ AH_TEMPLATE([HAVE_LIBFTDI],
             [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)
diff --git a/ft245r.c b/ft245r.c
new file mode 100644
index 00000000..bf14368c
--- /dev/null
+++ b/ft245r.c
@@ -0,0 +1,831 @@
+
+/*
+ * 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  = 2;  # RxD   / D1
+  sck   = 1;  # RTS   / D0
+  mosi  = 3;  # TxD   / D2
+  reset = 5;  # 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 "avrdude.h"
+#include "avr.h"
+#include "avrpart.h"
+#include "pindefs.h"
+#include "pgm.h"
+#include "config.h"
+#include "bitbang.h"
+#include "ft245r.h"
+
+#include <pthread.h>
+#include <semaphore.h>
+
+#ifdef HAVE_LIBFTDI
+
+#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
+
+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.
+
+static pthread_t readerthread;
+static sem_t buf_data, buf_space;
+static unsigned char buffer[BUFSIZE];
+static int head, tail;
+
+static void add_to_buf (unsigned 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);
+}
+
+static void *reader (void *arg) {
+    struct ftdi_context *handle = (struct ftdi_context *)(arg);
+    unsigned char buf[0x1000];
+    int br, i;
+
+    while (1) {
+        br = ftdi_read_data (handle, buf, sizeof(buf));
+        for (i=0; i<br; i++)
+            add_to_buf (buf[i]);
+    }
+    return NULL;
+}
+
+static inline void setmybits(unsigned char *data, int pins, int v) {
+    if (v) {
+        *data |= (pins >> 1);
+    } else {
+        *data &= ~(pins >> 1);
+    }
+}
+
+static inline void setmybit(unsigned char *data, int pinno, int v) {
+    if (v) {
+        *data |= (1 << (pinno-1));
+    } else {
+        *data &= ~(1 <<(pinno-1));
+    }
+}
+
+static int ft245r_send(PROGRAMMER * pgm, unsigned char * buf, size_t len) {
+    int rv;
+
+    rv = ftdi_write_data(handle, buf, len);
+    if (len != rv) return -1;
+    return 0;
+}
+
+static int ft245r_recv(PROGRAMMER * pgm, unsigned 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;
+
+    // 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);
+    }
+    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];
+
+static void ft245r_set_bitclock(PROGRAMMER * pgm) {
+    int r;
+    int rate = 0;
+
+    /* 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 */
+    }
+
+    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];
+    }
+    if (reset_ok && (saved_signature[0] == 0x1e)) // success
+        return 0;
+
+    if (retry_count < 5) {
+        if (retry_count == 3) {
+            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;
+}
+
+#define check_pin(s) {\
+    if ((pgm->pinno[s] & PIN_MASK) == 0) {\
+        fprintf(stderr,\
+                "%s: pin %s is not set\n",\
+                progname,#s);\
+        exit(1);\
+    }\
+    if ((pgm->pinno[s] & PIN_INVERSE) != 0) {\
+        fprintf(stderr,\
+                "%s: pin %s inverse is not supported.\n",\
+                progname,#s);\
+        exit(1);\
+    }\
+}
+
+/*
+ * initialize the AVR device and prepare it to accept commands
+ */
+static int ft245r_initialize(PROGRAMMER * pgm, AVRPART * p) {
+    check_pin(PIN_AVR_SCK);
+    check_pin(PIN_AVR_MOSI);
+    check_pin(PIN_AVR_MISO);
+    check_pin(PIN_AVR_RESET);
+    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);
+
+    if (strcmp(port,DEFAULT_USB) != 0) {
+        if (strncasecmp("ft", port, 2) == 0) {
+            char *startptr = port + 2;
+            char *endptr = NULL;
+            devnum = strtol(startptr,&endptr,10);
+            if ((startptr==endptr) || (*endptr != '\0')) {
+                devnum = -1;
+            }
+        }
+        if (devnum < 0) {
+            fprintf(stderr,
+                    "%s: invalid portname '%s': use 'ft[0-9]+'\n",
+                    progname,port);
+            exit(1);
+        }
+    } else {
+        devnum = 0;
+    }
+
+    handle = malloc (sizeof (struct ftdi_context));
+    ftdi_init(handle);
+    rv = ftdi_usb_open_desc_index(handle,
+                                  pgm->usbvid?pgm->usbvid:0x0403,
+                                  pgm->usbpid?pgm->usbpid:0x6001,
+                                  pgm->usbproduct[0]?pgm->usbproduct:NULL,
+                                  pgm->usbsn[0]?pgm->usbsn: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);
+
+    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);
+
+    rv = ftdi_set_bitmode(handle, ft245r_ddr, 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);
+    }
+    /*
+     * drain any extraneous input
+     */
+    ft245r_drain (pgm, 0);
+
+    return 0;
+}
+
+
+static void ft245r_close(PROGRAMMER * pgm) {
+    // 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) {
+    fprintf(stderr, "%sPin assignment  : 1..8 = DBUS0..7, 9..12 = GPIO0..3\n",p);
+    pgm_display_generic_mask(pgm, p, SHOW_AVR_PINS);
+}
+
+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;
+
+    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);
+
+                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;
+    unsigned 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));
+    }
+    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;
+    unsigned char buf[FT245R_FRAGMENT_SIZE+1+128];
+
+    req_count = 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) {
+    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 addr_save,buf_pos;
+    int req_count = 0;
+    unsigned 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, "ftdi_syncbb");
+
+    /*
+     * 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_noftdi_open (struct programmer_t *pgm, char * name) {
+    fprintf(stderr,
+            "%s: error: no ftdi support. Please compile again with libftdi installed.\n",
+            progname);
+
+    exit(1);
+}
+
+void ft245r_initpgm(PROGRAMMER * pgm) {
+    strcpy(pgm->type, "ftdi_syncbb");
+    pgm->open = ft245r_noftdi_open;
+}
+#endif
+
+const char ft245r_desc[] = "FT245R/FT232R Synchronous BitBangMode Programmer";
diff --git a/ft245r.h b/ft245r.h
new file mode 100644
index 00000000..81479496
--- /dev/null
+++ b/ft245r.h
@@ -0,0 +1,10 @@
+#ifndef ft245r_h
+#define ft245r_h
+
+#include "pgm.h"
+
+extern const char ft245r_desc[];
+void ft245r_initpgm (PROGRAMMER * pgm);
+
+
+#endif /* ft245r_h */
diff --git a/par.c b/par.c
index 37f640cc..2f669966 100644
--- a/par.c
+++ b/par.c
@@ -178,25 +178,6 @@ static int par_highpulsepin(PROGRAMMER * pgm, int pin)
   return 0;
 }
 
-static char * pins_to_str(unsigned int pmask)
-{
-  static char buf[64];
-  int pin;
-  char b2[8];
-
-  buf[0] = 0;
-  for (pin = 1; pin <= 17; pin++) {
-    if (pmask & (1 << pin)) {
-      sprintf(b2, "%d", pin);
-      if (buf[0] != 0)
-        strcat(buf, ",");
-      strcat(buf, b2);
-    }
-  }
-
-  return buf;
-}
-
 /*
  * apply power to the AVR processor
  */
@@ -337,52 +318,6 @@ static void par_close(PROGRAMMER * pgm)
   pgm->fd.ifd = -1;
 }
 
-static void par_display(PROGRAMMER * pgm, const char * p)
-{
-  char vccpins[64];
-  char buffpins[64];
-
-  if (pgm->pinno[PPI_AVR_VCC]) {
-    snprintf(vccpins, sizeof(vccpins), "%s",
-             pins_to_str(pgm->pinno[PPI_AVR_VCC]));
-  }
-  else {
-    strcpy(vccpins, " (not used)");
-  }
-
-  if (pgm->pinno[PPI_AVR_BUFF]) {
-    snprintf(buffpins, sizeof(buffpins), "%s",
-             pins_to_str(pgm->pinno[PPI_AVR_BUFF]));
-  }
-  else {
-    strcpy(buffpins, " (not used)");
-  }
-
-  fprintf(stderr, 
-          "%s  VCC     = %s\n"
-          "%s  BUFF    = %s\n"
-          "%s  RESET   = %d\n"
-          "%s  SCK     = %d\n"
-          "%s  MOSI    = %d\n"
-          "%s  MISO    = %d\n"
-          "%s  ERR LED = %d\n"
-          "%s  RDY LED = %d\n"
-          "%s  PGM LED = %d\n"
-          "%s  VFY LED = %d\n",
-
-          p, vccpins,
-          p, buffpins,
-          p, pgm->pinno[PIN_AVR_RESET],
-          p, pgm->pinno[PIN_AVR_SCK],
-          p, pgm->pinno[PIN_AVR_MOSI],
-          p, pgm->pinno[PIN_AVR_MISO],
-          p, pgm->pinno[PIN_LED_ERR],
-          p, pgm->pinno[PIN_LED_RDY],
-          p, pgm->pinno[PIN_LED_PGM],
-          p, pgm->pinno[PIN_LED_VFY]);
-}
-
-
 /*
  * parse the -E string
  */
@@ -433,7 +368,7 @@ void par_initpgm(PROGRAMMER * pgm)
   pgm->pgm_led        = bitbang_pgm_led;
   pgm->vfy_led        = bitbang_vfy_led;
   pgm->initialize     = bitbang_initialize;
-  pgm->display        = par_display;
+  pgm->display        = pgm_display_generic;
   pgm->enable         = par_enable;
   pgm->disable        = par_disable;
   pgm->powerup        = par_powerup;
diff --git a/pgm.c b/pgm.c
index f0e3b5b3..eaa0ead1 100644
--- a/pgm.c
+++ b/pgm.c
@@ -212,6 +212,72 @@ void programmer_display(PROGRAMMER * pgm, const char * p)
   pgm->display(pgm, p);
 }
 
+static char * pins_to_str(unsigned int pmask)
+{
+  static char buf[64];
+  int pin;
+  char b2[8];
+
+  if ((pmask & PIN_MASK) == 0)
+     return " (not used)";
+
+  buf[0] = ' ';
+  buf[1] = 0;
+  for (pin = 0; pin <= 17; pin++) {
+    if (pmask & (1 << pin)) {
+      sprintf(b2, "%d", pin);
+      if (buf[1] != 0)
+        strcat(buf, ",");
+      strcat(buf, b2);
+    }
+  }
+
+  return buf;
+}
+
+static char * pin_to_str(unsigned int pin)
+{
+  static char buf[12];
+
+  if ((pin & PIN_MASK) == 0)
+     return " (not used)";
+
+  buf[0] = (pin & PIN_INVERSE)?'~':' ';
+  buf[1] = 0;
+  sprintf(buf+1, "%d", pin & PIN_MASK);
+
+  return buf;
+}
+
+void pgm_display_generic_mask(PROGRAMMER * pgm, const char * p, unsigned int show)
+{
+  if(show & (1<<PPI_AVR_VCC)) 
+    fprintf(stderr, "%s  VCC     = %s\n", p, pins_to_str(pgm->pinno[PPI_AVR_VCC]));
+  if(show & (1<<PPI_AVR_BUFF))
+    fprintf(stderr, "%s  BUFF    = %s\n", p, pins_to_str(pgm->pinno[PPI_AVR_BUFF]));
+  if(show & (1<<PIN_AVR_RESET))
+    fprintf(stderr, "%s  RESET   = %s\n", p, pin_to_str(pgm->pinno[PIN_AVR_RESET]));
+  if(show & (1<<PIN_AVR_SCK))
+    fprintf(stderr, "%s  SCK     = %s\n", p, pin_to_str(pgm->pinno[PIN_AVR_SCK]));
+  if(show & (1<<PIN_AVR_MOSI))
+    fprintf(stderr, "%s  MOSI    = %s\n", p, pin_to_str(pgm->pinno[PIN_AVR_MOSI]));
+  if(show & (1<<PIN_AVR_MISO))
+    fprintf(stderr, "%s  MISO    = %s\n", p, pin_to_str(pgm->pinno[PIN_AVR_MISO]));
+  if(show & (1<<PIN_LED_ERR))
+    fprintf(stderr, "%s  ERR LED = %s\n", p, pin_to_str(pgm->pinno[PIN_LED_ERR]));
+  if(show & (1<<PIN_LED_RDY))
+    fprintf(stderr, "%s  RDY LED = %s\n", p, pin_to_str(pgm->pinno[PIN_LED_RDY]));
+  if(show & (1<<PIN_LED_PGM))
+    fprintf(stderr, "%s  PGM LED = %s\n", p, pin_to_str(pgm->pinno[PIN_LED_PGM]));
+  if(show & (1<<PIN_LED_VFY))
+    fprintf(stderr, "%s  VFY LED = %s\n", p, pin_to_str(pgm->pinno[PIN_LED_VFY]));
+}
+
+void pgm_display_generic(PROGRAMMER * pgm, const char * p)
+{
+  pgm_display_generic_mask(pgm, p, SHOW_ALL_PINS);
+}
+
 PROGRAMMER * locate_programmer(LISTID programmers, const char * configid)
 {
   LNODEID ln1, ln2;
diff --git a/pgm.h b/pgm.h
index c8097980..784a32f4 100644
--- a/pgm.h
+++ b/pgm.h
@@ -143,6 +143,15 @@ PROGRAMMER * pgm_dup(const PROGRAMMER const * src);
 void         pgm_free(PROGRAMMER * const p);
 
 void programmer_display(PROGRAMMER * pgm, const char * p);
+
+/* show is a mask like this (1<<PIN_AVR_SCK)|(1<<PIN_AVR_MOSI)| ... */
+#define SHOW_ALL_PINS (~0u)
+#define SHOW_PPI_PINS ((1<<PPI_AVR_VCC)|(1<<PPI_AVR_BUFF))
+#define SHOW_AVR_PINS ((1<<PIN_AVR_RESET)|(1<<PIN_AVR_SCK)|(1<<PIN_AVR_MOSI)|(1<<PIN_AVR_MISO))
+#define SHOW_LED_PINS ((1<<PIN_LED_ERR)|(1<<PIN_LED_RDY)|(1<<PIN_LED_PGM)|(1<<PIN_LED_VFY))
+void pgm_display_generic_mask(PROGRAMMER * pgm, const char * p, unsigned int show);
+void pgm_display_generic(PROGRAMMER * pgm, const char * p);
+
 PROGRAMMER * locate_programmer(LISTID programmers, const char * configid);
 
 typedef void (*walk_programmers_cb)(const char *name, const char *desc,
diff --git a/pgm_type.c b/pgm_type.c
index 47b386c5..541ba444 100644
--- a/pgm_type.c
+++ b/pgm_type.c
@@ -35,6 +35,7 @@
 #include "avrftdi.h"
 #include "buspirate.h"
 #include "butterfly.h"
+#include "ft245r.h"
 #include "jtagmkI.h"
 #include "jtagmkII.h"
 #include "par.h"
@@ -61,6 +62,7 @@ const PROGRAMMER_TYPE const programmers_types[] = {
         {"dragon_jtag", jtagmkII_dragon_initpgm, jtagmkII_dragon_desc},
         {"dragon_pdi", jtagmkII_dragon_pdi_initpgm, jtagmkII_dragon_pdi_desc},
         {"dragon_pp", stk500v2_dragon_pp_initpgm, stk500v2_dragon_pp_desc},
+        {"ftdi_syncbb", ft245r_initpgm, ft245r_desc},
         {"jtagmki", jtagmkI_initpgm, jtagmkI_desc},
         {"jtagmkii", jtagmkII_initpgm, jtagmkII_desc},
         {"jtagmkii_avr32", jtagmkII_avr32_initpgm, jtagmkII_avr32_desc},
diff --git a/pindefs.h b/pindefs.h
index 9c16ed1c..282ce5ba 100644
--- a/pindefs.h
+++ b/pindefs.h
@@ -22,6 +22,8 @@
 #ifndef __pindefs_h__
 #define __pindefs_h__
 
+#include <limits.h>
+
 enum {
   PPI_AVR_VCC=1,
   PPI_AVR_BUFF,
@@ -35,8 +37,8 @@ enum {
   PIN_LED_VFY,
   N_PINS
 };
-#define PIN_INVERSE 0x80	/* flag for inverted pin in serbb */
-#define PIN_MASK    0x7f
+#define PIN_MASK    (UINT_MAX>>1)
+#define PIN_INVERSE (~(PIN_MASK))	/* flag for inverted pin in serbb */
 
 #define LED_ON(fd,pin)  ppi_setpin(fd,pin,0)
 #define LED_OFF(fd,pin) ppi_setpin(fd,pin,1)