From fa3739aa237507b61ef64994bd17302fd561860b Mon Sep 17 00:00:00 2001
From: Bob Kuhn <bob.kuhn@att.net>
Date: Thu, 9 May 2019 21:57:44 -0500
Subject: [PATCH] M100: LPC1768 and DUE compatibility (#13962)

---
 Marlin/src/gcode/calibrate/M100.cpp | 152 ++++++++++++++++++----------
 1 file changed, 97 insertions(+), 55 deletions(-)

diff --git a/Marlin/src/gcode/calibrate/M100.cpp b/Marlin/src/gcode/calibrate/M100.cpp
index c2121f52d1..eed7800976 100644
--- a/Marlin/src/gcode/calibrate/M100.cpp
+++ b/Marlin/src/gcode/calibrate/M100.cpp
@@ -50,7 +50,7 @@
  *
  * Also, there are two support functions that can be called from a developer's C code.
  *
- *    uint16_t check_for_free_memory_corruption(PGM_P const ptr);
+ *    uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start);
  *    void M100_dump_routine(PGM_P const title, const char *start, const char *end);
  *
  * Initial version by Roxy-3D
@@ -60,16 +60,57 @@
 
 #define TEST_BYTE ((char) 0xE5)
 
-extern char* __brkval;
-extern size_t  __heap_start, __heap_end, __flp;
-extern char __bss_end;
+#if defined(__AVR__) || IS_32BIT_TEENSY
+
+  extern char __bss_end;
+  char* end_bss =  &__bss_end;
+  char* free_memory_start = end_bss;
+
+
+  char* free_memory_end = 0;
+  char* stacklimit = 0;
+  char* heaplimit = 0;
+
+  #define MEMORY_END_CORRECTION 0
+
+#elif defined(TARGET_LPC1768)
+
+  extern char   __bss_end__;
+  extern char   __StackLimit;
+  extern char   __HeapLimit;
+
+  char* end_bss =  &__bss_end__;
+  char* stacklimit =  &__StackLimit;
+  char* heaplimit =  &__HeapLimit ;
+
+  #define MEMORY_END_CORRECTION 0x200
+
+  char* free_memory_start = heaplimit;
+  char* free_memory_end = stacklimit - MEMORY_END_CORRECTION;
+
+
+#elif defined(__SAM3X8E__)
+
+  extern char   _ebss;
+
+  char* end_bss = &_ebss;
+
+  char* free_memory_start = end_bss;
+
+  char* free_memory_end = 0;
+  char* stacklimit = 0;
+  char* heaplimit = 0;
+
+  #define MEMORY_END_CORRECTION 0x10000  // need to stay well below 0x20080000 or M100 F crashes
+
+#else
+  #error "M100 - unsupported CPU"
+#endif
 
 //
 // Utility functions
 //
 
-#define END_OF_HEAP() (__brkval ? __brkval : &__bss_end)
-
 // Location of a variable on its stack frame. Returns a value above
 // the stack (once the function returns to the caller).
 char* top_of_stack() {
@@ -78,9 +119,9 @@ char* top_of_stack() {
 }
 
 // Count the number of test bytes at the specified location.
-inline int32_t count_test_bytes(const char * const ptr) {
+inline int32_t count_test_bytes(const char * const start_free_memory) {
   for (uint32_t i = 0; i < 32000; i++)
-    if (char(ptr[i]) != TEST_BYTE)
+    if (char(start_free_memory[i]) != TEST_BYTE)
       return i - 1;
 
   return -1;
@@ -93,35 +134,35 @@ inline int32_t count_test_bytes(const char * const ptr) {
 #if ENABLED(M100_FREE_MEMORY_DUMPER)
   /**
    * M100 D
-   *  Dump the free memory block from __brkval to the stack pointer.
+   *  Dump the free memory block from brkval to the stack pointer.
    *  malloc() eats memory from the start of the block and the stack grows
    *  up from the bottom of the block. Solid test bytes indicate nothing has
    *  used that memory yet. There should not be anything but test bytes within
    *  the block. If so, it may indicate memory corruption due to a bad pointer.
    *  Unexpected bytes are flagged in the right column.
    */
-  inline void dump_free_memory(const char *ptr, const char *sp) {
+  inline void dump_free_memory(const char *start_free_memory, const char *end_free_memory) {
     //
     // Start and end the dump on a nice 16 byte boundary
     // (even though the values are not 16-byte aligned).
     //
-    ptr = (char*)((ptr_int_t)((uint32_t)ptr & 0xFFFFFFF0)); // Align to 16-byte boundary
-    sp  = (char*)((ptr_int_t)((uint32_t)sp  | 0x0000000F)); // Align sp to the 15th byte (at or above sp)
+    start_free_memory = (char*)((ptr_int_t)((uint32_t)start_free_memory & 0xFFFFFFF0)); // Align to 16-byte boundary
+    end_free_memory  = (char*)((ptr_int_t)((uint32_t)end_free_memory  | 0x0000000F)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
 
     // Dump command main loop
-    while (ptr < sp) {
-      print_hex_address(ptr);             // Print the address
+    while (start_free_memory < end_free_memory) {
+      print_hex_address(start_free_memory);             // Print the address
       SERIAL_CHAR(':');
       for (uint8_t i = 0; i < 16; i++) {  // and 16 data bytes
         if (i == 8) SERIAL_CHAR('-');
-        print_hex_byte(ptr[i]);
+        print_hex_byte(start_free_memory[i]);
         SERIAL_CHAR(' ');
       }
       serial_delay(25);
       SERIAL_CHAR('|');                   // Point out non test bytes
       for (uint8_t i = 0; i < 16; i++) {
-        char ccc = (char)ptr[i]; // cast to char before automatically casting to char on assignment, in case the compiler is broken
-        if (&ptr[i] >= (const char*)command_queue && &ptr[i] < (const char*)(command_queue + sizeof(command_queue))) { // Print out ASCII in the command buffer area
+        char ccc = (char)start_free_memory[i]; // cast to char before automatically casting to char on assignment, in case the compiler is broken
+        if (&start_free_memory[i] >= (const char*)command_queue && &start_free_memory[i] < (const char*)(command_queue + sizeof(command_queue))) { // Print out ASCII in the command buffer area
           if (!WITHIN(ccc, ' ', 0x7E)) ccc = ' ';
         }
         else { // If not in the command buffer area, flag bytes that don't match the test byte
@@ -130,7 +171,7 @@ inline int32_t count_test_bytes(const char * const ptr) {
         SERIAL_CHAR(ccc);
       }
       SERIAL_EOL();
-      ptr += 16;
+      start_free_memory += 16;
       serial_delay(25);
       idle();
     }
@@ -143,7 +184,7 @@ inline int32_t count_test_bytes(const char * const ptr) {
     // Round the start and end locations to produce full lines of output
     //
     start = (char*)((ptr_int_t)((uint32_t)start & 0xFFFFFFF0)); // Align to 16-byte boundary
-    end   = (char*)((ptr_int_t)((uint32_t)end   | 0x0000000F)); // Align sp to the 15th byte (at or above sp)
+    end   = (char*)((ptr_int_t)((uint32_t)end   | 0x0000000F)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
     dump_free_memory(start, end);
   }
 
@@ -152,17 +193,15 @@ inline int32_t count_test_bytes(const char * const ptr) {
 inline int check_for_free_memory_corruption(PGM_P const title) {
   serialprintPGM(title);
 
-  char *ptr = END_OF_HEAP(), *sp = top_of_stack();
-  int n = sp - ptr;
+  char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end;
+  int n = end_free_memory - start_free_memory;
 
   SERIAL_ECHOPAIR("\nfmc() n=", n);
-  SERIAL_ECHOPAIR("\n&__brkval: ", hex_address(&__brkval));
-  SERIAL_ECHOPAIR("=",             hex_address(__brkval));
-  SERIAL_ECHOPAIR("\n__bss_end: ", hex_address(&__bss_end));
-  SERIAL_ECHOPAIR(" sp=",          hex_address(sp));
+  SERIAL_ECHOPAIR("\nfree_memory_start=", hex_address(free_memory_start));
+  SERIAL_ECHOLNPAIR("  end_free_memory=",          hex_address(end_free_memory));
 
-  if (sp < ptr)  {
-    SERIAL_ECHOPGM(" sp < Heap ");
+  if (end_free_memory < start_free_memory)  {
+    SERIAL_ECHOPGM(" end_free_memory < Heap ");
     // SET_INPUT_PULLUP(63);           // if the developer has a switch wired up to their controller board
     // safe_delay(5);                  // this code can be enabled to pause the display as soon as the
     // while ( READ(63))               // malfunction is detected.   It is currently defaulting to a switch
@@ -172,29 +211,29 @@ inline int check_for_free_memory_corruption(PGM_P const title) {
     //   idle();
     serial_delay(20);
     #if ENABLED(M100_FREE_MEMORY_DUMPER)
-      M100_dump_routine(PSTR("   Memory corruption detected with sp<Heap\n"), (char*)0x1B80, (char*)0x21FF);
+      M100_dump_routine(PSTR("   Memory corruption detected with end_free_memory<Heap\n"), (char*)0x1B80, (char*)0x21FF);
     #endif
   }
 
   // Scan through the range looking for the biggest block of 0xE5's we can find
   int block_cnt = 0;
   for (int i = 0; i < n; i++) {
-    if (ptr[i] == TEST_BYTE) {
-      int32_t j = count_test_bytes(ptr + i);
+    if (start_free_memory[i] == TEST_BYTE) {
+      int32_t j = count_test_bytes(start_free_memory + i);
       if (j > 8) {
         // SERIAL_ECHOPAIR("Found ", j);
-        // SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(ptr + i));
+        // SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(start_free_memory + i));
         i += j;
         block_cnt++;
         SERIAL_ECHOPAIR(" (", block_cnt);
         SERIAL_ECHOPAIR(") found=", j);
-        SERIAL_ECHOPGM("   ");
+        SERIAL_ECHOLNPGM("   ");
       }
     }
   }
   SERIAL_ECHOPAIR("  block_found=", block_cnt);
 
-  if (block_cnt != 1 || __brkval != nullptr)
+  if (block_cnt != 1)
     SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
 
   if (block_cnt == 0)       // Make sure the special case of no free blocks shows up as an
@@ -215,12 +254,12 @@ inline int check_for_free_memory_corruption(PGM_P const title) {
  *  Return the number of free bytes in the memory pool,
  *  with other vital statistics defining the pool.
  */
-inline void free_memory_pool_report(char * const ptr, const int32_t size) {
+inline void free_memory_pool_report(char * const start_free_memory, const int32_t size) {
   int32_t max_cnt = -1, block_cnt = 0;
   char *max_addr = nullptr;
   // Find the longest block of test bytes in the buffer
   for (int32_t i = 0; i < size; i++) {
-    char *addr = ptr + i;
+    char *addr = start_free_memory + i;
     if (*addr == TEST_BYTE) {
       const int32_t j = count_test_bytes(addr);
       if (j > 8) {
@@ -249,14 +288,14 @@ inline void free_memory_pool_report(char * const ptr, const int32_t size) {
    *  Corrupt <num> locations in the free memory pool and report the corrupt addresses.
    *  This is useful to check the correctness of the M100 D and the M100 F commands.
    */
-  inline void corrupt_free_memory(char *ptr, const uint32_t size) {
-    ptr += 8;
-    const uint32_t near_top = top_of_stack() - ptr - 250, // -250 to avoid interrupt activity that's altered the stack.
+  inline void corrupt_free_memory(char *start_free_memory, const uint32_t size) {
+    start_free_memory += 8;
+    const uint32_t near_top = top_of_stack() - start_free_memory - 250, // -250 to avoid interrupt activity that's altered the stack.
                    j = near_top / (size + 1);
 
     SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
     for (uint32_t i = 1; i <= size; i++) {
-      char * const addr = ptr + i * j;
+      char * const addr = start_free_memory + i * j;
       *addr = i;
       SERIAL_ECHOPAIR("\nCorrupting address: ", hex_address(addr));
     }
@@ -268,7 +307,7 @@ inline void free_memory_pool_report(char * const ptr, const int32_t size) {
  * M100 I
  *  Init memory for the M100 tests. (Automatically applied on the first M100.)
  */
-inline void init_free_memory(char *ptr, int32_t size) {
+inline void init_free_memory(char *start_free_memory, int32_t size) {
   SERIAL_ECHOLNPGM("Initializing free memory block.\n\n");
 
   size -= 250;    // -250 to avoid interrupt activity that's altered the stack.
@@ -277,17 +316,17 @@ inline void init_free_memory(char *ptr, int32_t size) {
     return;
   }
 
-  ptr += 8;       // move a few bytes away from the heap just because we don't want
+  start_free_memory += 8;       // move a few bytes away from the heap just because we don't want
                   // to be altering memory that close to it.
-  memset(ptr, TEST_BYTE, size);
+  memset(start_free_memory, TEST_BYTE, size);
 
   SERIAL_ECHO(size);
   SERIAL_ECHOLNPGM(" bytes of memory initialized.\n");
 
   for (int32_t i = 0; i < size; i++) {
-    if (ptr[i] != TEST_BYTE) {
-      SERIAL_ECHOPAIR("? address : ", hex_address(ptr + i));
-      SERIAL_ECHOLNPAIR("=", hex_byte(ptr[i]));
+    if (start_free_memory[i] != TEST_BYTE) {
+      SERIAL_ECHOPAIR("? address : ", hex_address(start_free_memory + i));
+      SERIAL_ECHOLNPAIR("=", hex_byte(start_free_memory[i]));
       SERIAL_EOL();
     }
   }
@@ -297,33 +336,36 @@ inline void init_free_memory(char *ptr, int32_t size) {
  * M100: Free Memory Check
  */
 void GcodeSuite::M100() {
-  SERIAL_ECHOPAIR("\n__brkval : ", hex_address(__brkval));
-  SERIAL_ECHOPAIR("\n__bss_end : ", hex_address(&__bss_end));
 
-  char *ptr = END_OF_HEAP(), *sp = top_of_stack();
-
-  SERIAL_ECHOPAIR("\nstart of free space : ", hex_address(ptr));
-  SERIAL_ECHOLNPAIR("\nStack Pointer : ", hex_address(sp));
+  char *sp = top_of_stack();
+  if (!free_memory_end) free_memory_end = sp - MEMORY_END_CORRECTION;
+  SERIAL_ECHOPAIR("\nbss_end               : ", hex_address(end_bss));
+  if (heaplimit) SERIAL_ECHOPAIR("\n__heaplimit           : ", hex_address(heaplimit ));
+  SERIAL_ECHOPAIR("\nfree_memory_start     : ", hex_address(free_memory_start));
+  if (stacklimit) SERIAL_ECHOPAIR("\n__stacklimit          : ", hex_address(stacklimit));
+  SERIAL_ECHOPAIR("\nfree_memory_end       : ", hex_address(free_memory_end  ));
+  if (MEMORY_END_CORRECTION)  SERIAL_ECHOPAIR("\nMEMORY_END_CORRECTION: ", MEMORY_END_CORRECTION );
+  SERIAL_ECHOLNPAIR("\nStack Pointer         : ", hex_address(sp));
 
   // Always init on the first invocation of M100
   static bool m100_not_initialized = true;
   if (m100_not_initialized || parser.seen('I')) {
     m100_not_initialized = false;
-    init_free_memory(ptr, sp - ptr);
+    init_free_memory(free_memory_start, free_memory_end - free_memory_start);
   }
 
   #if ENABLED(M100_FREE_MEMORY_DUMPER)
     if (parser.seen('D'))
-      return dump_free_memory(ptr, sp);
+      return dump_free_memory(free_memory_start, free_memory_end);
   #endif
 
   if (parser.seen('F'))
-    return free_memory_pool_report(ptr, sp - ptr);
+    return free_memory_pool_report(free_memory_start, free_memory_end - free_memory_start);
 
   #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
 
     if (parser.seen('C'))
-      return corrupt_free_memory(ptr, parser.value_int());
+      return corrupt_free_memory(free_memory_start, parser.value_int());
 
   #endif
 }