From 48919f59b318ac001146959079914fee4d27d386 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Tue, 11 Oct 2022 15:31:18 +0100
Subject: [PATCH 1/3] Use byte-wise read/write when page size is 1 in terminal
 cache

---
 src/avrcache.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/avrcache.c b/src/avrcache.c
index 299e9288..ed105f25 100644
--- a/src/avrcache.c
+++ b/src/avrcache.c
@@ -114,7 +114,10 @@
  *  - Memory has positive page size, which is a power of two
  *  - Memory has positive size, which is a multiple of the page size
  *  - Memory is flash type or eeprom type
+ *
+ * Note that in this definition the page size can be 1
  */
+
 int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem) {
   return pgm->paged_load && pgm->paged_write &&
          mem->page_size > 0 && (mem->page_size & (mem->page_size-1)) == 0 &&
@@ -127,6 +130,7 @@ int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem) {
  * Read the page containing addr from the device into buf
  *   - Caller to ensure buf has mem->page_size bytes
  *   - Part memory buffer mem is unaffected by this (though temporarily changed)
+ *   - Uses read_byte() if memory page size is one, otherwise paged_load()
  */
 int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf) {
   if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
@@ -135,6 +139,9 @@ int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
   int rc, pgsize = mem->page_size, off = addr & ~(pgsize-1);
   unsigned char *pagecopy = cfg_malloc("avr_read_page_default()", pgsize);
 
+  if(pgsize == 1)
+    return pgm->read_byte(pgm, p, mem, addr, buf);
+
   memcpy(pagecopy, mem->buf + off, pgsize);
   if((rc = pgm->paged_load(pgm, p, mem, pgsize, off, pgsize)) >= 0)
     memcpy(buf, mem->buf + off, pgsize);
@@ -149,6 +156,7 @@ int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
  * Write the data page to the device into the page containing addr
  *   - Caller to provide all mem->page_size bytes incl padding if any
  *   - Part memory buffer mem is unaffected by this (though temporarily changed)
+ *   - Uses write_byte() if memory page size is one, otherwise paged_write()
  */
 int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *data) {
   if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
@@ -157,6 +165,9 @@ int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
   int rc, pgsize = mem->page_size, off = addr & ~(pgsize-1);
   unsigned char *pagecopy = cfg_malloc("avr_write_page_default()", pgsize);
 
+  if(pgsize == 1)
+    return pgm->write_byte(pgm, p, mem, addr, *data);
+
   memcpy(pagecopy, mem->buf + off, pgsize);
   memcpy(mem->buf + off, data, pgsize);
   rc = pgm->paged_write(pgm, p, mem, pgsize, off, pgsize);
@@ -652,11 +663,16 @@ int avr_page_erase_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
 
   int addr = uaddr;
 
-  if(!pgm->page_erase || !avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
+  if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
     return LIBAVRDUDE_GENERAL_FAILURE;
 
-  if(pgm->page_erase(pgm, p, mem, uaddr) < 0)
-    return LIBAVRDUDE_GENERAL_FAILURE;
+  if(mem->page_size == 1) {
+    if(pgm->write_byte(pgm, p, mem, uaddr, 0xff) < 0)
+      return LIBAVRDUDE_GENERAL_FAILURE;
+  } else {
+    if(!pgm->page_erase || pgm->page_erase(pgm, p, mem, uaddr) < 0)
+      return LIBAVRDUDE_GENERAL_FAILURE;
+  }
 
   AVR_Cache *cp = avr_mem_is_eeprom_type(mem)? pgm->cp_eeprom: pgm->cp_flash;
 

From 8a3864d2639c97b03a57b685c96aa52ef32290a9 Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Wed, 12 Oct 2022 15:53:54 +0100
Subject: [PATCH 2/3] Fall back on bytewise r/w if paged access fails for avr
 cache

---
 src/avrcache.c | 38 +++++++++++++++++++++++++++++---------
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/avrcache.c b/src/avrcache.c
index ed105f25..aa919584 100644
--- a/src/avrcache.c
+++ b/src/avrcache.c
@@ -32,7 +32,7 @@
 #include "avrintel.h"
 
 /*
- * Provides an API for cached byte-wise access
+ * Provides an API for cached bytewise access
  *
  * int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const
  *   AVRMEM *mem, unsigned long addr, unsigned char *value);
@@ -52,8 +52,8 @@
  * avr_read_byte_cached() and avr_write_byte_cached() use a cache if paged
  * routines are available and if the device memory is EEPROM or flash,
  * otherwise they fall back to pgm->read_byte() and pgm->write_byte(),
- * respectively. Byte-wise cached read always gets its data from the cache,
- * possibly after reading a page from the device memory. Byte-wise cached
+ * respectively. Bytewise cached read always gets its data from the cache,
+ * possibly after reading a page from the device memory. Bytewise cached
  * write with an address in memory range only ever modifies the cache. Any
  * modifications are written to the device after calling avr_flush_cache() or
  * when attempting to read or write from a location outside the address range
@@ -131,6 +131,7 @@ int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem) {
  *   - Caller to ensure buf has mem->page_size bytes
  *   - Part memory buffer mem is unaffected by this (though temporarily changed)
  *   - Uses read_byte() if memory page size is one, otherwise paged_load()
+ *   - Fall back to bytewise read if paged_load() returned an error
  */
 int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf) {
   if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
@@ -146,6 +147,18 @@ int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
   if((rc = pgm->paged_load(pgm, p, mem, pgsize, off, pgsize)) >= 0)
     memcpy(buf, mem->buf + off, pgsize);
   memcpy(mem->buf + off, pagecopy, pgsize);
+
+  if(rc < 0) {
+    rc = LIBAVRDUDE_SUCCESS;
+    for(int i=0; i<pgsize; i++) {
+      if(pgm->read_byte(pgm, p, mem, off+i, pagecopy+i) < 0) {
+        rc = LIBAVRDUDE_GENERAL_FAILURE;
+        break;
+      }
+    }
+    if(rc == LIBAVRDUDE_SUCCESS)
+      memcpy(buf, pagecopy, pgsize);
+  }
   free(pagecopy);
 
   return rc;
@@ -248,13 +261,20 @@ static int loadCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p,
 
 
 static int writeCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int base, int nlOnErr) {
-  // Write modified page cont to device
+  // Write modified page cont to device; if unsuccessful try bytewise access
   if(avr_write_page_default(pgm, p, mem, base, cp->cont + base) < 0) {
-    report_progress(1, -1, NULL);
-    if(nlOnErr && quell_progress)
-      avrdude_message(MSG_INFO, "\n");
-    avrdude_message(MSG_INFO, "%s: writeCachePage() %s write error at addr 0x%04x\n", progname, mem->desc, base);
-    return LIBAVRDUDE_GENERAL_FAILURE;
+    for(int i=0; i < cp->page_size; i++)
+      if(cp->cont[base+i] != cp->copy[base+i])
+        if(pgm->write_byte(pgm, p, mem, base+i, cp->cont[base+i]) < 0 ||
+           pgm->read_byte(pgm, p, mem, base+i, cp->copy+base+i) < 0) {
+          report_progress(1, -1, NULL);
+          if(nlOnErr && quell_progress)
+            avrdude_message(MSG_INFO, "\n");
+          avrdude_message(MSG_INFO, "%s: writeCachePage() %s access error at addr 0x%04x\n", progname, mem->desc, base+i);
+          return LIBAVRDUDE_GENERAL_FAILURE;
+        }
+
+    return LIBAVRDUDE_SUCCESS;  // Bytewise writes & reads successful
   }
   // Read page back from device and update copy to what is on device
   if(avr_read_page_default(pgm, p, mem, base, cp->copy + base) < 0) {

From 714c2fbf959253f4b03106e39d9a2ec140db8ceb Mon Sep 17 00:00:00 2001
From: Stefan Rueger <stefan.rueger@urclocks.com>
Date: Wed, 12 Oct 2022 15:55:22 +0100
Subject: [PATCH 3/3] Rename variable for clarity in avrcache.c

---
 src/avrcache.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/avrcache.c b/src/avrcache.c
index aa919584..23563036 100644
--- a/src/avrcache.c
+++ b/src/avrcache.c
@@ -137,21 +137,21 @@ int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
   if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
     return LIBAVRDUDE_GENERAL_FAILURE;
 
-  int rc, pgsize = mem->page_size, off = addr & ~(pgsize-1);
+  int rc, pgsize = mem->page_size, base = addr & ~(pgsize-1);
   unsigned char *pagecopy = cfg_malloc("avr_read_page_default()", pgsize);
 
   if(pgsize == 1)
     return pgm->read_byte(pgm, p, mem, addr, buf);
 
-  memcpy(pagecopy, mem->buf + off, pgsize);
-  if((rc = pgm->paged_load(pgm, p, mem, pgsize, off, pgsize)) >= 0)
-    memcpy(buf, mem->buf + off, pgsize);
-  memcpy(mem->buf + off, pagecopy, pgsize);
+  memcpy(pagecopy, mem->buf + base, pgsize);
+  if((rc = pgm->paged_load(pgm, p, mem, pgsize, base, pgsize)) >= 0)
+    memcpy(buf, mem->buf + base, pgsize);
+  memcpy(mem->buf + base, pagecopy, pgsize);
 
   if(rc < 0) {
     rc = LIBAVRDUDE_SUCCESS;
     for(int i=0; i<pgsize; i++) {
-      if(pgm->read_byte(pgm, p, mem, off+i, pagecopy+i) < 0) {
+      if(pgm->read_byte(pgm, p, mem, base+i, pagecopy+i) < 0) {
         rc = LIBAVRDUDE_GENERAL_FAILURE;
         break;
       }
@@ -175,16 +175,16 @@ int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
   if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
     return LIBAVRDUDE_GENERAL_FAILURE;
 
-  int rc, pgsize = mem->page_size, off = addr & ~(pgsize-1);
+  int rc, pgsize = mem->page_size, base = addr & ~(pgsize-1);
   unsigned char *pagecopy = cfg_malloc("avr_write_page_default()", pgsize);
 
   if(pgsize == 1)
     return pgm->write_byte(pgm, p, mem, addr, *data);
 
-  memcpy(pagecopy, mem->buf + off, pgsize);
-  memcpy(mem->buf + off, data, pgsize);
-  rc = pgm->paged_write(pgm, p, mem, pgsize, off, pgsize);
-  memcpy(mem->buf + off, pagecopy, pgsize);
+  memcpy(pagecopy, mem->buf + base, pgsize);
+  memcpy(mem->buf + base, data, pgsize);
+  rc = pgm->paged_write(pgm, p, mem, pgsize, base, pgsize);
+  memcpy(mem->buf + base, pagecopy, pgsize);
   free(pagecopy);
 
   return rc;