From ed2b8342df46c1bbd3919fe3734cdde234d2d132 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 30 Aug 2022 02:08:15 +0100
Subject: [PATCH] Prepare for new components in avrdude.conf incl prog_modes

 - Add prog_modes to part and programmer definitions; prog_mode is a bitwise
   or of programming modes
     + PM_SPM: Bootloaders, self-programming with SPM/NVM Controllers
     + PM_TPI: t4, t5, t9, t10, t20, t40, t102, t104
     + PM_ISP: SPI programming for In-System Programming (typ classic parts)
     + PM_PDI: Program and Debug Interface (xmega parts)
     + PM_UPDI: Unified Program and Debug Interface
     + PM_HVSP: High Voltage Serial Programming (some classic parts)
     + PM_HVPP: High Voltage Parallel Programming (most non-HVSP classic parts)
     + PM_debugWIRE: Simpler alternative to JTAG (a subset of HVPP/HVSP parts)
     + PM_JTAG: some classic parts, some xmega
     + PM_aWire: AVR32 parts
 - Add mcuid, a unique id in 0..2039, to part definition for urclock programmer
 - Add n_interrupts, the number of interrupts, to part definition
 - Add n_page_erase to part definition (# of pages erased during NVM erase)

 - Implement a simple calculator in config_gram.y so numeric values can be
   expressed as simple expressions such as PM_SPM | PM_UPDI

 - Introduce a new method of assigning simple components to the grammar without
   touching config_gram.y via an eligible-component list in config.c; numeric
   expressions on the rhs of an assignment resolve to integer values

 - Update documentation in avrdude.conf.in and avrdude.texi
---
 src/avrdude.conf.in  |  60 +++++++++-----
 src/config.c         | 154 ++++++++++++++++++++++++++++++++++--
 src/config.h         |  59 ++++++++++++--
 src/config_gram.y    | 112 +++++++++++++++++++-------
 src/developer_opts.c |  39 +++++++++-
 src/doc/avrdude.texi | 181 +++++++++++++++++++++++++++----------------
 src/lexer.l          |  39 ++++++++--
 src/libavrdude.h     |  40 +++++++++-
 8 files changed, 542 insertions(+), 142 deletions(-)

diff --git a/src/avrdude.conf.in b/src/avrdude.conf.in
index 5a68cff5..e2a330ff 100644
--- a/src/avrdude.conf.in
+++ b/src/avrdude.conf.in
@@ -21,39 +21,56 @@
 #       id       = <id1> [, <id2> [, <id3>] ...] ;  # <idN> are quoted strings
 #       desc     = <description> ;                  # quoted string
 #       type     = <type>;                          # programmer type, quoted string
-#                          # supported programmer types can be listed by "-c ?type"
+#                                                   # supported types can be listed by "-c ?type"
+#       prog_modes = PM_<i/f> {| PM_<i/f>}          # interfaces, eg, PM_SPM|PM_PDI (1)
 #       connection_type = parallel | serial | usb | spi
 #       baudrate = <num> ;                          # baudrate for avr910-programmer
-#       vcc      = <num1> [, <num2> ... ] ;         # pin number(s)
-#       buff     = <num1> [, <num2> ... ] ;         # pin number(s)
-#       reset    = <num> ;                          # pin number
-#       sck      = <num> ;                          # pin number
-#       mosi     = <num> ;                          # pin number
-#       miso     = <num> ;                          # pin number
-#       errled   = <num> ;                          # pin number
-#       rdyled   = <num> ;                          # pin number
-#       pgmled   = <num> ;                          # pin number
-#       vfyled   = <num> ;                          # pin number
+#       vcc      = <pin1> [, <pin2> ... ] ;         # pin number(s)
+#       buff     = <pin1> [, <pin2> ... ] ;         # pin number(s)
+#       reset    = <pin> ;                          # pin number
+#       sck      = <pin> ;                          # pin number
+#       mosi     = <pin> ;                          # pin number
+#       miso     = <pin> ;                          # pin number
+#       errled   = <pin> ;                          # pin number
+#       rdyled   = <pin> ;                          # pin number
+#       pgmled   = <pin> ;                          # pin number
+#       vfyled   = <pin> ;                          # pin number
 #       usbvid   = <hexnum> ;                       # USB VID (Vendor ID)
-#       usbpid   = <hexnum> [, <hexnum> ...] ;      # USB PID (Product ID) (1)
+#       usbpid   = <hexnum> [, <hexnum> ...] ;      # USB PID (Product ID) (2)
 #       usbdev   = <interface> ;                    # USB interface or other device info
 #       usbvendor = <vendorname> ;                  # USB Vendor Name
 #       usbproduct = <productname> ;                # USB Product Name
 #       usbsn    = <serialno> ;                     # USB Serial Number
 #       hvupdi_support = <num> [, <num>, ... ] ;    # UPDI HV Variants Support
-#
-#        To invert a bit, use = ~ <num>, the spaces are important.
-#        For a pin list all pins must be inverted.
-#        A single pin can be specified as usual = ~ <num>, for lists
-#        specify it as follows = ~ ( <num> [, <num2> ... ] ) .
-#
-#        (1) Not all programmer types can process a list of PIDs.
 #   ;
 #
+#       # To invert a bit, use = ~ <num>, the spaces are important.
+#       # For a pin list all pins must be inverted.
+#       # A single pin can be specified as usual = ~ <num>, for lists
+#       # specify it as follows = ~ ( <num> [, <num2> ... ] ).
+#       #
+#       # (1) The following program modes are known:
+#       #  - PM_SPM: Bootloaders, self-programming with SPM opcodes or NVM Controllers
+#       #  - PM_TPI: Tiny Programming Interface (t4, t5, t9, t10, t20, t40, t102, t104)
+#       #  - PM_ISP: SPI programming for In-System Programming (almost all classic parts)
+#       #  - PM_PDI: Program and Debug Interface (xmega parts)
+#       #  - PM_UPDI: Unified Program and Debug Interface
+#       #  - PM_HVSP: High Voltage Serial Programming (some classic parts)
+#       #  - PM_HVPP: High Voltage Parallel Programming (most non-HVSP classic parts)
+#       #  - PM_debugWIRE: Simpler alternative to JTAG (a subset of HVPP/HVSP parts)
+#       #  - PM_JTAG: Joint Test Action Group standard (some classic parts, some xmega)
+#       #  - PM_aWire: AVR32 parts
+#       #
+#       # (2) Not all programmer types can process a list of PIDs
+#
 #   part
 #       desc             = <description> ;        # quoted string
 #       id               = <id> ;                 # quoted string
 #       family_id        = <id> ;                 # quoted string, eg, "megaAVR" or "tinyAVR"
+#       prog_modes       = PM_<i/f> {| PM_<i/f>}  # interfaces, eg, PM_SPM|PM_ISP|PM_HVPP|PM_debugWIRE
+#       mcuid            = <num>;                 # unique id in 0..2039 for urclock programmer
+#       n_interrupts     = <num>;                 # number of interrupts, used for vector bootloaders
+#       n_page_erase     = <num>;                 # if set, number of pages erased during NVM erase
 #       hvupdi_variant   = <num> ;                # numeric -1 (n/a) or 0..2
 #       devicecode       = <num> ;                # deprecated, use stk500_devcode
 #       stk500_devcode   = <num> ;                # numeric
@@ -63,8 +80,9 @@
 #       has_pdi          = <yes/no> ;             # part has PDI i/f
 #       has_updi         = <yes/no> ;             # part has UPDI i/f
 #       has_tpi          = <yes/no> ;             # part has TPI i/f
-#       is_at90s1200     = <yes/no> ;             # AT90S1200 part
 #       is_avr32         = <yes/no> ;             # AVR32 part
+#
+#       is_at90s1200     = <yes/no> ;             # AT90S1200 part
 #       signature        = <num> <num> <num> ;    # signature bytes
 #       usbpid           = <num> ;                # DFU USB PID
 #       chip_erase_delay = <num> ;                # micro-seconds
@@ -247,7 +265,7 @@
 # section avr061.zip which accompanies the application note
 # AVR061 available from:
 #
-#      http://www.atmel.com/dyn/resources/prod_documents/doc2525.pdf
+#      https://ww1.microchip.com/downloads/en/Appnotes/doc2525.pdf
 #
 
 #define ATTINY10    0x10  /* the _old_ one that never existed! */
diff --git a/src/config.c b/src/config.c
index d36e0814..531dac83 100644
--- a/src/config.c
+++ b/src/config.c
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stddef.h>
 #include <string.h>
 #include <ctype.h>
 
@@ -44,6 +45,7 @@ LISTID       number_list;
 PROGRAMMER * current_prog;
 AVRPART    * current_part;
 AVRMEM     * current_mem;
+int          current_strct;
 LISTID       part_list;
 LISTID       programmers;
 bool         is_alias;
@@ -53,6 +55,22 @@ char * cfg_infile;
 
 extern char * yytext;
 
+#define pgm_comp_desc(x, type)  { #x, COMP_PROGRAMMER, offsetof(PROGRAMMER, x), sizeof(((PROGRAMMER *) NULL)->x), type }
+#define part_comp_desc(x, type) { #x, COMP_AVRPART, offsetof(AVRPART, x), sizeof(((AVRPART *) NULL)->x), type }
+#define mem_comp_desc(x, type)  { #x, COMP_AVRMEM, offsetof(AVRMEM, x), sizeof(((AVRMEM *) NULL)->x), type }
+
+// Component description for config_gram.y, will be sorted appropriately on first use
+Component_t avr_comp[] = {
+  // PROGRAMMER
+  pgm_comp_desc(prog_modes, COMP_INT),
+
+  // AVRPART
+  part_comp_desc(prog_modes, COMP_INT),
+  part_comp_desc(mcuid, COMP_INT),
+  part_comp_desc(n_interrupts, COMP_INT),
+  part_comp_desc(n_page_erase, COMP_INT),
+};
+
 #define DEBUG 0
 
 void cleanup_config(void)
@@ -190,7 +208,7 @@ void free_tokens(int n, ...)
 
 
 
-TOKEN *number(const char *text) {
+TOKEN *new_number(const char *text) {
   struct token_t *tkn = new_token(TKN_NUMBER);
   tkn->value.type   = V_NUM;
   tkn->value.number = atoi(text);
@@ -202,7 +220,7 @@ TOKEN *number(const char *text) {
   return tkn;
 }
 
-TOKEN *number_real(const char *text) {
+TOKEN *new_number_real(const char *text) {
   struct token_t * tkn = new_token(TKN_NUMBER);
   tkn->value.type   = V_NUM_REAL;
   tkn->value.number_real = atof(text);
@@ -214,7 +232,7 @@ TOKEN *number_real(const char *text) {
   return tkn;
 }
 
-TOKEN *hexnumber(const char *text) {
+TOKEN *new_hexnumber(const char *text) {
   struct token_t *tkn = new_token(TKN_NUMBER);
   char * e;
 
@@ -233,11 +251,41 @@ TOKEN *hexnumber(const char *text) {
   return tkn;
 }
 
+TOKEN *new_constant(const char *con) {
+  struct token_t *tkn = new_token(TKN_NUMBER);
+  int assigned = 1;
 
-TOKEN *string(const char *text) {
+  tkn->value.type = V_NUM;
+  tkn->value.number =
+    !strcmp("PM_SPM", con)? PM_SPM:
+    !strcmp("PM_TPI", con)? PM_TPI:
+    !strcmp("PM_ISP", con)? PM_ISP:
+    !strcmp("PM_PDI", con)? PM_PDI:
+    !strcmp("PM_UPDI", con)? PM_UPDI:
+    !strcmp("PM_HVSP", con)? PM_HVSP:
+    !strcmp("PM_HVPP", con)? PM_HVPP:
+    !strcmp("PM_debugWIRE", con)? PM_debugWIRE:
+    !strcmp("PM_JTAG", con)? PM_JTAG:
+    !strcmp("PM_aWire", con)? PM_aWire:
+    (assigned = 0);
+
+  if(!assigned) {
+    yyerror("can't identify constant %s", con);
+    free_token(tkn);
+    return NULL;
+  }
+
+#if DEBUG
+  avrdude_message(MSG_INFO, "CONSTANT(%s=%d)\n", con, tkn->value.number);
+#endif
+
+  return tkn;
+}
+
+TOKEN *new_string(const char *text) {
   struct token_t *tkn = new_token(TKN_STRING);
   tkn->value.type   = V_STR;
-  tkn->value.string = cfg_strdup("string()", text);
+  tkn->value.string = cfg_strdup("new_string()", text);
 
 #if DEBUG
   avrdude_message(MSG_INFO, "STRING(%s)\n", tkn->value.string);
@@ -247,7 +295,7 @@ TOKEN *string(const char *text) {
 }
 
 
-TOKEN * keyword(int primary) {
+TOKEN *new_keyword(int primary) {
   return new_token(primary);
 }
 
@@ -692,3 +740,97 @@ char *cfg_escape(const char *s) {
 
   return cfg_strdup("cfg_escape()", buf);
 }
+
+
+static int cmp_comp(const void *v1, const void *v2) {
+  const Component_t *c1 = v1, *c2 = v2;
+  int ret = strcmp(c1->name, c2->name);
+
+  return ret? ret: c1->strct - c2->strct;
+}
+
+Component_t *cfg_comp_search(const char *name, int strct) {
+  static int init;
+  Component_t key;
+
+  if(!init++)
+    qsort(avr_comp, sizeof avr_comp/sizeof*avr_comp, sizeof(Component_t), cmp_comp);
+
+
+  key.name = name;
+  key.strct = strct;
+  return bsearch(&key, avr_comp, sizeof avr_comp/sizeof*avr_comp, sizeof(Component_t), cmp_comp);
+}
+
+
+const char *cfg_strct_name(int strct) {
+  switch(strct) {
+  case COMP_CONFIG_MAIN: return "avrdude.conf main";
+  case COMP_AVRPART: return "AVRPART";
+  case COMP_AVRMEM: return "AVRMEM";
+  case COMP_PROGRAMMER: return "PROGRAMMER";
+  }
+  return "unknown struct";
+}
+
+const char *cfg_v_type(int type) {
+  switch(type) {
+  case V_NONE: return "void";
+  case V_NUM: return "number";
+  case V_NUM_REAL: return "real";
+  case V_STR: return "string";
+  case V_COMPONENT: return "component";
+  }
+  return "unknown v type";
+}
+
+const char *cfg_comp_type(int type) {
+  switch(type) {
+  case COMP_INT: return "number";
+  case COMP_SHORT: return "short";
+  case COMP_CHAR: return "char";
+  case COMP_STRING: return "string";
+  case COMP_CHAR_ARRAY: return "byte array";
+  case COMP_INT_LISTID: return "number list";
+  case COMP_STRING_LISTID: return "string list";
+  case COMP_OPCODE: return "opcode";
+  case COMP_PIN: return "pin";
+  case COMP_PIN_LIST: return "pin list";
+  }
+  return "unknown comp type";
+}
+
+
+// Used by config_gram.y to assign a component in one of the relevant structures with a value
+void cfg_assign(char *sp, int strct, Component_t *cp, VALUE *v) {
+  const char *str;
+  int num;
+
+  switch(cp->type) {
+  case COMP_CHAR:
+  case COMP_SHORT:
+  case COMP_INT:
+    if(v->type != V_NUM) {
+      yywarning("%s in %s expects a %s but is assigned a %s",
+        cp->name, cfg_strct_name(strct), cfg_comp_type(cp->type), cfg_v_type(v->type));
+      return;
+    }
+    // TODO: consider endianess (code currently assumes little endian)
+    num = v->number;
+    memcpy(sp+cp->offset, &num, cp->size);
+    break;
+  case COMP_STRING:
+    if(v->type != V_STR) {
+      yywarning("%s in %s expects a string but is assigned a %s",
+        cp->name, cfg_strct_name(strct), cfg_v_type(v->type));
+      return;
+    }
+    str = cache_string(v->string);
+    memcpy(sp+cp->offset, &str, cp->size);
+    break;
+  // TODO: implement COMP_CHAR_ARRAY, COMP_INT_LISTID, COMP_STRING_LISTID, ...
+  default:
+    yywarning("%s in %s expects a %s but that is not implemented",
+      cp->name, cfg_strct_name(strct), cfg_comp_type(cp->type));
+  }
+}
diff --git a/src/config.h b/src/config.h
index b8bd2031..42b6219b 100644
--- a/src/config.h
+++ b/src/config.h
@@ -37,13 +37,48 @@ typedef struct {
 } COMMENT;
 
 
-enum { V_NONE, V_NUM, V_NUM_REAL, V_STR };
+enum {                          // Which structures a component can occur in
+  COMP_CONFIG_MAIN,
+  COMP_PROGRAMMER,
+  COMP_AVRPART,
+  COMP_AVRMEM,
+};
+
+enum {                          // Component types in structure
+  COMP_INT,
+  COMP_SHORT,
+  COMP_CHAR,
+  COMP_STRING,
+  COMP_CHAR_ARRAY,              // This and below are not yet implemented
+  COMP_INT_LISTID,
+  COMP_STRING_LISTID,
+  COMP_OPCODE,
+  COMP_PIN,                     // Pins may never be implemented
+  COMP_PIN_LIST
+};
+
+typedef struct {                // Description of a component in a structure
+  const char *name;             // Component name
+  int strct;                    // Structure, eg, COMP_AVRPART
+  int offset, size, type;       // Location, size and type within structure
+} Component_t;
+
+
+enum {                          // Value types for VALUE struct
+  V_NONE,
+  V_NUM,
+  V_NUM_REAL,
+  V_STR,
+  V_COMPONENT,
+};
+
 typedef struct value_t {
   int      type;
   union {
     int      number;
     double   number_real;
     char   * string;
+    Component_t *comp;
   };
 } VALUE;
 
@@ -59,6 +94,7 @@ extern FILE       * yyin;
 extern PROGRAMMER * current_prog;
 extern AVRPART    * current_part;
 extern AVRMEM     * current_mem;
+int                 current_strct;
 extern int          cfg_lineno;
 extern char       * cfg_infile;
 extern LISTID       string_list;
@@ -87,15 +123,17 @@ void free_token(TOKEN *tkn);
 
 void free_tokens(int n, ...);
 
-TOKEN *number(const char *text);
+TOKEN *new_number(const char *text);
 
-TOKEN *number_real(const char *text);
+TOKEN *new_number_real(const char *text);
 
-TOKEN *hexnumber(const char *text);
+TOKEN *new_hexnumber(const char *text);
 
-TOKEN *string(const char *text);
+TOKEN *new_constant(const char *text);
 
-TOKEN *keyword(int primary);
+TOKEN *new_string(const char *text);
+
+TOKEN *new_keyword(int primary);
 
 void print_token(TOKEN *tkn);
 
@@ -115,6 +153,15 @@ LISTID cfg_move_comments(void);
 
 void cfg_pop_comms(void);
 
+Component_t *cfg_comp_search(const char *name, int strct);
+
+const char *cfg_v_type(int type);
+
+const char *cfg_strct_name(int strct);
+
+void cfg_assign(char *sp, int strct, Component_t *cp, VALUE *v);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/config_gram.y b/src/config_gram.y
index dfdf50b5..bc48d098 100644
--- a/src/config_gram.y
+++ b/src/config_gram.y
@@ -208,12 +208,19 @@ static int pin_name;
 %token TKN_COMMA
 %token TKN_EQUAL
 %token TKN_SEMI
-%token TKN_TILDE
 %token TKN_LEFT_PAREN
 %token TKN_RIGHT_PAREN
 %token TKN_NUMBER
 %token TKN_NUMBER_REAL
 %token TKN_STRING
+%token TKN_COMPONENT
+
+%left  OP_OR                    /* calculator operations */
+%left  OP_XOR
+%left  OP_AND
+%left  OP_PLUS OP_MINUS
+%left  OP_TIMES OP_DIVIDE OP_MODULO
+%right OP_TILDE UNARY
 
 %start configuration
 
@@ -229,6 +236,27 @@ number_real :
   TKN_NUMBER_REAL {
     $$ = $1;
   }
+;
+
+
+expr: numexpr | TKN_STRING;
+
+numexpr:
+  TKN_NUMBER |
+  numexpr OP_OR numexpr     { $$ = $1; $$->value.number |= $3->value.number; } |
+  numexpr OP_XOR numexpr    { $$ = $1; $$->value.number ^= $3->value.number; } |
+  numexpr OP_AND numexpr    { $$ = $1; $$->value.number &= $3->value.number; } |
+  numexpr OP_PLUS numexpr   { $$ = $1; $$->value.number += $3->value.number; } |
+  numexpr OP_MINUS numexpr  { $$ = $1; $$->value.number -= $3->value.number; } |
+  numexpr OP_TIMES numexpr  { $$ = $1; $$->value.number *= $3->value.number; } |
+  numexpr OP_DIVIDE numexpr { $$ = $1; $$->value.number /= $3->value.number; } |
+  numexpr OP_MODULO numexpr { $$ = $1; $$->value.number %= $3->value.number; } |
+  OP_PLUS numexpr %prec UNARY  { $$ = $2; } |
+  OP_MINUS numexpr %prec UNARY { $$ = $2; $$->value.number = -$$->value.number; } |
+  OP_TILDE numexpr %prec UNARY { $$ = $2; $$->value.number = ~$$->value.number; } |
+  TKN_LEFT_PAREN numexpr TKN_RIGHT_PAREN { $$ = $2; }
+;
+
 
 configuration :
   /* empty */ | config
@@ -302,6 +330,7 @@ prog_def :
 //      pgm_fill_old_pins(current_prog); // TODO to be removed if old pin data no longer needed
 //      pgm_display_generic(current_prog, id);
       current_prog = NULL;
+      current_strct = COMP_CONFIG_MAIN;
     }
 ;
 
@@ -309,6 +338,7 @@ prog_def :
 prog_decl :
   K_PROGRAMMER
     { current_prog = pgm_new();
+      current_strct = COMP_PROGRAMMER;
       current_prog->config_file = cache_string(cfg_infile);
       current_prog->lineno = cfg_lineno;
     }
@@ -322,6 +352,7 @@ prog_decl :
         YYABORT;
       }
       current_prog = pgm_dup(pgm);
+      current_strct = COMP_PROGRAMMER;
       current_prog->parent_id = cache_string($3->value.string);
       current_prog->comments = NULL;
       current_prog->config_file = cache_string(cfg_infile);
@@ -389,6 +420,7 @@ part_def :
       current_part->comments = cfg_move_comments();
       LISTADD(part_list, current_part); 
       current_part = NULL; 
+      current_strct = COMP_CONFIG_MAIN;
     }
 ;
 
@@ -396,6 +428,7 @@ part_decl :
   K_PART
     {
       current_part = avr_new_part();
+      current_strct = COMP_AVRPART;
       current_part->config_file = cache_string(cfg_infile);
       current_part->lineno = cfg_lineno;
     } |
@@ -409,6 +442,7 @@ part_decl :
       }
 
       current_part = avr_dup_part(parent_part);
+      current_strct = COMP_AVRPART;
       current_part->parent_id = cache_string($3->value.string);
       current_part->comments = NULL;
       current_part->config_file = cache_string(cfg_infile);
@@ -435,6 +469,10 @@ prog_parms :
 ;
 
 prog_parm :
+  TKN_COMPONENT TKN_EQUAL expr {
+    cfg_assign((char *) current_prog, COMP_PROGRAMMER, $1->value.comp, &$3->value);
+    free_token($1);
+  } |
   K_ID TKN_EQUAL string_list {
     {
       while (lsize(string_list)) {
@@ -589,7 +627,7 @@ hvupdi_support_list:
 pin_number_non_empty:
   TKN_NUMBER { if(0 != assign_pin(pin_name, $1, 0)) YYABORT;  }
   |
-  TKN_TILDE TKN_NUMBER { if(0 != assign_pin(pin_name, $2, 1)) YYABORT; }
+  OP_TILDE TKN_NUMBER { if(0 != assign_pin(pin_name, $2, 1)) YYABORT; }
 ;
 
 pin_number:
@@ -601,7 +639,7 @@ pin_number:
 pin_list_element:
   pin_number_non_empty
   |
-  TKN_TILDE TKN_LEFT_PAREN num_list TKN_RIGHT_PAREN { if(0 != assign_pin_list(1)) YYABORT; }
+  OP_TILDE TKN_LEFT_PAREN num_list TKN_RIGHT_PAREN { if(0 != assign_pin_list(1)) YYABORT; }
 ;
 
 pin_list_non_empty:
@@ -665,6 +703,10 @@ retry_lines :
 ;
 
 part_parm :
+  TKN_COMPONENT TKN_EQUAL expr {
+    cfg_assign((char *) current_part, COMP_AVRPART, $1->value.comp, &$3->value);
+    free_token($1);
+  } |
   K_ID TKN_EQUAL TKN_STRING 
     {
       current_part->id = cache_string($3->value.string);
@@ -1075,51 +1117,61 @@ part_parm :
 
   K_HAS_JTAG TKN_EQUAL yesno
     {
-      if ($3->primary == K_YES)
+      if ($3->primary == K_YES) {
         current_part->flags |= AVRPART_HAS_JTAG;
-      else if ($3->primary == K_NO)
+        current_part->prog_modes |= PM_JTAG;
+      } else if ($3->primary == K_NO) {
         current_part->flags &= ~AVRPART_HAS_JTAG;
-
+        current_part->prog_modes &= ~PM_JTAG;
+      }
       free_token($3);
     } |
 
   K_HAS_DW TKN_EQUAL yesno
     {
-      if ($3->primary == K_YES)
+      if ($3->primary == K_YES) {
         current_part->flags |= AVRPART_HAS_DW;
-      else if ($3->primary == K_NO)
+        current_part->prog_modes |= PM_debugWIRE;
+      } else if ($3->primary == K_NO) {
         current_part->flags &= ~AVRPART_HAS_DW;
-
+        current_part->prog_modes &= ~PM_debugWIRE;
+      }
       free_token($3);
     } |
 
   K_HAS_PDI TKN_EQUAL yesno
     {
-      if ($3->primary == K_YES)
+      if ($3->primary == K_YES) {
         current_part->flags |= AVRPART_HAS_PDI;
-      else if ($3->primary == K_NO)
+        current_part->prog_modes |= PM_PDI;
+      } else if ($3->primary == K_NO) {
         current_part->flags &= ~AVRPART_HAS_PDI;
-
+        current_part->prog_modes &= ~PM_PDI;
+      }
       free_token($3);
     } |
 
   K_HAS_UPDI TKN_EQUAL yesno
     {
-      if ($3->primary == K_YES)
+      if ($3->primary == K_YES) {
         current_part->flags |= AVRPART_HAS_UPDI;
-      else if ($3->primary == K_NO)
+        current_part->prog_modes |= PM_UPDI;
+      } else if ($3->primary == K_NO) {
         current_part->flags &= ~AVRPART_HAS_UPDI;
-
+        current_part->prog_modes &= ~PM_UPDI;
+      }
       free_token($3);
     } |
 
   K_HAS_TPI TKN_EQUAL yesno
     {
-      if ($3->primary == K_YES)
+      if ($3->primary == K_YES) {
         current_part->flags |= AVRPART_HAS_TPI;
-      else if ($3->primary == K_NO)
+        current_part->prog_modes |= PM_TPI;
+      } else if ($3->primary == K_NO) {
         current_part->flags &= ~AVRPART_HAS_TPI;
-
+        current_part->prog_modes &= ~PM_TPI;
+     }
       free_token($3);
     } |
 
@@ -1135,11 +1187,13 @@ part_parm :
 
   K_IS_AVR32 TKN_EQUAL yesno
     {
-      if ($3->primary == K_YES)
+      if ($3->primary == K_YES) {
         current_part->flags |= AVRPART_AVR32;
-      else if ($3->primary == K_NO)
+        current_part->prog_modes |= PM_aWire;
+      } else if ($3->primary == K_NO) {
         current_part->flags &= ~AVRPART_AVR32;
-
+        current_part->prog_modes &= ~PM_aWire;
+      }
       free_token($3);
     } |
 
@@ -1255,14 +1309,6 @@ part_parm :
     } |
 
 
-/*
-  K_EEPROM { current_mem = AVR_M_EEPROM; }
-    mem_specs |
-
-  K_FLASH { current_mem = AVR_M_FLASH; }
-    mem_specs |
-*/
-
   K_MEMORY TKN_STRING 
     { /* select memory for extension or create if not there */
       AVRMEM *mem = avr_locate_mem_noalias(current_part, $2->value.string);
@@ -1273,6 +1319,7 @@ part_parm :
       }
       avr_add_mem_order($2->value.string);
       current_mem = mem;
+      current_strct = COMP_AVRMEM;
       free_token($2);
     }
     mem_specs 
@@ -1295,6 +1342,7 @@ part_parm :
       }
       cfg_pop_comms();
       current_mem = NULL; 
+      current_strct = COMP_AVRPART;
     } |
   K_MEMORY TKN_STRING TKN_EQUAL K_NULL
    {
@@ -1306,6 +1354,7 @@ part_parm :
       free_token($2);
       cfg_pop_comms();
       current_mem = NULL;
+      current_strct = COMP_AVRPART;
     } |
   opcode TKN_EQUAL string_list {
     { 
@@ -1355,6 +1404,11 @@ mem_specs :
 
 
 mem_spec :
+  TKN_COMPONENT TKN_EQUAL expr {
+    cfg_assign((char *) current_mem, COMP_AVRMEM, $1->value.comp, &$3->value);
+    free_token($1);
+  } |
+
   K_PAGED          TKN_EQUAL yesno
     {
       current_mem->paged = $3->primary == K_YES ? 1 : 0;
diff --git a/src/developer_opts.c b/src/developer_opts.c
index c8aa903c..8f716f03 100644
--- a/src/developer_opts.c
+++ b/src/developer_opts.c
@@ -63,7 +63,7 @@ static struct {
   const char *mcu, *var, *value;
 } ptinj[] = {
   // Add triples here, eg, {"ATmega328P", "mcuid", "999"},
- {NULL, NULL, NULL},
+  {NULL, NULL, NULL},
 };
 
 static struct {
@@ -134,7 +134,7 @@ static void printallopcodes(const AVRPART *p, const char *d, OPCODE * const *opa
 
 
 // Programming modes
-static char *prog_modes(const AVRPART *p) {
+static char *prog_modes_str_flags(const AVRPART *p) {
   static char type[1024];
 
   *type = 0;
@@ -194,6 +194,34 @@ static char *prog_modes(const AVRPART *p) {
   return type + (*type == '|');
 }
 
+static char *prog_modes_str(int pm) {
+  static char type[1024];
+
+  strcpy(type, "0");
+  if(pm & PM_SPM)
+    strcat(type, " | PM_SPM");
+  if(pm & PM_TPI)
+    strcat(type, " | PM_TPI");
+  if(pm & PM_ISP)
+    strcat(type, " | PM_ISP");
+  if(pm & PM_PDI)
+    strcat(type, " | PM_PDI");
+  if(pm & PM_UPDI)
+    strcat(type, " | PM_UPDI");
+  if(pm & PM_HVSP)
+    strcat(type, " | PM_HVSP");
+  if(pm & PM_HVPP)
+    strcat(type, " | PM_HVPP");
+  if(pm & PM_debugWIRE)
+    strcat(type, " | PM_debugWIRE");
+  if(pm & PM_JTAG)
+    strcat(type, " | PM_JTAG");
+  if(pm & PM_aWire)
+    strcat(type, " | PM_aWire");
+
+  return type + (type[1] == 0? 0: 4);
+}
+
 
 // Check whether address bits are where they should be in ISP commands
 static void checkaddr(int memsize, int pagesize, int opnum, const OPCODE *op, const AVRPART *p, const AVRMEM *m) {
@@ -573,6 +601,10 @@ static void dev_part_strct(const AVRPART *p, bool tsv, const AVRPART *base, bool
   _if_partout_str(strcmp, descstr, desc);
   _if_partout_str(strcmp, cfg_escape(p->id), id);
   _if_partout_str(strcmp, cfg_escape(p->family_id), family_id);
+  _if_partout_str(intcmp, cfg_strdup("dev_part_strct()", prog_modes_str(p->prog_modes)), prog_modes);
+  _if_partout(intcmp, "%d", mcuid);
+  _if_partout(intcmp, "%d", n_interrupts);
+  _if_partout(intcmp, "%d", n_page_erase);
   _if_partout(intcmp, "%d", hvupdi_variant);
   _if_partout(intcmp, "0x%02x", stk500_devcode);
   _if_partout(intcmp, "0x%02x", avr910_devcode);
@@ -1032,7 +1064,7 @@ void dev_output_part_defs(char *partdesc) {
           nfuses,
           ok,
           p->flags,
-          prog_modes(p),
+          prog_modes_str_flags(p),
           p->config_file, p->lineno
         );
       }
@@ -1189,6 +1221,7 @@ static void dev_pgm_strct(const PROGRAMMER *pgm, bool tsv, const PROGRAMMER *bas
   _if_pgmout_str(strcmp, cfg_escape(pgm->desc), desc);
   if(!base || base->initpgm != pgm->initpgm)
     _pgmout_fmt("type", "\"%s\"", locate_programmer_type_id(pgm->initpgm));
+  _if_pgmout_str(intcmp, cfg_strdup("dev_pgm_strct()", prog_modes_str(pgm->prog_modes)), prog_modes);
   if(!base || base->conntype != pgm->conntype)
     _pgmout_fmt("connection_type", "%s", connstr(pgm->conntype));
   _if_pgmout(intcmp, "%d", baudrate);
diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi
index cdc7602b..19d019c5 100644
--- a/src/doc/avrdude.texi
+++ b/src/doc/avrdude.texi
@@ -1694,34 +1694,53 @@ The format of the programmer definition is as follows:
 
 @smallexample
 programmer
-    parent <id>                                 # <id> is a quoted string
+    parent <id>                                 # optional parent
     id       = <id1> [, <id2> [, <id3>] ...] ;  # <idN> are quoted strings
     desc     = <description> ;                  # quoted string
-    type     = "par" | "stk500" | ... ;         # programmer type (see below for a list)
-    baudrate = <num> ;                          # baudrate for serial ports
-    vcc      = <num1> [, <num2> ... ] ;         # pin number(s)
-    buff     = <num1> [, <num2> ... ] ;         # pin number(s)
-    reset    = <num> ;                          # pin number
-    sck      = <num> ;                          # pin number
-    mosi     = <num> ;                          # pin number
-    miso     = <num> ;                          # pin number
-    errled   = <num> ;                          # pin number
-    rdyled   = <num> ;                          # pin number
-    pgmled   = <num> ;                          # pin number
-    vfyled   = <num> ;                          # pin number
-    usbvid   = <hexnum>;                        # USB VID (Vendor ID)
-    usbpid   = <hexnum> [, <hexnum> ...];       # USB PID (Product ID)
-    usbdev   = <interface>;                     # USB interface or other device info 
-    usbvendor = <vendorname>;                   # USB Vendor Name
-    usbproduct = <productname>;                 # USB Product Name
-    usbsn    = <serialno>;                      # USB Serial Number
-  ;
+    type     = <type>;                          # programmer type, quoted string
+                                                # supported types can be listed by "-c ?type"
+    prog_modes = PM_<i/f> @{ | PM_<i/f> @}        # interfaces, eg, PM_SPM|PM_PDI
+    connection_type = parallel | serial | usb | spi
+    baudrate = <num> ;                          # baudrate for avr910-programmer
+    vcc      = <pin1> [, <pin2> ... ] ;         # pin number(s)
+    buff     = <pin1> [, <pin2> ... ] ;         # pin number(s)
+    reset    = <pin> ;                          # pin number
+    sck      = <pin> ;                          # pin number
+    mosi     = <pin> ;                          # pin number
+    miso     = <pin> ;                          # pin number
+    errled   = <pin> ;                          # pin number
+    rdyled   = <pin> ;                          # pin number
+    pgmled   = <pin> ;                          # pin number
+    vfyled   = <pin> ;                          # pin number
+    usbvid   = <hexnum> ;                       # USB VID (Vendor ID)
+    usbpid   = <hexnum> [, <hexnum> ...] ;      # USB PID (Product ID)
+    usbdev   = <interface> ;                    # USB interface or other device info
+    usbvendor = <vendorname> ;                  # USB Vendor Name
+    usbproduct = <productname> ;                # USB Product Name
+    usbsn    = <serialno> ;                     # USB Serial Number
+    hvupdi_support = <num> [, <num>, ... ] ;    # UPDI HV Variants Support
+;
 @end smallexample
 
 @noindent
 If a parent is specified, all settings of it (except its ids) are used for the new 
 programmer. These values can be changed by new setting them for the new programmer.
 
+@noindent
+Known programming modes are
+@itemize @bullet
+@item @code{PM_SPM}: Bootloaders, self-programming with SPM opcodes or NVM Controllers
+@item @code{PM_TPI}: Tiny Programming Interface (t4, t5, t9, t10, t20, t40, t102, t104)
+@item @code{PM_ISP}: SPI programming for In-System Programming (almost all classic parts)
+@item @code{PM_PDI}: Program and Debug Interface (xmega parts)
+@item @code{PM_UPDI}: Unified Program and Debug Interface
+@item @code{PM_HVSP}: High Voltage Serial Programming (some classic parts)
+@item @code{PM_HVPP}: High Voltage Parallel Programming (most non-HVSP classic parts)
+@item @code{PM_debugWIRE}: Simpler alternative to JTAG (a subset of HVPP/HVSP parts)
+@item @code{PM_JTAG}: Joint Test Action Group standard (some classic parts, some xmega)
+@item @code{PM_aWire}: AVR32 parts
+@end itemize
+
 @noindent
 To invert a bit in the pin definitions, use @code{= ~ <num>}.
 
@@ -1729,7 +1748,7 @@ To invert a bit in the pin definitions, use @code{= ~ <num>}.
 Not all programmer types can handle a list of USB PIDs.
 
 @noindent
-Following programmer types are currently implemented:
+The following programmer types are currently implemented:
 
 @multitable @columnfractions .25 .6
 @include programmer_types.texi
@@ -1743,29 +1762,35 @@ Following programmer types are currently implemented:
 
 @smallexample
 part
-    id               = <id> ;                 # quoted string
     desc             = <description> ;        # quoted string
-    family_id        = <description> ;        # quoted string
+    id               = <id> ;                 # quoted string
+    family_id        = <id> ;                 # quoted string, eg, "megaAVR" or "tinyAVR"
+    prog_modes       = PM_<i/f> @{ | PM_<i/f> @} # interfaces, eg, PM_SPM|PM_ISP|PM_HVPP|PM_debugWIRE
+    mcuid            = <num>;                 # unique id in 0..2039 for urclock programmer
+    n_interrupts     = <num>;                 # number of interrupts, used for vector bootloaders
+    n_page_erase     = <num>;                 # if set, number of pages erased during NVM erase
+    hvupdi_variant   = <num> ;                # numeric -1 (n/a) or 0..2
+    devicecode       = <num> ;                # deprecated, use stk500_devcode
+    stk500_devcode   = <num> ;                # numeric
+    avr910_devcode   = <num> ;                # numeric
     has_jtag         = <yes/no> ;             # part has JTAG i/f
     has_debugwire    = <yes/no> ;             # part has debugWire i/f
     has_pdi          = <yes/no> ;             # part has PDI i/f
     has_updi         = <yes/no> ;             # part has UPDI i/f
     has_tpi          = <yes/no> ;             # part has TPI i/f
-    devicecode       = <num> ;                # numeric
-    stk500_devcode   = <num> ;                # numeric
-    avr910_devcode   = <num> ;                # numeric
+    is_avr32         = <yes/no> ;             # AVR32 part
+    is_at90s1200     = <yes/no> ;             # AT90S1200 part
     signature        = <num> <num> <num> ;    # signature bytes
     usbpid           = <num> ;                # DFU USB PID
-    reset            = dedicated | io;
-    retry_pulse      = reset | sck;
-    pgm_enable       = <instruction format> ;
-    chip_erase       = <instruction format> ;
     chip_erase_delay = <num> ;                # micro-seconds
+    reset            = dedicated | io ;
+    retry_pulse      = reset | sck ;
+    chip_erase_delay = <num> ;                # chip erase delay (us)
     # STK500 parameters (parallel programming IO lines)
     pagel            = <num> ;                # pin name in hex, i.e., 0xD7
     bs2              = <num> ;                # pin name in hex, i.e., 0xA0
     serial           = <yes/no> ;             # can use serial downloading
-    parallel         = <yes/no/pseudo>;       # can use par. programming
+    parallel         = <yes/no/pseudo> ;      # can use par. programming
     # STK500v2 parameters, to be taken from Atmel's XML files
     timeout          = <num> ;
     stabdelay        = <num> ;
@@ -1777,52 +1802,59 @@ part
     predelay         = <num> ;
     postdelay        = <num> ;
     pollmethod       = <num> ;
-    mode             = <num> ;
-    delay            = <num> ;
-    blocksize        = <num> ;
-    readsize         = <num> ;
     hvspcmdexedelay  = <num> ;
     # STK500v2 HV programming parameters, from XML
-    pp_controlstack  = <num>, <num>, ...;     # PP only
-    hvsp_controlstack = <num>, <num>, ...;    # HVSP only
-    hventerstabdelay = <num>;
-    progmodedelay    = <num>;                 # PP only
-    latchcycles      = <num>;
-    togglevtg        = <num>;
-    poweroffdelay    = <num>;
-    resetdelayms     = <num>;
-    resetdelayus     = <num>;
-    hvleavestabdelay = <num>;
-    resetdelay       = <num>;
-    synchcycles      = <num>;                 # HVSP only
-    chiperasepulsewidth = <num>;              # PP only
-    chiperasepolltimeout = <num>;
-    chiperasetime    = <num>;                 # HVSP only
-    programfusepulsewidth = <num>;            # PP only
-    programfusepolltimeout = <num>;
-    programlockpulsewidth = <num>;            # PP only
-    programlockpolltimeout = <num>;
+    pp_controlstack  = <num>, <num>, ... ;  # PP only
+    hvsp_controlstack = <num>, <num>, ... ; # HVSP only
+    flash_instr      = <num>, <num>, <num> ;
+    eeprom_instr     = <num>, <num>, ... ;
+    hventerstabdelay = <num> ;
+    progmodedelay    = <num> ;              # PP only
+    latchcycles      = <num> ;
+    togglevtg        = <num> ;
+    poweroffdelay    = <num> ;
+    resetdelayms     = <num> ;
+    resetdelayus     = <num> ;
+    hvleavestabdelay = <num> ;
+    resetdelay       = <num> ;
+    synchcycles      = <num> ;               # HVSP only
+    chiperasepulsewidth = <num> ;            # PP only
+    chiperasepolltimeout = <num> ;
+    chiperasetime    = <num> ;               # HVSP only
+    programfusepulsewidth = <num> ;          # PP only
+    programfusepolltimeout = <num> ;
+    programlockpulsewidth = <num> ;          # PP only
+    programlockpolltimeout = <num> ;
     # JTAG ICE mkII parameters, also from XML files
     allowfullpagebitstream = <yes/no> ;
     enablepageprogramming = <yes/no> ;
-    idr              = <num> ;                # IO addr of IDR (OCD) reg.
-    rampz            = <num> ;                # IO addr of RAMPZ reg.
-    spmcr            = <num> ;                # mem addr of SPMC[S]R reg.
-    eecr             = <num> ;                # mem addr of EECR reg.
-                                              # (only when != 0x3F)
-    is_at90s1200     = <yes/no> ;             # AT90S1200 part
-    is_avr32         = <yes/no> ;             # AVR32 part
+    idr              = <num> ;               # IO addr of IDR (OCD) reg
+    rampz            = <num> ;               # IO addr of RAMPZ reg
+    spmcr            = <num> ;               # mem addr of SPMC[S]R reg
+    eecr             = <num> ;               # mem addr of EECR reg only when != 0x3f
+    mcu_base         = <num> ;
+    nvm_base         = <num> ;
+    ocd_base         = <num> ;
+    ocdrev           = <num> ;
+    pgm_enable       = <instruction format> ;
+    chip_erase       = <instruction format> ;
 
     memory <memtype>
-        paged           = <yes/no> ;          # yes / no
+        paged           = <yes/no> ;          # yes/no (flash only, do not use for EEPROM)
+        offset          = <num> ;             # memory offset
         size            = <num> ;             # bytes
         page_size       = <num> ;             # bytes
         num_pages       = <num> ;             # numeric
         min_write_delay = <num> ;             # micro-seconds
         max_write_delay = <num> ;             # micro-seconds
-        readback_p1     = <num> ;             # byte value
-        readback_p2     = <num> ;             # byte value
-        pwroff_after_write = <yes/no> ;       # yes / no
+        readback        = <num> <num> ;       # pair of byte values
+        readback_p1     = <num> ;             # byte value (first component)
+        readback_p2     = <num> ;             # byte value (second component)
+        pwroff_after_write = <yes/no> ;       # yes/no
+        mode            = <num> ;             # STK500 v2 file parameter from Atmel's XML files
+        delay           = <num> ;             #   "
+        blocksize       = <num> ;             #   "
+        readsize        = <num> ;             #   "
         read            = <instruction format> ;
         write           = <instruction format> ;
         read_lo         = <instruction format> ;
@@ -1832,8 +1864,8 @@ part
         loadpage_lo     = <instruction format> ;
         loadpage_hi     = <instruction format> ;
         writepage       = <instruction format> ;
-      ;
-  ;
+    ;
+;
 @end smallexample
 
 @menu
@@ -1914,7 +1946,22 @@ write = "1  1  0  0   0  0  0  0   x x x x  x x x x",
 
 @end smallexample
 
+As the address bit numbers in the SPI opcodes are highly systematic, they
+don't really need to be specified. A compact version of the format
+specification neither uses bit-numbers for address lines nor spaces. If such
+a string is longer than 7 characters, then the characters @code{0}, @code{1},
+@code{x}, @code{a}, @code{i} and @code{o} will be recognised as the
+corresponding bit, whilst any of the characters @code{.}, @code{-}, @code{_}
+or @code{/} can act as arbitrary visual separators, which are ignored.
+Examples:
 
+@smallexample
+
+  loadpage_lo = "0100.0000--000x.xxxx--xxaa.aaaa--iiii.iiii";
+
+  loadpage_lo = "0100.0000", "000x.xxxx", "xxaa.aaaa", "iiii.iiii";
+
+@end smallexample
 
 @c
 @c Node
diff --git a/src/lexer.l b/src/lexer.l
index a69130d3..65b7a451 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -63,9 +63,9 @@ SIGN     [+-]
 
 %%
 
-{SIGN}?{DIGIT}+            { yylval = number(yytext); return TKN_NUMBER; }
-{SIGN}?{DIGIT}+"."{DIGIT}* { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
-{SIGN}?"."{DIGIT}+         { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
+{SIGN}?{DIGIT}+            { yylval = new_number(yytext); return TKN_NUMBER; }
+{SIGN}?{DIGIT}+"."{DIGIT}* { yylval = new_number_real(yytext); return TKN_NUMBER_REAL; }
+{SIGN}?"."{DIGIT}+         { yylval = new_number_real(yytext); return TKN_NUMBER_REAL; }
 
 ["]([^"\\\n]|\\.|\\\n)*["] {
   char *str= cfg_strdup("lexer.l", yytext);
@@ -73,12 +73,12 @@ SIGN     [+-]
   size_t len = strlen(str);
   if(len)
     str[len-1] = 0;
-  yylval = string(str);
+  yylval = new_string(str);
   free(str);
   return TKN_STRING;
 }
 
-0x{HEXDIGIT}+ { yylval = hexnumber(yytext); return TKN_NUMBER; }
+0x{HEXDIGIT}+ { yylval = new_hexnumber(yytext); return TKN_NUMBER; }
 
 #\n#\ PROGRAMMER\ DEFINITIONS\n#\n+ { /* Record comments so far as prologue and skip */
   cfg_capture_prologue();
@@ -121,6 +121,23 @@ SIGN     [+-]
      }
 
 
+prog_modes|mcuid|n_interrupts|n_page_erase { /* Components for assignment  */
+  Component_t *cp = cfg_comp_search(yytext, current_strct);
+  if(!cp) {
+    yyerror("Unknown component %s in %s", yytext, cfg_strct_name(current_strct));
+    return YYERRCODE;
+  }
+  yylval = new_token(TKN_COMPONENT);
+  yylval->value.comp = cp;
+  ccap();
+  return TKN_COMPONENT;
+}
+
+PM_SPM|PM_TPI|PM_ISP|PM_PDI|PM_UPDI|PM_HVSP|PM_HVPP|PM_debugWIRE|PM_JTAG|PM_aWire { /* Constants */
+  yylval = new_constant(yytext);
+  return TKN_NUMBER;
+}
+
 alias            { yylval=NULL; return K_ALIAS; }
 allowfullpagebitstream { yylval=NULL; ccap(); return K_ALLOWFULLPAGEBITSTREAM; }
 avr910_devcode   { yylval=NULL; ccap(); return K_AVR910_DEVCODE; }
@@ -258,7 +275,17 @@ yes              { yylval=new_token(K_YES); return K_YES; }
 ","       { yylval = NULL; pyytext(); return TKN_COMMA; }
 "="       { yylval = NULL; pyytext(); return TKN_EQUAL; }
 ";"       { yylval = NULL; pyytext(); return TKN_SEMI; }
-"~"       { yylval = NULL; pyytext(); return TKN_TILDE; }
+
+"|"       { yylval = NULL; pyytext(); return OP_OR; }
+"^"       { yylval = NULL; pyytext(); return OP_XOR; }
+"&"       { yylval = NULL; pyytext(); return OP_AND; }
+"+"       { yylval = NULL; pyytext(); return OP_PLUS; }
+"-"       { yylval = NULL; pyytext(); return OP_MINUS; }
+"*"       { yylval = NULL; pyytext(); return OP_TIMES; }
+"/"       { yylval = NULL; pyytext(); return OP_DIVIDE; }
+"%"       { yylval = NULL; pyytext(); return OP_MODULO; }
+"~"       { yylval = NULL; pyytext(); return OP_TILDE; }
+
 "("       { yylval = NULL; pyytext(); return TKN_LEFT_PAREN; }
 ")"       { yylval = NULL; pyytext(); return TKN_RIGHT_PAREN; }
 
diff --git a/src/libavrdude.h b/src/libavrdude.h
index 9288b43b..c99117f4 100644
--- a/src/libavrdude.h
+++ b/src/libavrdude.h
@@ -169,6 +169,7 @@ enum ctl_stack_t {
   CTL_STACK_HVSP      /* high voltage serial programming control stack */
 };
 
+
 /*
  * serial programming instruction bit specifications
  */
@@ -197,6 +198,18 @@ typedef struct opcode {
 #define AVRPART_IS_AT90S1200   0x1000  /* part is an AT90S1200 (needs special treatment) */
 #define AVRPART_HAS_UPDI       0x2000  /* part has UPDI i/f (AVR8X) */
 
+// Programming modes for parts and programmers: reflect changes in lexer.l, developer_opts.c and config.c
+#define PM_SPM                1 // Bootloaders, self-programming with SPM opcodes or NVM Controllers
+#define PM_TPI                2 // Tiny Programming Interface (t4, t5, t9, t10, t20, t40, t102, t104)
+#define PM_ISP                4 // SPI programming for In-System Programming (almost all classic parts)
+#define PM_PDI                8 // Program and Debug Interface (xmega parts)
+#define PM_UPDI              16 // Unified Program and Debug Interface
+#define PM_HVSP              32 // High Voltage Serial Programming (some classic parts)
+#define PM_HVPP              64 // High Voltage Parallel Programming (most non-HVSP classic parts)
+#define PM_debugWIRE        128 // Simpler alternative to JTAG (a subset of HVPP/HVSP parts)
+#define PM_JTAG             256 // Joint Test Action Group standard (some classic parts, some xmega)
+#define PM_aWire            512 // AVR32 parts
+
 #define HV_UPDI_VARIANT_0      0 /* Shared UPDI/GPIO/RESET pin, HV on UPDI pin (tinyAVR0/1/2)*/
 #define HV_UPDI_VARIANT_1      1 /* Dedicated UPDI pin, no HV (megaAVR0/AVR-Dx) */
 #define HV_UPDI_VARIANT_2      2 /* Shared UPDI pin, HV on _RESET (AVR-Ex) */
@@ -209,13 +222,24 @@ typedef struct opcode {
 
 #define TAG_ALLOCATED          1    /* memory byte is allocated */
 
-/* Any changes here, please also reflect in dev_part_strct() of developer_opts.c */
+/*
+ * Any changes in AVRPART or AVRMEM, please also ensure changes are made in
+ *  - lexer.l
+ *  - Either Component_t avr_comp[] of config.c or in config_gram.y
+ *  - dev_part_strct() in developer_opts.c
+ *  - avr_new_part() and/or avr_new_memtype() in avrpart.c for
+ *    initialisation; note that all const char * must be initialised with ""
+ */
 typedef struct avrpart {
   const char  * desc;               /* long part name */
   const char  * id;                 /* short part name */
   LISTID        comments;           // Used by developer options -p*/[ASsr...]
   const char  * parent_id;          /* Used by developer options */
   const char  * family_id;          /* family id in the SIB (avr8x) */
+  int           prog_modes;         /* Programming interfaces, see #define PM_... */
+  int           mcuid;              /* Unique id in 0..2039 for urclock programmer */
+  int           n_interrupts;       /* Number of interrupts, used for vector bootloaders */
+  int           n_page_erase;       /* If set, number of pages erased during NVM erase */
   int           hvupdi_variant;     /* HV pulse on UPDI pin, no pin or RESET pin */
   int           stk500_devcode;     /* stk500 device code */
   int           avr910_devcode;     /* avr910 device code */
@@ -262,7 +286,7 @@ typedef struct avrpart {
   int           programlockpulsewidth; /* stk500 v2 hv mode parameter */
   int           programlockpolltimeout; /* stk500 v2 hv mode parameter */
   int           synchcycles;        /* stk500 v2 hv mode parameter */
-  int           hvspcmdexedelay;    /* stk500 v2 xml file parameter */
+  int           hvspcmdexedelay;    /* stk500 v2 hv mode file parameter */
 
   unsigned char idr;                /* JTAG ICE mkII XML file parameter */
   unsigned char rampz;              /* JTAG ICE mkII XML file parameter */
@@ -677,13 +701,21 @@ typedef enum {
   CONNTYPE_SPI
 } conntype_t;
 
-/* Any changes here, please also reflect in dev_pgm_strct() of developer_opts.c */
+/*
+ * Any changes in PROGRAMMER, please also ensure changes are made in
+ *  - lexer.l
+ *  - Either Component_t avr_comp[] of config.c or config_gram.y
+ *  - dev_pgm_strct() in developer_opts.c
+ *  - pgm_new() in pgm.c for initialisation; note that all const char * must
+ *    be initialised with ""
+ */
 typedef struct programmer_t {
   LISTID id;
   const char *desc;
   void (*initpgm)(struct programmer_t *pgm); // Sets up the AVRDUDE programmer
   LISTID comments;              // Used by developer options -c*/[ASsr...]
   const char *parent_id;        // Used by developer options
+  int prog_modes;               // Programming interfaces, see #define PM_...
   struct pindef_t pin[N_PINS];
   conntype_t conntype;
   int baudrate;
@@ -695,7 +727,7 @@ typedef struct programmer_t {
   const char *usbproduct;
   LISTID hvupdi_support;        // List of UPDI HV variants the tool supports, see HV_UPDI_VARIANT_x
 
-  // Values below are not set by config_gram.y; make sure fd is first for dev_pgm_raw()
+  // Values below are not set by config_gram.y; ensure fd is first for dev_pgm_raw()
   union filedescriptor fd;
   char type[PGM_TYPELEN];
   char port[PGM_PORTLEN];