From a193330803bd85c1baaf5aea07d7c1ef0df60098 Mon Sep 17 00:00:00 2001
From: "Brian S. Dean" <bsd@bsdhome.com>
Date: Sat, 30 Nov 2002 14:09:12 +0000
Subject: [PATCH] Seperate programmer operations out into a driver-like
 interface so that programmers other than the direct parallel port connection
 can be supported.

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@159 81a1dc3b-b13d-400b-aceb-764788c761c2
---
 Makefile            |   1 +
 avr.c               | 313 ++++++---------------------------
 avr.h               |  87 ++--------
 avrdude.conf.sample |   4 +
 config.c            |  23 ---
 config.h            |   7 -
 config_gram.y       |  25 ++-
 lexer.l             |   5 +-
 main.c              | 216 ++++++++---------------
 pgm.c               | 135 +++++++++++++++
 pgm.h               |  75 ++++++++
 ppi.c               | 413 ++++++++++++++++++++++++++++++++++++++++++++
 ppi.h               |  44 +++++
 term.c              |  63 +++----
 term.h              |   3 +-
 15 files changed, 868 insertions(+), 546 deletions(-)
 create mode 100644 pgm.c
 create mode 100644 pgm.h

diff --git a/Makefile b/Makefile
index 7ba16868..82a2c300 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ DIRS         = ${BINDIR} ${MANDIR} ${DOCDIR} ${CONFIGDIR}
 INSTALL      = /usr/bin/install -c -o root -g wheel
 
 CFLAGS       += -Wall -DCONFIG_DIR=\"${CONFIGDIR}\" ${YYDEF}
+#CFLAGS       = -g -Wall -DCONFIG_DIR=\"${CONFIGDIR}\" ${YYDEF}
 LDFLAGS      =  
 YFLAGS       = -t -d -v
 
diff --git a/avr.c b/avr.c
index 8d28cbb8..8555ec8d 100644
--- a/avr.c
+++ b/avr.c
@@ -177,65 +177,6 @@ AVRMEM * avr_locate_mem(AVRPART * p, char * desc)
 
 
 
-/*
- * transmit and receive a byte of data to/from the AVR device
- */
-unsigned char avr_txrx(int fd, unsigned char byte)
-{
-  int i;
-  unsigned char r, b, rbyte;
-
-  rbyte = 0;
-  for (i=0; i<8; i++) {
-    b = (byte >> (7-i)) & 0x01;
-
-    /* 
-     * read the result bit (it is either valid from a previous clock
-     * pulse or it is ignored in the current context)
-     */
-    r = ppi_getpin(fd, pgm->pinno[PIN_AVR_MISO]);
-    
-    /* set the data input line as desired */
-    ppi_setpin(fd, pgm->pinno[PIN_AVR_MOSI], b);
-    
-    /* 
-     * pulse the clock line, clocking in the MOSI data, and clocking out
-     * the next result bit
-     */
-    ppi_pulsepin(fd, pgm->pinno[PIN_AVR_SCK]);
-
-    rbyte = rbyte | (r << (7-i));
-  }
-
-  return rbyte;
-}
-
-
-/*
- * transmit an AVR device command and return the results; 'cmd' and
- * 'res' must point to at least a 4 byte data buffer
- */
-int avr_cmd(int fd, unsigned char cmd[4], unsigned char res[4])
-{
-  int i;
-
-  for (i=0; i<4; i++) {
-    res[i] = avr_txrx(fd, cmd[i]);
-  }
-
-#if 0
-  fprintf(stderr, "avr_cmd(): [ ");
-  for (i=0; i<4; i++)
-    fprintf(stderr, "%02x ", cmd[i]);
-  fprintf(stderr, "] [ ");
-  for (i=0; i<4; i++)
-    fprintf(stderr, "%02x ", res[i]);
-  fprintf(stderr, "]\n");
-#endif
-
-  return 0;
-}
-
 
 /*
  * avr_set_bits()
@@ -354,16 +295,16 @@ int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data)
 /*
  * read a byte of data from the indicated memory region
  */
-int avr_read_byte(int fd, AVRPART * p, AVRMEM * mem, unsigned long addr, 
-                  unsigned char * value)
+int avr_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, 
+                  unsigned long addr, unsigned char * value)
 {
   unsigned char cmd[4];
   unsigned char res[4];
   unsigned char data;
   OPCODE * readop;
 
-  LED_ON(fd, pgm->pinno[PIN_LED_PGM]);
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
+  pgm->pgm_led(pgm, ON);
+  pgm->err_led(pgm, OFF);
 
   /*
    * figure out what opcode to use
@@ -392,11 +333,11 @@ int avr_read_byte(int fd, AVRPART * p, AVRMEM * mem, unsigned long addr,
 
   avr_set_bits(readop, cmd);
   avr_set_addr(readop, cmd, addr);
-  avr_cmd(fd, cmd, res);
+  pgm->cmd(pgm, cmd, res);
   data = 0;
   avr_get_output(readop, res, &data);
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
+  pgm->pgm_led(pgm, OFF);
 
   *value = data;
 
@@ -411,7 +352,8 @@ int avr_read_byte(int fd, AVRPART * p, AVRMEM * mem, unsigned long addr,
  *
  * Return the number of bytes read, or < 0 if an error occurs.  
  */
-int avr_read(int fd, AVRPART * p, char * memtype, int size, int verbose)
+int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, 
+             int verbose)
 {
   unsigned char    rbyte;
   unsigned long    i;
@@ -435,7 +377,7 @@ int avr_read(int fd, AVRPART * p, char * memtype, int size, int verbose)
   printed = 0;
 
   for (i=0; i<size; i++) {
-    rc = avr_read_byte(fd, p, mem, i, &rbyte);
+    rc = avr_read_byte(pgm, p, mem, i, &rbyte);
     if (rc != 0) {
       fprintf(stderr, "avr_read(): error reading address 0x%04lx\n", i);
       if (rc == -1) 
@@ -464,7 +406,7 @@ int avr_read(int fd, AVRPART * p, char * memtype, int size, int verbose)
 /*
  * write a page data at the specified address
  */
-int avr_write_page(int fd, AVRPART * p, AVRMEM * mem,
+int avr_write_page(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, 
                    unsigned long addr)
 {
   unsigned char cmd[4];
@@ -486,14 +428,14 @@ int avr_write_page(int fd, AVRPART * p, AVRMEM * mem,
   if (mem->op[AVR_OP_LOADPAGE_LO])
     addr = addr / 2;
 
-  LED_ON(fd, pgm->pinno[PIN_LED_PGM]);
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
+  pgm->pgm_led(pgm, ON);
+  pgm->err_led(pgm, OFF);
 
   memset(cmd, 0, sizeof(cmd));
 
   avr_set_bits(wp, cmd);
   avr_set_addr(wp, cmd, addr);
-  avr_cmd(fd, cmd, res);
+  pgm->cmd(pgm, cmd, res);
 
   /*
    * since we don't know what voltage the target AVR is powered by, be
@@ -501,7 +443,7 @@ int avr_write_page(int fd, AVRPART * p, AVRMEM * mem,
    */
   usleep(mem->max_write_delay);
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
+  pgm->pgm_led(pgm, OFF);
   return 0;
 }
 
@@ -509,7 +451,7 @@ int avr_write_page(int fd, AVRPART * p, AVRMEM * mem,
 /*
  * write a byte of data at the specified address
  */
-int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
+int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
                    unsigned long addr, unsigned char data)
 {
   unsigned char cmd[4];
@@ -529,7 +471,7 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
      * value and only write if we are changing the value; we can't
      * use this optimization for paged addressing.
      */
-    rc = avr_read_byte(fd, p, mem, addr, &b);
+    rc = avr_read_byte(pgm, p, mem, addr, &b);
     if (rc != 0) {
       if (rc != -1) {
         return -2;
@@ -578,15 +520,15 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
   }
 
 
-  LED_ON(fd, pgm->pinno[PIN_LED_PGM]);
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
+  pgm->pgm_led(pgm, ON);
+  pgm->err_led(pgm, OFF);
 
   memset(cmd, 0, sizeof(cmd));
 
   avr_set_bits(writeop, cmd);
   avr_set_addr(writeop, cmd, caddr);
   avr_set_input(writeop, cmd, data);
-  avr_cmd(fd, cmd, res);
+  pgm->cmd(pgm, cmd, res);
 
   if (mem->paged) {
     /*
@@ -594,7 +536,7 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
      * page complete immediately, we only need to delay when we commit
      * the whole page via the avr_write_page() routine.
      */
-    LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
+    pgm->pgm_led(pgm, OFF);
     return 0;
   }
 
@@ -604,7 +546,7 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
      * the max programming time and then return 
      */
     usleep(mem->max_write_delay); /* maximum write delay */
-    LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
+    pgm->pgm_led(pgm, OFF);
     return 0;
   }
 
@@ -612,10 +554,10 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
   ready = 0;
   while (!ready) {
     usleep(mem->min_write_delay);
-    rc = avr_read_byte(fd, p, mem, addr, &r);
+    rc = avr_read_byte(pgm, p, mem, addr, &r);
     if (rc != 0) {
-      LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->pgm_led(pgm, OFF);
+      pgm->err_led(pgm, ON);
       return -4;
     }
 
@@ -628,10 +570,10 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
        * specified for the chip.
        */
       usleep(mem->max_write_delay);
-      rc = avr_read_byte(fd, p, mem, addr, &r);
+      rc = avr_read_byte(pgm, p, mem, addr, &r);
       if (rc != 0) {
-        LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
-        LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+        pgm->pgm_led(pgm, OFF);
+        pgm->err_led(pgm, OFF);
         return -5;
       }
     }
@@ -648,15 +590,15 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
        * device if the data read back does not match what we wrote.
        */
       usleep(mem->max_write_delay); /* maximum write delay */
-      LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
+      pgm->pgm_led(pgm, OFF);
       fprintf(stderr,
               "%s: this device must be powered off and back on to continue\n",
               progname);
       if (pgm->pinno[PPI_AVR_VCC]) {
         fprintf(stderr, "%s: attempting to do this now ...\n", progname);
-        avr_powerdown(fd);
+        pgm->powerdown(pgm);
         usleep(250000);
-        rc = avr_initialize(fd, p);
+        rc = pgm->initialize(pgm, p);
         if (rc < 0) {
           fprintf(stderr, "%s: initialization failed, rc=%d\n", progname, rc);
           fprintf(stderr, 
@@ -682,14 +624,14 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
        * been plenty of time, the memory cell still doesn't match what
        * we wrote.  Indicate a write error.
        */
-      LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->pgm_led(pgm, OFF);
+      pgm->err_led(pgm, ON);
       
       return -6;
     }
   }
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
+  pgm->pgm_led(pgm, OFF);
   return 0;
 }
 
@@ -703,7 +645,8 @@ int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
  *
  * Return the number of bytes written, or -1 if an error occurs.
  */
-int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
+int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, 
+              int verbose)
 {
   int              rc;
   int              wsize;
@@ -720,7 +663,7 @@ int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
     return -1;
   }
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
+  pgm->err_led(pgm, OFF);
 
   printed = 0;
   werror  = 0;
@@ -745,11 +688,11 @@ int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
         printed = 1;
       }
     }
-    rc = avr_write_byte(fd, p, m, i, data);
+    rc = avr_write_byte(pgm, p, m, i, data);
     if (rc) {
       fprintf(stderr, " ***failed;  ");
       fprintf(stderr, "\n");
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->err_led(pgm, ON);
       werror = 1;
     }
 
@@ -760,7 +703,7 @@ int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
        */
       if (((i % m->page_size) == m->page_size-1) ||
           (i == wsize-1)) {
-        rc = avr_write_page(fd, p, m, i);
+        rc = avr_write_page(pgm, p, m, i);
         if (rc) {
           fprintf(stderr,
                   " *** page %ld (addresses 0x%04lx - 0x%04lx) failed "
@@ -768,7 +711,7 @@ int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
                   i % m->page_size, 
                   i - m->page_size + 1, i);
           fprintf(stderr, "\n");
-          LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+          pgm->err_led(pgm, ON);
           werror = 1;
         }
       }
@@ -779,7 +722,7 @@ int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
        * make sure the error led stay on if there was a previous write
        * error, otherwise it gets cleared in avr_write_byte() 
        */
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->err_led(pgm, ON);
     }
   }
 
@@ -790,93 +733,15 @@ int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose)
 }
 
 
-/*
- * issue the 'program enable' command to the AVR device
- */
-int avr_program_enable(int fd, AVRPART * p)
-{
-  unsigned char cmd[4];
-  unsigned char res[4];
-
-  if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
-    fprintf(stderr, "program enable instruction not defined for part \"%s\"\n",
-            p->desc);
-    return -1;
-  }
-
-  memset(cmd, 0, sizeof(cmd));
-  avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd);
-  avr_cmd(fd, cmd, res);
-
-  if (res[2] != cmd[1])
-    return -2;
-
-  return 0;
-}
-
-
-/*
- * issue the 'chip erase' command to the AVR device
- */
-int avr_chip_erase(int fd, AVRPART * p)
-{
-  unsigned char cmd[4];
-  unsigned char res[4];
-  int cycles;
-  int rc;
-
-  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
-    fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
-            p->desc);
-    return -1;
-  }
-
-  rc = avr_get_cycle_count(fd, p, &cycles);
-
-  /*
-   * only print out the current cycle count if we aren't going to
-   * display it below 
-   */
-  if (!do_cycles && ((rc >= 0) && (cycles != 0xffffffff))) {
-    fprintf(stderr,
-            "%s: current erase-rewrite cycle count is %d%s\n",
-            progname, cycles, 
-            do_cycles ? "" : " (if being tracked)");
-  }
-
-  LED_ON(fd, pgm->pinno[PIN_LED_PGM]);
-
-  memset(cmd, 0, sizeof(cmd));
-
-  avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
-  avr_cmd(fd, cmd, res);
-  usleep(p->chip_erase_delay);
-  avr_initialize(fd, p);
-
-  LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
-
-  if (do_cycles && (cycles != -1)) {
-    if (cycles == 0x00ffff) {
-      cycles = 0;
-    }
-    cycles++;
-    fprintf(stderr, "%s: erase-rewrite cycle count is now %d\n", 
-            progname, cycles);
-    avr_put_cycle_count(fd, p, cycles);
-  }
-
-  return 0;
-}
-
 
 /*
  * read the AVR device's signature bytes
  */
-int avr_signature(int fd, AVRPART * p)
+int avr_signature(PROGRAMMER * pgm, AVRPART * p)
 {
   int rc;
 
-  rc = avr_read(fd, p, "signature", 0, 0);
+  rc = avr_read(pgm, p, "signature", 0, 0);
   if (rc < 0) {
     fprintf(stderr, 
             "%s: error reading signature data for part \"%s\", rc=%d\n",
@@ -888,78 +753,6 @@ int avr_signature(int fd, AVRPART * p)
 }
 
 
-/*
- * apply power to the AVR processor
- */
-void avr_powerup(int fd)
-{
-  ppi_set(fd, PPIDATA, pgm->pinno[PPI_AVR_VCC]);    /* power up */
-  usleep(100000);
-}
-
-
-/*
- * remove power from the AVR processor
- */
-void avr_powerdown(int fd)
-{
-  ppi_clr(fd, PPIDATA, pgm->pinno[PPI_AVR_VCC]);    /* power down */
-}
-
-
-/*
- * initialize the AVR device and prepare it to accept commands
- */
-int avr_initialize(int fd, AVRPART * p)
-{
-  int rc;
-  int tries;
-
-  avr_powerup(fd);
-  usleep(20000);
-
-  ppi_setpin(fd, pgm->pinno[PIN_AVR_SCK], 0);
-  ppi_setpin(fd, pgm->pinno[PIN_AVR_RESET], 0);
-  usleep(20000);
-
-  ppi_pulsepin(fd, pgm->pinno[PIN_AVR_RESET]);
-
-  usleep(20000); /* 20 ms XXX should be a per-chip parameter */
-
-  /*
-   * Enable programming mode.  If we are programming an AT90S1200, we
-   * can only issue the command and hope it worked.  If we are using
-   * one of the other chips, the chip will echo 0x53 when issuing the
-   * third byte of the command.  In this case, try up to 32 times in
-   * order to possibly get back into sync with the chip if we are out
-   * of sync.
-   */
-  if (strcmp(p->desc, "AT90S1200")==0) {
-    avr_program_enable(fd, p);
-  }
-  else {
-    tries = 0;
-    do {
-      rc = avr_program_enable(fd, p);
-      if ((rc == 0)||(rc == -1))
-        break;
-      ppi_pulsepin(fd, pgm->pinno[PIN_AVR_SCK]);
-      tries++;
-    } while (tries < 65);
-
-    /*
-     * can't sync with the device, maybe it's not attached?
-     */
-    if (rc) {
-      fprintf(stderr, "%s: AVR device not responding\n", progname);
-      return -1;
-    }
-  }
-
-  return 0;
-}
-
-
 /*
  * Allocate and initialize memory buffers for each of the device's
  * defined memory regions.  
@@ -1043,7 +836,7 @@ int avr_verify(AVRPART * p, AVRPART * v, char * memtype, int size)
 }
 
 
-int avr_get_cycle_count(int fd, AVRPART * p, int * cycles)
+int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles)
 {
   AVRMEM * a;
   int cycle_count;
@@ -1055,28 +848,28 @@ int avr_get_cycle_count(int fd, AVRPART * p, int * cycles)
     return -1;
   }
 
-  rc = avr_read_byte(fd, p, a, a->size-4, &v1);
+  rc = avr_read_byte(pgm, p, a, a->size-4, &v1);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't read memory for cycle count, rc=%d\n",
             progname, rc);
     return -1;
   }
 
-  rc = avr_read_byte(fd, p, a, a->size-3, &v2);
+  rc = avr_read_byte(pgm, p, a, a->size-3, &v2);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't read memory for cycle count, rc=%d\n",
             progname, rc);
     return -1;
   }
 
-  rc = avr_read_byte(fd, p, a, a->size-2, &v3);
+  rc = avr_read_byte(pgm, p, a, a->size-2, &v3);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't read memory for cycle count, rc=%d\n",
             progname, rc);
     return -1;
   }
 
-  rc = avr_read_byte(fd, p, a, a->size-1, &v4);
+  rc = avr_read_byte(pgm, p, a, a->size-1, &v4);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't read memory for cycle count, rc=%d\n",
             progname, rc);
@@ -1099,7 +892,7 @@ int avr_get_cycle_count(int fd, AVRPART * p, int * cycles)
 }
 
 
-int avr_put_cycle_count(int fd, AVRPART * p, int cycles)
+int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles)
 {
   AVRMEM * a;
   unsigned char v1, v2, v3, v4;
@@ -1115,25 +908,25 @@ int avr_put_cycle_count(int fd, AVRPART * p, int cycles)
   v2 = (cycles & 0x0ff0000) >> 16;
   v1 = (cycles & 0x0ff000000) >> 24;
 
-  rc = avr_write_byte(fd, p, a, a->size-4, v1);
+  rc = avr_write_byte(pgm, p, a, a->size-4, v1);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't write memory for cycle count, rc=%d\n",
             progname, rc);
     return -1;
   }
-  rc = avr_write_byte(fd, p, a, a->size-3, v2);
+  rc = avr_write_byte(pgm, p, a, a->size-3, v2);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't write memory for cycle count, rc=%d\n",
             progname, rc);
     return -1;
   }
-  rc = avr_write_byte(fd, p, a, a->size-2, v3);
+  rc = avr_write_byte(pgm, p, a, a->size-2, v3);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't write memory for cycle count, rc=%d\n",
             progname, rc);
     return -1;
   }
-  rc = avr_write_byte(fd, p, a, a->size-1, v4);
+  rc = avr_write_byte(pgm, p, a, a->size-1, v4);
   if (rc < 0) {
     fprintf(stderr, "%s: WARNING: can't write memory for cycle count, rc=%d\n",
             progname, rc);
diff --git a/avr.h b/avr.h
index 489439e2..c48f1cfa 100644
--- a/avr.h
+++ b/avr.h
@@ -34,48 +34,9 @@
 
 #include <stdio.h>
 
+#include "avrpart.h"
 #include "lists.h"
-
-
-/*
- * AVR serial programming instructions
- */
-enum {
-  AVR_OP_READ,
-  AVR_OP_WRITE,
-  AVR_OP_READ_LO,
-  AVR_OP_READ_HI,
-  AVR_OP_WRITE_LO,
-  AVR_OP_WRITE_HI,
-  AVR_OP_LOADPAGE_LO,
-  AVR_OP_LOADPAGE_HI,
-  AVR_OP_WRITEPAGE,
-  AVR_OP_CHIP_ERASE,
-  AVR_OP_PGM_ENABLE,
-  AVR_OP_MAX
-};
-
-
-enum {
-  AVR_CMDBIT_IGNORE,  /* bit is ignored on input and output */
-  AVR_CMDBIT_VALUE,   /* bit is set to 0 or 1 for input or output */
-  AVR_CMDBIT_ADDRESS, /* this bit represents an input address bit */
-  AVR_CMDBIT_INPUT,   /* this bit is an input bit */
-  AVR_CMDBIT_OUTPUT   /* this bit is an output bit */
-};
-
-/*
- * serial programming instruction bit specifications
- */
-typedef struct cmdbit {
-  int          type;  /* AVR_CMDBIT_* */
-  int          bitno; /* which input bit to use for this command bit */
-  int          value; /* bit value if type == AVR_CMDBIT_VALUD */
-} CMDBIT;
-
-typedef struct opcode {
-  CMDBIT        bit[32]; /* opcode bit specs */
-} OPCODE;
+#include "pgm.h"
 
 
 #define AVR_MEMDESCLEN 64
@@ -97,18 +58,6 @@ typedef struct avrmem {
 } AVRMEM;
 
 
-#define AVR_DESCLEN 64
-#define AVR_IDLEN   32
-typedef struct avrpart {
-  char          desc[AVR_DESCLEN];  /* long part name */
-  char          id[AVR_IDLEN];      /* short part name */
-  int           chip_erase_delay;   /* microseconds */
-  OPCODE      * op[AVR_OP_MAX];     /* opcodes */
-
-  LISTID        mem;                /* avr memory definitions */
-} AVRPART;
-
-
 extern struct avrpart parts[];
 
 
@@ -129,32 +78,24 @@ int avr_txrx_bit(int fd, int bit);
 
 unsigned char avr_txrx(int fd, unsigned char byte);
 
-int avr_cmd(int fd, unsigned char cmd[4], unsigned char res[4]);
+int avr_set_bits(OPCODE * op, unsigned char * cmd);
 
-int avr_read_byte(int fd, AVRPART * p, AVRMEM * mem, unsigned long addr, 
-                  unsigned char * value);
+int avr_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, 
+                  unsigned long addr, unsigned char * value);
 
-int avr_read(int fd, AVRPART * p, char * memtype, int size, int verbose);
+int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, 
+             int verbose);
 
-int avr_write_page(int fd, AVRPART * p, AVRMEM * mem,
+int avr_write_page(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
                    unsigned long addr);
 
-int avr_write_byte(int fd, AVRPART * p, AVRMEM * mem,
+int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
                    unsigned long addr, unsigned char data);
 
-int avr_write(int fd, AVRPART * p, char * memtype, int size, int verbose);
+int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, 
+              int verbose);
 
-int avr_program_enable(int fd, AVRPART * p);
-
-int avr_chip_erase(int fd, AVRPART * p);
-
-int avr_signature(int fd, AVRPART * p);
-
-void avr_powerup(int fd);
-
-void avr_powerdown(int fd);
-
-int avr_initialize(int fd, AVRPART * p);
+int avr_signature(PROGRAMMER * pgm, AVRPART * p);
 
 char * avr_memtstr(int memtype);
 
@@ -167,8 +108,8 @@ void avr_mem_display(char * prefix, FILE * f, AVRMEM * m, int type,
 
 void avr_display(FILE * f, AVRPART * p, char * prefix, int verbose);
 
-int avr_get_cycle_count(int fd, AVRPART * p, int * cycles);
+int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles);
 
-int avr_put_cycle_count(int fd, AVRPART * p, int cycles);
+int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles);;
 
 #endif
diff --git a/avrdude.conf.sample b/avrdude.conf.sample
index 3ac84609..5ff61e08 100644
--- a/avrdude.conf.sample
+++ b/avrdude.conf.sample
@@ -110,6 +110,7 @@
 programmer
   id    = "bsd", "default";
   desc  = "Brian Dean's Programmer";
+  type  = ppi;
   vcc   = 2, 3, 4, 5;
   reset = 7;
   sck   = 8;
@@ -120,6 +121,7 @@ programmer
 programmer
   id    = "stk200";
   desc  = "STK200";
+  type  = ppi;
   buff  = 4, 5;
   sck   = 6;
   mosi  = 7;
@@ -130,6 +132,7 @@ programmer
 programmer
   id    = "dt006";
   desc  = "Dontronics DT006";
+  type  = ppi;
   reset = 4;
   sck   = 5;
   mosi  = 2;
@@ -139,6 +142,7 @@ programmer
 programmer
   id     = "alf";
   desc   = "Tony Friebel's Programmer";
+  type   = ppi;
   vcc    = 2, 3, 4, 5;
   buff   = 6;
   reset  = 7;
diff --git a/config.c b/config.c
index adca8159..aeaf307f 100644
--- a/config.c
+++ b/config.c
@@ -274,29 +274,6 @@ void pyytext(void)
 }
 
 
-PROGRAMMER * new_programmer(void)
-{
-  PROGRAMMER * p;
-  int i;
-
-  p = (PROGRAMMER *)malloc(sizeof(PROGRAMMER));
-  if (p == NULL) {
-    fprintf(stderr, "new_programmer(): out of memory\n");
-    exit(1);
-  }
-
-  memset(p, 0, sizeof(*p));
-
-  p->id = lcreat(NULL, 0);
-  p->desc[0] = 0;
-
-  for (i=0; i<N_PINS; i++)
-    p->pinno[i] = 0;
-
-  return p;
-}
-
-
 char * dup_string(char * str)
 {
   char * s;
diff --git a/config.h b/config.h
index 8b39b95f..544a1677 100644
--- a/config.h
+++ b/config.h
@@ -53,13 +53,6 @@ typedef struct token_t {
 } TOKEN;
 
 
-#define PGM_DESCLEN 80
-typedef struct programmer_t {
-  LISTID id;
-  char desc[PGM_DESCLEN];
-  unsigned int pinno[N_PINS];
-} PROGRAMMER;
-
 extern FILE       * yyin;
 extern PROGRAMMER * current_prog;
 extern AVRPART    * current_part;
diff --git a/config_gram.y b/config_gram.y
index 1b0a1d7f..d800c932 100644
--- a/config_gram.y
+++ b/config_gram.y
@@ -60,8 +60,9 @@
 %token K_NUM_PAGES
 %token K_PART
 %token K_PGMLED
-%token K_PWROFF_AFTER_WRITE
+%token K_PPI
 %token K_PROGRAMMER
+%token K_PWROFF_AFTER_WRITE
 %token K_RDYLED
 %token K_READBACK_P1
 %token K_READBACK_P2
@@ -69,6 +70,8 @@
 %token K_RESET
 %token K_SCK
 %token K_SIZE
+%token K_STK500
+%token K_TYPE
 %token K_VCC
 %token K_VFYLED
 %token K_WRITEPAGE
@@ -102,7 +105,7 @@ def :
 
 prog_def :
   K_PROGRAMMER 
-    { current_prog = new_programmer(); }
+    { current_prog = pgm_new(); }
     prog_parms
     { 
       if (lsize(current_prog->id) == 0) {
@@ -111,6 +114,11 @@ prog_def :
                 progname, infile, lineno);
         exit(1);
       }
+      if (current_prog->type[0] == 0) {
+        fprintf(stderr, "%s: error at %s:%d: programmer type not specified\n",
+                progname, infile, lineno);
+        exit(1);
+      }
       ladd(programmers, current_prog); 
       current_prog = NULL; 
     }
@@ -205,6 +213,18 @@ prog_parm :
     }
   } |
 
+  K_TYPE TKN_EQUAL K_PPI {
+    { 
+      ppi_initpgm(current_prog);
+    }
+  } |
+
+  K_TYPE TKN_EQUAL K_STK500 {
+    { 
+      fprintf(stderr, "%s: programmer 'stk500' not yet supported\n", progname);
+    }
+  } |
+
   K_DESC TKN_EQUAL TKN_STRING {
     strncpy(current_prog->desc, $3->value.string, PGM_DESCLEN);
     current_prog->desc[PGM_DESCLEN-1] = 0;
@@ -441,6 +461,7 @@ mem_spec :
 #include "config.h"
 #include "lists.h"
 #include "pindefs.h"
+#include "pgm.h"
 #include "avr.h"
 
 extern char * progname;
diff --git a/lexer.l b/lexer.l
index 6d81fbb5..5acf8b81 100644
--- a/lexer.l
+++ b/lexer.l
@@ -142,14 +142,17 @@ page_size        { yylval=NULL; return K_PAGE_SIZE; }
 paged            { yylval=NULL; return K_PAGED; }
 part             { yylval=NULL; return K_PART; }
 pgmled           { yylval=NULL; return K_PGMLED; }
-pwroff_after_write { yylval=NULL; return K_PWROFF_AFTER_WRITE; }
+ppi              { yylval=NULL; return K_PPI; }
 programmer       { yylval=NULL; return K_PROGRAMMER; }
+pwroff_after_write { yylval=NULL; return K_PWROFF_AFTER_WRITE; }
 rdyled           { yylval=NULL; return K_RDYLED; }
 readback_p1      { yylval=NULL; return K_READBACK_P1; }
 readback_p2      { yylval=NULL; return K_READBACK_P2; }
 reset            { yylval=NULL; return K_RESET; }
 sck              { yylval=NULL; return K_SCK; }
 size             { yylval=NULL; return K_SIZE; }
+stk500           { yylval=NULL; return K_STK500; }
+type             { yylval=NULL; return K_TYPE; }
 vcc              { yylval=NULL; return K_VCC; }
 vfyled           { yylval=NULL; return K_VFYLED; }
 
diff --git a/main.c b/main.c
index 4e15d7fd..23c83b39 100644
--- a/main.c
+++ b/main.c
@@ -94,16 +94,18 @@
 
 
 #define DEFAULT_PARALLEL "/dev/ppi0"
+#define DEFAULT_SERIAL   "/dev/cuaa0"
 
 extern char * avr_version;
 extern char * config_version;
 extern char * fileio_version;
 extern char * lists_version;
 extern char * main_version;
+extern char * pgm_version;
 extern char * ppi_version;
 extern char * term_version;
 
-#define N_MODULES 7
+#define N_MODULES 8
 
 char ** modules[N_MODULES] = { 
   &avr_version,
@@ -111,6 +113,7 @@ char ** modules[N_MODULES] = {
   &fileio_version,
   &lists_version,
   &main_version, 
+  &pgm_version, 
   &ppi_version, 
   &term_version 
 };
@@ -126,7 +129,7 @@ char   progbuf[PATH_MAX]; /* temporary buffer of spaces the same
 
 PROGRAMMER * pgm = NULL;
 
-PROGRAMMER compiled_in_pgm;
+PROGRAMMER * compiled_in_pgm;
 
 /*
  * global options
@@ -142,7 +145,7 @@ void usage(void)
   fprintf(stderr,
           "\nUsage: %s -p partno [-e] [-E exitspec[,exitspec]] [-f format] "
           "[-F]\n"
-          "      %s[-i filename] [-m memtype] [-o filename] [-P parallel] "
+          "      %s[-i filename] [-m memtype] [-o filename] [-P port] "
           "[-t]\n"
           "      %s[-c programmer] [-C config-file] [-v [-v]] [-n]\n\n",
           progname, progbuf, progbuf);
@@ -306,75 +309,11 @@ int read_config(char * file)
 
 
 
-static char vccpins_buf[64];
-char * vccpins_str(unsigned int pmask)
-{
-  unsigned int mask;
-  int pin;
-  char b2[8];
-  char * b;
-
-  b = vccpins_buf;
-
-  b[0] = 0;
-  for (pin = 2, mask = 1; mask < 0x80; mask = mask << 1, pin++) {
-    if (pmask & mask) {
-      sprintf(b2, "%d", pin);
-      if (b[0] != 0)
-        strcat(b, ",");
-      strcat(b, b2);
-    }
-  }
-
-  return b;
-}
-
-
 void pinconfig_display(char * p)
 {
-  char vccpins[64];
-  char buffpins[64];
+  fprintf(stderr, "%sProgrammer Type: %s\n", p, pgm->type);
 
-  if (pgm->pinno[PPI_AVR_VCC]) {
-    snprintf(vccpins, sizeof(vccpins), " = pins %s", 
-             vccpins_str(pgm->pinno[PPI_AVR_VCC]));
-  }
-  else {
-    strcpy(vccpins, " (not used)");
-  }
-
-  if (pgm->pinno[PPI_AVR_BUFF]) {
-    snprintf(buffpins, sizeof(buffpins), " = pins %s", 
-             vccpins_str(pgm->pinno[PPI_AVR_BUFF]));
-  }
-  else {
-    strcpy(buffpins, " (not used)");
-  }
-
-  fprintf(stderr, "%sProgrammer Pin Configuration: %s (%s)\n", p, 
-          (char *)ldata(lfirst(pgm->id)), pgm->desc);
-
-  fprintf(stderr, 
-          "%s  VCC     = 0x%02x%s\n"
-          "%s  BUFF    = 0x%02x%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, pgm->pinno[PPI_AVR_VCC], vccpins,
-          p, pgm->pinno[PPI_AVR_BUFF], 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]);
+  pgm->display(pgm, p);
 }
 
 
@@ -457,7 +396,6 @@ void list_parts(FILE * f, char * prefix, LISTID parts)
  */
 int main(int argc, char * argv [])
 {
-  int              fd;          /* file descriptor for parallel port */
   int              rc;          /* general return code checking */
   int              exitrc;      /* exit code for main() */
   int              i;           /* general loop counter */
@@ -479,7 +417,7 @@ int main(int argc, char * argv [])
   char  * outputf;     /* output file name */
   char  * inputf;      /* input file name */
   int     ovsigck;     /* 1=override sig check, 0=don't */
-  char  * parallel;    /* parallel port device */
+  char  * port;        /* device port (/dev/xxx) */
   int     terminal;    /* 1=enter terminal mode, 0=don't */
   FILEFMT filefmt;     /* FMT_AUTO, FMT_IHEX, FMT_SREC, FMT_RBIN */
   int     nowrite;     /* don't actually write anything to the chip */
@@ -505,7 +443,7 @@ int main(int argc, char * argv [])
 
   partdesc      = NULL;
   readorwrite   = 0;
-  parallel      = DEFAULT_PARALLEL;
+  port          = DEFAULT_PARALLEL;
   outputf       = NULL;
   inputf        = NULL;
   doread        = 1;
@@ -535,12 +473,11 @@ int main(int argc, char * argv [])
   /*
    * initialize compiled-in default programmer 
    */
-  pgm = &compiled_in_pgm;
-  pgm->id = lcreat(NULL, 0);
+  compiled_in_pgm = pgm_new();
+  pgm = compiled_in_pgm;
+  ppi_initpgm(pgm);
   ladd(pgm->id, dup_string("avrprog"));
   strcpy(pgm->desc, "avrprog compiled-in default");
-  for (i=0; i<N_PINS; i++)
-    pgm->pinno[i] = 0;
   pgm->pinno[PPI_AVR_VCC]   = 0x0f;  /* ppi pins 2-5, data reg bits 0-3 */
   pgm->pinno[PPI_AVR_BUFF]  =  0;
   pgm->pinno[PIN_AVR_RESET] =  7;
@@ -674,7 +611,7 @@ int main(int argc, char * argv [])
         break;
 
       case 'P':
-        parallel = optarg;
+        port = optarg;
         break;
 
       case 'v':
@@ -739,7 +676,7 @@ int main(int argc, char * argv [])
     pgm = locate_pinconfig(programmers, "default");
     if (pgm == NULL) {
       /* no default config listed, use the compile-in default */
-      pgm = &compiled_in_pgm;
+      pgm = compiled_in_pgm;
     }
   }
   else {
@@ -776,7 +713,14 @@ int main(int argc, char * argv [])
 
 
   if (exitspecs != NULL) {
-    if (getexitspecs(exitspecs, &ppisetbits, &ppiclrbits) < 0) {
+    if (strcmp(pgm->type, "PPI") != 0) {
+      fprintf(stderr, 
+              "%s: WARNING: -E option is only valid with \"PPI\" "
+              "programmer types\n",
+              progname);
+      exitspecs = NULL;
+    }
+    else if (getexitspecs(exitspecs, &ppisetbits, &ppiclrbits) < 0) {
       usage();
       exit(1);
     }
@@ -799,70 +743,50 @@ int main(int argc, char * argv [])
 
   fprintf(stderr, "\n");
 
-  verify_pin_assigned(PIN_AVR_RESET, "AVR RESET");
-  verify_pin_assigned(PIN_AVR_SCK,   "AVR SCK");
-  verify_pin_assigned(PIN_AVR_MISO,  "AVR MISO");
-  verify_pin_assigned(PIN_AVR_MOSI,  "AVR MOSI");
+  if (strcmp(pgm->type, "PPI") == 0) {
+    verify_pin_assigned(PIN_AVR_RESET, "AVR RESET");
+    verify_pin_assigned(PIN_AVR_SCK,   "AVR SCK");
+    verify_pin_assigned(PIN_AVR_MISO,  "AVR MISO");
+    verify_pin_assigned(PIN_AVR_MOSI,  "AVR MOSI");
+  }
 
   /*
-   * open the parallel port
+   * open the programmer
    */
-  fd = open(parallel, O_RDWR);
-  if (fd < 0) {
-    fprintf(stderr, "%s: can't open device \"%s\": %s\n\n",
-              progname, parallel, strerror(errno));
-    return 1;
-  }
+  pgm->open(pgm, port);
 
   exitrc = 0;
 
-#if 0
-  ppi_sense(fd);
-#endif
-
-  ppidata = ppi_getall(fd, PPIDATA);
-  if (ppidata < 0) {
-    fprintf(stderr, "%s: error reading status of ppi data port\n", progname);
+  rc = pgm->save(pgm);
+  if (rc < 0) {
     exitrc = 1;
     ppidata = 0; /* clear all bits at exit */
     goto main_exit;
   }
 
-  ppidata &= ~ppiclrbits;
-  ppidata |= ppisetbits;
+  if (strcmp(pgm->type, "PPI") == 0) {
+    pgm->ppidata &= ~ppiclrbits;
+    pgm->ppidata |= ppisetbits;
+  }
 
-  /* 
+
+  /*
+   * enable the programmer
+   */
+  pgm->enable(pgm);
+
+  /*
    * turn off all the status leds
    */
-  LED_OFF(fd, pgm->pinno[PIN_LED_RDY]);
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
-  LED_OFF(fd, pgm->pinno[PIN_LED_PGM]);
-  LED_OFF(fd, pgm->pinno[PIN_LED_VFY]);
-
-
-  /*
-   * Prepare to start talking to the connected device - pull reset low
-   * first, delay a few milliseconds, then enable the buffer.  This
-   * sequence allows the AVR to be reset before the buffer is enabled
-   * to avoid a short period of time where the AVR may be driving the
-   * programming lines at the same time the programmer tries to.  Of
-   * course, if a buffer is being used, then the /RESET line from the
-   * programmer needs to be directly connected to the AVR /RESET line
-   * and not via the buffer chip.
-   */
-
-  ppi_setpin(fd, pgm->pinno[PIN_AVR_RESET], 0);
-  usleep(1);
-
-  /*
-   * enable the 74367 buffer, if connected; this signal is active low
-   */
-  ppi_clr(fd, PPIDATA, pgm->pinno[PPI_AVR_BUFF]);
+  pgm->rdy_led(pgm, OFF);
+  pgm->err_led(pgm, OFF);
+  pgm->pgm_led(pgm, OFF);
+  pgm->vfy_led(pgm, OFF);
 
   /*
    * initialize the chip in preperation for accepting commands
    */
-  rc = avr_initialize(fd,p);
+  rc = pgm->initialize(pgm, p);
   if (rc < 0) {
     fprintf(stderr, "%s: initialization failed, rc=%d\n", progname, rc);
     exitrc = 1;
@@ -870,7 +794,7 @@ int main(int argc, char * argv [])
   }
 
   /* indicate ready */
-  LED_ON(fd, pgm->pinno[PIN_LED_RDY]);
+  pgm->rdy_led(pgm, ON);
 
   fprintf(stderr, 
             "%s: AVR device initialized and ready to accept instructions\n",
@@ -882,7 +806,7 @@ int main(int argc, char * argv [])
    * against 0xffffffff should ensure that the signature bytes are
    * valid.  
    */
-  rc = avr_signature(fd, p);
+  rc = avr_signature(pgm, p);
   if (rc != 0) {
     fprintf(stderr, "%s: error reading signature data, rc=%d\n",
             progname, rc);
@@ -923,7 +847,7 @@ int main(int argc, char * argv [])
   }
 
   if (set_cycles != -1) {
-    rc = avr_get_cycle_count(fd, p, &cycles);
+    rc = avr_get_cycle_count(pgm, p, &cycles);
     if (rc == 0) {
       /*
        * only attempt to update the cycle counter if we can actually
@@ -932,7 +856,7 @@ int main(int argc, char * argv [])
       cycles = set_cycles;
       fprintf(stderr, "%s: setting erase-rewrite cycle count to %d\n", 
               progname, cycles);
-      rc = avr_put_cycle_count(fd, p, cycles);
+      rc = avr_put_cycle_count(pgm, p, cycles);
       if (rc < 0) {
         fprintf(stderr, 
                 "%s: WARNING: failed to update the erase-rewrite cycle "
@@ -948,7 +872,7 @@ int main(int argc, char * argv [])
      * before the chip can accept new programming
      */
     fprintf(stderr, "%s: erasing chip\n", progname);
-    avr_chip_erase(fd,p);
+    pgm->chip_erase(pgm, p);
     fprintf(stderr, "%s: done.\n", progname);
   }
   else if (set_cycles == -1) {
@@ -960,7 +884,7 @@ int main(int argc, char * argv [])
      * see if the cycle count in the last four bytes of eeprom seems
      * reasonable 
      */
-    rc = avr_get_cycle_count(fd, p, &cycles);
+    rc = avr_get_cycle_count(pgm, p, &cycles);
     if ((rc >= 0) && (cycles != 0xffffffff)) {
       fprintf(stderr,
               "%s: current erase-rewrite cycle count is %d%s\n",
@@ -990,7 +914,7 @@ int main(int argc, char * argv [])
     /*
      * terminal mode
      */
-    exitrc = terminal_mode(fd, p);
+    exitrc = terminal_mode(pgm, p);
   }
   else if (doread) {
     /*
@@ -998,7 +922,7 @@ int main(int argc, char * argv [])
      */
     fprintf(stderr, "%s: reading %s memory:\n", 
             progname, memtype);
-    rc = avr_read(fd, p, memtype, 0, 1);
+    rc = avr_read(pgm, p, memtype, 0, 1);
     if (rc < 0) {
       fprintf(stderr, "%s: failed to read all of %s memory, rc=%d\n", 
               progname, memtype, rc);
@@ -1039,7 +963,7 @@ int main(int argc, char * argv [])
             progname, memtype, size);
 
     if (!nowrite) {
-      rc = avr_write(fd, p, memtype, size, 1);
+      rc = avr_write(pgm, p, memtype, size, 1);
     }
     else {
       /* 
@@ -1068,17 +992,17 @@ int main(int argc, char * argv [])
      * verify that the in memory file (p->mem[AVR_M_FLASH|AVR_M_EEPROM])
      * is the same as what is on the chip 
      */
-    LED_ON(fd, pgm->pinno[PIN_LED_VFY]);
+    pgm->vfy_led(pgm, ON);
 
     fprintf(stderr, "%s: verifying %s memory against %s:\n", 
             progname, memtype, inputf);
     fprintf(stderr, "%s: reading on-chip %s data:\n", 
             progname, memtype);
-    rc = avr_read(fd, v, memtype, vsize, 1);
+    rc = avr_read(pgm, v, memtype, vsize, 1);
     if (rc < 0) {
       fprintf(stderr, "%s: failed to read all of %s memory, rc=%d\n", 
               progname, memtype, rc);
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->err_led(pgm, ON);
       exitrc = 1;
       goto main_exit;
     }
@@ -1088,7 +1012,7 @@ int main(int argc, char * argv [])
     if (rc < 0) {
       fprintf(stderr, "%s: verification error; content mismatch\n", 
               progname);
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->err_led(pgm, ON);
       exitrc = 1;
       goto main_exit;
     }
@@ -1096,7 +1020,7 @@ int main(int argc, char * argv [])
     fprintf(stderr, "%s: %d bytes of %s verified\n", 
             progname, rc, memtype);
 
-    LED_OFF(fd, pgm->pinno[PIN_LED_VFY]);
+    pgm->vfy_led(pgm, OFF);
   }
 
 
@@ -1107,19 +1031,15 @@ int main(int argc, char * argv [])
    * program complete
    */
 
-  avr_powerdown(fd);
+  pgm->powerdown(pgm);
 
-  ppi_setall(fd, PPIDATA, ppidata);
+  pgm->restore(pgm);
 
-  /*
-   * disable the 74367 buffer, if connected; this signal is active low 
-   */
-  /* ppi_setpin(fd, pgm->pinno[PIN_AVR_BUFF], 1); */
-  ppi_set(fd, PPIDATA, pgm->pinno[PPI_AVR_BUFF]);
+  pgm->disable(pgm);
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_RDY]);
+  pgm->rdy_led(pgm, OFF);
 
-  close(fd);
+  pgm->close(pgm);
 
   fprintf(stderr, "\n%s done.  Thank you.\n\n", progname);
 
diff --git a/pgm.c b/pgm.c
new file mode 100644
index 00000000..5d466338
--- /dev/null
+++ b/pgm.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002  Brian S. Dean <bsd@bsdhome.com>
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRIAN S. DEAN ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BRIAN S. DEAN BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * 
+ */
+
+/* $Id$ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "pgm.h"
+
+extern char * progname;
+
+int  pgm_default_1 (struct programmer_t *, int);
+int  pgm_default_2 (struct programmer_t *, AVRPART *);
+int  pgm_default_3 (struct programmer_t *);
+void pgm_default_4 (struct programmer_t *);
+int  pgm_default_5 (struct programmer_t *, unsigned char cmd[4], 
+                    unsigned char res[4]);
+void pgm_default_6 (struct programmer_t *, char *);
+
+char * pgm_version = "$Id$";
+
+
+PROGRAMMER * pgm_new(void)
+{
+  int i;
+  PROGRAMMER * pgm;
+
+  pgm = (PROGRAMMER *)malloc(sizeof(*pgm));
+  if (pgm == NULL) {
+    fprintf(stderr, "%s: out of memory allocating programmer structure\n",
+            progname);
+    exit(1);
+  }
+
+  memset(pgm, 0, sizeof(*pgm));
+
+  pgm->id = lcreat(NULL, 0);
+  pgm->desc[0] = 0;
+  pgm->type[0] = 0;
+
+  for (i=0; i<N_PINS; i++)
+    pgm->pinno[i] = 0;
+
+  pgm->rdy_led        = pgm_default_1;
+  pgm->err_led        = pgm_default_1;
+  pgm->pgm_led        = pgm_default_1;
+  pgm->vfy_led        = pgm_default_1;
+  pgm->initialize     = pgm_default_2;
+  pgm->display        = pgm_default_6;
+  pgm->save           = pgm_default_3;
+  pgm->restore        = pgm_default_4;
+  pgm->enable         = pgm_default_4;
+  pgm->disable        = pgm_default_4;
+  pgm->powerup        = pgm_default_4;
+  pgm->powerdown      = pgm_default_4;
+  pgm->program_enable = pgm_default_2;
+  pgm->chip_erase     = pgm_default_2;
+  pgm->cmd            = pgm_default_5;
+  pgm->open           = pgm_default_6;
+  pgm->close          = pgm_default_4;
+
+  return pgm;
+}
+
+
+void pgm_default(void)
+{
+  fprintf(stderr, "%s: programmer operation not supported\n", progname);
+}
+
+
+int  pgm_default_1 (struct programmer_t * pgm, int value)
+{
+  pgm_default();
+  return -1;
+}
+
+int  pgm_default_2 (struct programmer_t * pgm, AVRPART * p)
+{
+  pgm_default();
+  return -1;
+}
+
+int  pgm_default_3 (struct programmer_t * pgm)
+{
+  pgm_default();
+  return -1;
+}
+
+void pgm_default_4 (struct programmer_t * pgm)
+{
+  pgm_default();
+}
+
+int  pgm_default_5 (struct programmer_t * pgm, unsigned char cmd[4], 
+                    unsigned char res[4])
+{
+  pgm_default();
+  return -1;
+}
+
+void pgm_default_6 (struct programmer_t * pgm, char * p)
+{
+  pgm_default();
+}
+
+
diff --git a/pgm.h b/pgm.h
new file mode 100644
index 00000000..9738c011
--- /dev/null
+++ b/pgm.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2000, 2001, 2002  Brian S. Dean <bsd@bsdhome.com>
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRIAN S. DEAN ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BRIAN S. DEAN BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * 
+ */
+
+/* $Id$ */
+
+#ifndef __pgm_h__
+#define __pgm_h__
+
+#include "avrpart.h"
+#include "lists.h"
+#include "pindefs.h"
+
+
+#define ON  1
+#define OFF 0
+
+#define PGM_DESCLEN 80
+#define PGM_TYPELEN 32
+typedef struct programmer_t {
+  LISTID id;
+  char desc[PGM_DESCLEN];
+  char type[PGM_TYPELEN];
+  unsigned int pinno[N_PINS];
+  int ppidata;
+  int fd;
+  int  (*rdy_led)        (struct programmer_t * pgm, int value);
+  int  (*err_led)        (struct programmer_t * pgm, int value);
+  int  (*pgm_led)        (struct programmer_t * pgm, int value);
+  int  (*vfy_led)        (struct programmer_t * pgm, int value);
+  int  (*initialize)     (struct programmer_t * pgm, AVRPART * p);
+  void (*display)        (struct programmer_t * pgm, char * p);
+  int  (*save)           (struct programmer_t * pgm);
+  void (*restore)        (struct programmer_t * pgm);
+  void (*enable)         (struct programmer_t * pgm);
+  void (*disable)        (struct programmer_t * pgm);
+  void (*powerup)        (struct programmer_t * pgm);
+  void (*powerdown)      (struct programmer_t * pgm);
+  int  (*program_enable) (struct programmer_t * pgm, AVRPART * p);
+  int  (*chip_erase)     (struct programmer_t * pgm, AVRPART * p);
+  int  (*cmd)            (struct programmer_t * pgm, unsigned char cmd[4], 
+                          unsigned char res[4]);
+  void (*open)           (struct programmer_t * pgm, char * port);
+  void (*close)          (struct programmer_t * pgm);
+} PROGRAMMER;
+
+
+PROGRAMMER * pgm_new(void);
+
+#endif
diff --git a/ppi.c b/ppi.c
index b4fbd77d..521f5e42 100644
--- a/ppi.c
+++ b/ppi.c
@@ -30,14 +30,21 @@
 /* $Id$ */
 
 #include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
 #include <dev/ppbus/ppi.h>
 
+#include "avr.h"
+#include "pindefs.h"
+#include "pgm.h"
 #include "ppi.h"
 
 #define SLOW_TOGGLE 0
 
 extern char * progname;
+extern int do_cycles;
 
 struct ppipins_t {
   int pin;
@@ -71,6 +78,32 @@ static struct ppipins_t pins[] = {
 
 char * ppi_version = "$Id$";
 
+
+
+static char vccpins_buf[64];
+char * vccpins_str(unsigned int pmask)
+{
+  unsigned int mask;
+  int pin;
+  char b2[8];
+  char * b;
+
+  b = vccpins_buf;
+
+  b[0] = 0;
+  for (pin = 2, mask = 1; mask < 0x80; mask = mask << 1, pin++) {
+    if (pmask & mask) {
+      sprintf(b2, "%d", pin);
+      if (b[0] != 0)
+        strcat(b, ",");
+      strcat(b, b2);
+    }
+  }
+
+  return b;
+}
+
+
 /*
  * set 'get' and 'set' appropriately for subsequent passage to ioctl()
  * to get/set the specified PPI registers.  
@@ -367,3 +400,383 @@ int ppi_sense(int fd)
 }
 
 
+
+
+/*
+ * transmit and receive a byte of data to/from the AVR device
+ */
+unsigned char ppi_txrx(PROGRAMMER * pgm, unsigned char byte)
+{
+  int i;
+  unsigned char r, b, rbyte;
+
+  rbyte = 0;
+  for (i=0; i<8; i++) {
+    b = (byte >> (7-i)) & 0x01;
+
+    /* 
+     * read the result bit (it is either valid from a previous clock
+     * pulse or it is ignored in the current context)
+     */
+    r = ppi_getpin(pgm->fd, pgm->pinno[PIN_AVR_MISO]);
+    
+    /* set the data input line as desired */
+    ppi_setpin(pgm->fd, pgm->pinno[PIN_AVR_MOSI], b);
+    
+    /* 
+     * pulse the clock line, clocking in the MOSI data, and clocking out
+     * the next result bit
+     */
+    ppi_pulsepin(pgm->fd, pgm->pinno[PIN_AVR_SCK]);
+
+    rbyte = rbyte | (r << (7-i));
+  }
+
+  return rbyte;
+}
+
+
+
+
+
+int ppi_rdy_led(PROGRAMMER * pgm, int value)
+{
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_LED_RDY], !value);
+  return 0;
+}
+
+int ppi_err_led(PROGRAMMER * pgm, int value)
+{
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_LED_ERR], !value);
+  return 0;
+}
+
+int ppi_pgm_led(PROGRAMMER * pgm, int value)
+{
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_LED_PGM], !value);
+  return 0;
+}
+
+int ppi_vfy_led(PROGRAMMER * pgm, int value)
+{
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_LED_VFY], !value);
+  return 0;
+}
+
+
+/*
+ * transmit an AVR device command and return the results; 'cmd' and
+ * 'res' must point to at least a 4 byte data buffer
+ */
+int ppi_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4])
+{
+  int i;
+
+  for (i=0; i<4; i++) {
+    res[i] = ppi_txrx(pgm, cmd[i]);
+  }
+
+#if 0
+  fprintf(stderr, "avr_cmd(): [ ");
+  for (i=0; i<4; i++)
+    fprintf(stderr, "%02x ", cmd[i]);
+  fprintf(stderr, "] [ ");
+  for (i=0; i<4; i++)
+    fprintf(stderr, "%02x ", res[i]);
+  fprintf(stderr, "]\n");
+#endif
+
+  return 0;
+}
+
+
+/*
+ * issue the 'chip erase' command to the AVR device
+ */
+int ppi_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char cmd[4];
+  unsigned char res[4];
+  int cycles;
+  int rc;
+
+  if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
+    fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
+            p->desc);
+    return -1;
+  }
+
+  rc = avr_get_cycle_count(pgm, p, &cycles);
+
+  /*
+   * only print out the current cycle count if we aren't going to
+   * display it below 
+   */
+  if (!do_cycles && ((rc >= 0) && (cycles != 0xffffffff))) {
+    fprintf(stderr,
+            "%s: current erase-rewrite cycle count is %d%s\n",
+            progname, cycles, 
+            do_cycles ? "" : " (if being tracked)");
+  }
+
+  pgm->pgm_led(pgm, ON);
+
+  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);
+
+  pgm->pgm_led(pgm, OFF);
+
+  if (do_cycles && (cycles != -1)) {
+    if (cycles == 0x00ffff) {
+      cycles = 0;
+    }
+    cycles++;
+    fprintf(stderr, "%s: erase-rewrite cycle count is now %d\n", 
+            progname, cycles);
+    avr_put_cycle_count(pgm, p, cycles);
+  }
+
+  return 0;
+}
+
+/*
+ * issue the 'program enable' command to the AVR device
+ */
+int ppi_program_enable(PROGRAMMER * pgm, AVRPART * p)
+{
+  unsigned char cmd[4];
+  unsigned char res[4];
+
+  if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
+    fprintf(stderr, "program enable instruction not defined for part \"%s\"\n",
+            p->desc);
+    return -1;
+  }
+
+  memset(cmd, 0, sizeof(cmd));
+  avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd);
+  pgm->cmd(pgm, cmd, res);
+
+  if (res[2] != cmd[1])
+    return -2;
+
+  return 0;
+}
+
+
+/*
+ * apply power to the AVR processor
+ */
+void ppi_powerup(PROGRAMMER * pgm)
+{
+  ppi_set(pgm->fd, PPIDATA, pgm->pinno[PPI_AVR_VCC]);    /* power up */
+  usleep(100000);
+}
+
+
+/*
+ * remove power from the AVR processor
+ */
+void ppi_powerdown(PROGRAMMER * pgm)
+{
+  ppi_clr(pgm->fd, PPIDATA, pgm->pinno[PPI_AVR_VCC]);    /* power down */
+}
+
+
+/*
+ * initialize the AVR device and prepare it to accept commands
+ */
+int ppi_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+  int rc;
+  int tries;
+
+  pgm->powerup(pgm);
+  usleep(20000);
+
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_AVR_SCK], 0);
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_AVR_RESET], 0);
+  usleep(20000);
+
+  ppi_pulsepin(pgm->fd, pgm->pinno[PIN_AVR_RESET]);
+
+  usleep(20000); /* 20 ms XXX should be a per-chip parameter */
+
+  /*
+   * Enable programming mode.  If we are programming an AT90S1200, we
+   * can only issue the command and hope it worked.  If we are using
+   * one of the other chips, the chip will echo 0x53 when issuing the
+   * third byte of the command.  In this case, try up to 32 times in
+   * order to possibly get back into sync with the chip if we are out
+   * of sync.
+   */
+  if (strcmp(p->desc, "AT90S1200")==0) {
+    pgm->program_enable(pgm, p);
+  }
+  else {
+    tries = 0;
+    do {
+      rc = pgm->program_enable(pgm, p);
+      if ((rc == 0)||(rc == -1))
+        break;
+      ppi_pulsepin(pgm->fd, pgm->pinno[PIN_AVR_SCK]);
+      tries++;
+    } while (tries < 65);
+
+    /*
+     * can't sync with the device, maybe it's not attached?
+     */
+    if (rc) {
+      fprintf(stderr, "%s: AVR device not responding\n", progname);
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+
+int ppi_save(PROGRAMMER * pgm)
+{
+  int rc;
+
+  rc = ppi_getall(pgm->fd, PPIDATA);
+  if (rc < 0) {
+    fprintf(stderr, "%s: error reading status of ppi data port\n", progname);
+    return -1;
+  }
+
+  pgm->ppidata = rc;
+
+  return 0;
+}
+
+void ppi_restore(PROGRAMMER * pgm)
+{
+  ppi_setall(pgm->fd, PPIDATA, pgm->ppidata);
+}
+
+void ppi_disable(PROGRAMMER * pgm)
+{
+  ppi_set(pgm->fd, PPIDATA, pgm->pinno[PPI_AVR_BUFF]);
+}
+
+void ppi_enable(PROGRAMMER * pgm)
+{
+  /*
+   * Prepare to start talking to the connected device - pull reset low
+   * first, delay a few milliseconds, then enable the buffer.  This
+   * sequence allows the AVR to be reset before the buffer is enabled
+   * to avoid a short period of time where the AVR may be driving the
+   * programming lines at the same time the programmer tries to.  Of
+   * course, if a buffer is being used, then the /RESET line from the
+   * programmer needs to be directly connected to the AVR /RESET line
+   * and not via the buffer chip.
+   */
+
+  ppi_setpin(pgm->fd, pgm->pinno[PIN_AVR_RESET], 0);
+  usleep(1);
+
+  /*
+   * enable the 74367 buffer, if connected; this signal is active low
+   */
+  ppi_clr(pgm->fd, PPIDATA, pgm->pinno[PPI_AVR_BUFF]);
+}
+
+
+void ppi_open(PROGRAMMER * pgm, char * port)
+{
+  pgm->fd = open(port, O_RDWR);
+  if (pgm->fd < 0) {
+    fprintf(stderr, "%s: can't open device \"%s\": %s\n\n",
+              progname, port, strerror(errno));
+    exit(1);
+  }
+}
+
+
+void ppi_close(PROGRAMMER * pgm)
+{
+  close(pgm->fd);
+  pgm->fd = -1;
+}
+
+
+void ppi_display(PROGRAMMER * pgm, char * p)
+{
+  char vccpins[64];
+  char buffpins[64];
+
+  if (pgm->pinno[PPI_AVR_VCC]) {
+    snprintf(vccpins, sizeof(vccpins), " = pins %s", 
+             vccpins_str(pgm->pinno[PPI_AVR_VCC]));
+  }
+  else {
+    strcpy(vccpins, " (not used)");
+  }
+
+  if (pgm->pinno[PPI_AVR_BUFF]) {
+    snprintf(buffpins, sizeof(buffpins), " = pins %s", 
+             vccpins_str(pgm->pinno[PPI_AVR_BUFF]));
+  }
+  else {
+    strcpy(buffpins, " (not used)");
+  }
+
+
+  fprintf(stderr, "%sProgrammer Pin Configuration: %s (%s)\n", p, 
+          (char *)ldata(lfirst(pgm->id)), pgm->desc);
+
+  fprintf(stderr, 
+          "%s  VCC     = 0x%02x%s\n"
+          "%s  BUFF    = 0x%02x%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, pgm->pinno[PPI_AVR_VCC], vccpins,
+          p, pgm->pinno[PPI_AVR_BUFF], 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]);
+}
+
+
+void ppi_initpgm(PROGRAMMER * pgm)
+{
+  strcpy(pgm->type, "PPI");
+
+  pgm->rdy_led        = ppi_rdy_led;
+  pgm->err_led        = ppi_err_led;
+  pgm->pgm_led        = ppi_pgm_led;
+  pgm->vfy_led        = ppi_vfy_led;
+  pgm->initialize     = ppi_initialize;
+  pgm->display        = ppi_display;
+  pgm->save           = ppi_save;
+  pgm->restore        = ppi_restore;
+  pgm->enable         = ppi_enable;
+  pgm->disable        = ppi_disable;
+  pgm->powerup        = ppi_powerup;
+  pgm->powerdown      = ppi_powerdown;
+  pgm->program_enable = ppi_program_enable;
+  pgm->chip_erase     = ppi_chip_erase;
+  pgm->cmd            = ppi_cmd;
+  pgm->open           = ppi_open;
+  pgm->close          = ppi_close;
+}
+
+
diff --git a/ppi.h b/ppi.h
index 45ffc433..9b2a3bf8 100644
--- a/ppi.h
+++ b/ppi.h
@@ -32,6 +32,9 @@
 #ifndef __ppi_h__
 #define __ppi_h__
 
+#include "config.h"
+
+
 /*
  * PPI registers
  */
@@ -69,6 +72,47 @@ int ppi_getpinreg (int pin);
 
 int ppi_sense     (int fd);
 
+
+void ppi_initpgm        (PROGRAMMER * pgm);
+
+
+int  ppi_rdy_led        (PROGRAMMER * pgm, int value);
+
+int  ppi_err_led        (PROGRAMMER * pgm, int value);
+
+int  ppi_pgm_led        (PROGRAMMER * pgm, int value);
+
+int  ppi_vfy_led        (PROGRAMMER * pgm, int value);
+
+int  ppi_cmd            (PROGRAMMER * pgm, unsigned char cmd[4], 
+                         unsigned char res[4]);
+
+int  ppi_chip_erase     (PROGRAMMER * pgm, AVRPART * p);
+
+int  ppi_program_enable (PROGRAMMER * pgm, AVRPART * p);
+
+void ppi_powerup        (PROGRAMMER * pgm);
+
+void ppi_powerdown      (PROGRAMMER * pgm);
+
+int  ppi_initialize     (PROGRAMMER * pgm, AVRPART * p);
+
+int  ppi_save           (PROGRAMMER * pgm);
+
+void ppi_restore        (PROGRAMMER * pgm);
+
+void ppi_disable        (PROGRAMMER * pgm);
+
+void ppi_enable         (PROGRAMMER * pgm);
+
+void ppi_open           (PROGRAMMER * pgm, char * port);
+
+void ppi_close          (PROGRAMMER * pgm);
+
+void ppi_initpgm        (PROGRAMMER * pgm);
+
+
+
 #endif
 
 
diff --git a/term.c b/term.c
index 9952fab7..4925329e 100644
--- a/term.c
+++ b/term.c
@@ -39,6 +39,7 @@
 #include "avr.h"
 #include "config.h"
 #include "lists.h"
+#include "pgm.h"
 #include "pindefs.h"
 #include "ppi.h"
 
@@ -50,28 +51,28 @@ extern PROGRAMMER * pgm;
 
 struct command {
   char * name;
-  int (*func)(int fd, struct avrpart * p, int argc, char *argv[]);
+  int (*func)(PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
   char * desc;
 };
 
 char * term_version = "$Id$";
 
 
-int cmd_dump  (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_dump  (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_write (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_write (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_erase (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_erase (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_sig   (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_sig   (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_part  (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_part  (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_help  (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_help  (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_quit  (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_quit  (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
-int cmd_send  (int fd, struct avrpart * p, int argc, char *argv[]);
+int cmd_send  (PROGRAMMER * pgm, struct avrpart * p, int argc, char *argv[]);
 
 
 struct command cmd[] = {
@@ -204,7 +205,7 @@ int hexdump_buf(FILE * f, int startaddr, unsigned char * buf, int len)
 }
 
 
-int cmd_dump(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_dump(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   static char prevmem[128] = {0};
   char * e;
@@ -280,7 +281,7 @@ int cmd_dump(int fd, struct avrpart * p, int argc, char * argv[])
   }
 
   for (i=0; i<len; i++) {
-    rc = avr_read_byte(fd, p, mem, addr+i, &buf[i]);
+    rc = avr_read_byte(pgm, p, mem, addr+i, &buf[i]);
     if (rc != 0) {
       fprintf(stderr, "error reading %s address 0x%05lx of part %s\n",
               mem->desc, addr+i, p->desc);
@@ -303,7 +304,7 @@ int cmd_dump(int fd, struct avrpart * p, int argc, char * argv[])
 }
 
 
-int cmd_write(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_write(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   char * e;
   int len, maxsize;
@@ -373,10 +374,10 @@ int cmd_write(int fd, struct avrpart * p, int argc, char * argv[])
     }
   }
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
+  pgm->err_led(pgm, OFF);
   for (werror=0, i=0; i<len; i++) {
 
-    rc = avr_write_byte(fd, p, mem, addr+i, buf[i]);
+    rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]);
     if (rc) {
       fprintf(stderr, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n",
               progname, buf[i], addr+i, rc);
@@ -387,7 +388,7 @@ int cmd_write(int fd, struct avrpart * p, int argc, char * argv[])
       werror = 1;
     }
 
-    rc = avr_read_byte(fd, p, mem, addr+i, &b);
+    rc = avr_read_byte(pgm, p, mem, addr+i, &b);
     if (b != buf[i]) {
       fprintf(stderr, 
               "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n",
@@ -396,7 +397,7 @@ int cmd_write(int fd, struct avrpart * p, int argc, char * argv[])
     }
 
     if (werror) {
-      LED_ON(fd, pgm->pinno[PIN_LED_ERR]);
+      pgm->err_led(pgm, ON);
     }
   }
 
@@ -408,7 +409,7 @@ int cmd_write(int fd, struct avrpart * p, int argc, char * argv[])
 }
 
 
-int cmd_send(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_send(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   unsigned char cmd[4], res[4];
   char * e;
@@ -433,9 +434,9 @@ int cmd_send(int fd, struct avrpart * p, int argc, char * argv[])
     }
   }
 
-  LED_OFF(fd, pgm->pinno[PIN_LED_ERR]);
+  pgm->err_led(pgm, OFF);
 
-  avr_cmd(fd, cmd, res);
+  pgm->cmd(pgm, cmd, res);
 
   /*
    * display results
@@ -451,15 +452,15 @@ int cmd_send(int fd, struct avrpart * p, int argc, char * argv[])
 }
 
 
-int cmd_erase(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_erase(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   fprintf(stderr, "%s: erasing chip\n", progname);
-  avr_chip_erase(fd,p);
+  pgm->chip_erase(pgm, p);
   return 0;
 }
 
 
-int cmd_part(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_part(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   fprintf(stdout, "\n");
   avr_display(stdout, p, "", 0);
@@ -469,13 +470,13 @@ int cmd_part(int fd, struct avrpart * p, int argc, char * argv[])
 }
 
 
-int cmd_sig(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_sig(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   int i;
   int rc;
   AVRMEM * m;
 
-  rc = avr_signature(fd, p);
+  rc = avr_signature(pgm, p);
   if (rc != 0) {
     fprintf(stderr, "error reading signature data, rc=%d\n",
             rc);
@@ -498,13 +499,13 @@ int cmd_sig(int fd, struct avrpart * p, int argc, char * argv[])
 }
 
 
-int cmd_quit(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_quit(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   return 1;
 }
 
 
-int cmd_help(int fd, struct avrpart * p, int argc, char * argv[])
+int cmd_help(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   int i;
 
@@ -599,7 +600,7 @@ int tokenize(char * s, char *** argv)
 }
 
 
-int do_cmd(int fd, struct avrpart * p, int argc, char * argv[])
+int do_cmd(PROGRAMMER * pgm, struct avrpart * p, int argc, char * argv[])
 {
   int i;
   int hold;
@@ -609,7 +610,7 @@ int do_cmd(int fd, struct avrpart * p, int argc, char * argv[])
   hold = -1;
   for (i=0; i<NCMDS; i++) {
     if (strcasecmp(argv[0], cmd[i].name) == 0) {
-      return cmd[i].func(fd, p, argc, argv);
+      return cmd[i].func(pgm, p, argc, argv);
     }
     else if (strncasecmp(argv[0], cmd[i].name, len)==0) {
       if (hold != -1) {
@@ -622,7 +623,7 @@ int do_cmd(int fd, struct avrpart * p, int argc, char * argv[])
   }
 
   if (hold != -1)
-    return cmd[hold].func(fd, p, argc, argv);
+    return cmd[hold].func(pgm, p, argc, argv);
 
   fprintf(stderr, "%s: invalid command \"%s\"\n",
           progname, argv[0]);
@@ -631,7 +632,7 @@ int do_cmd(int fd, struct avrpart * p, int argc, char * argv[])
 }
 
 
-int terminal_mode(int fd, struct avrpart * p)
+int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
 {
   char  * cmdbuf;
   int     i, len;
@@ -666,7 +667,7 @@ int terminal_mode(int fd, struct avrpart * p)
     fprintf(stdout, "\n");
 
     /* run the command */
-    rc = do_cmd(fd, p, argc, argv);
+    rc = do_cmd(pgm, p, argc, argv);
     free(argv);
     if (rc > 0) {
       rc = 0;
diff --git a/term.h b/term.h
index 1936708c..471d6900 100644
--- a/term.h
+++ b/term.h
@@ -33,7 +33,8 @@
 #define __term_h__
 
 #include "avr.h"
+#include "pgm.h"
 
-int terminal_mode(int fd, struct avrpart * p);
+int terminal_mode(PROGRAMMER * pgm, struct avrpart * p);
 
 #endif