From ba85faabc04aae3f550b4f7d531a7d63f88964e9 Mon Sep 17 00:00:00 2001
From: Roxy-3D <Roxanne.Rae.Neufeld@gmail.com>
Date: Mon, 10 Apr 2017 15:20:20 -0500
Subject: [PATCH] Fix M100 Free Memory Checker

M100 had numerious changes and quit working.   Part of the problem is
the overloading of the SERIAL_PROTOCOL functions.   Also, some of the
address arithmatic was changed to use char *ptr and passing ptr into the
SERIAL_PROTOCOL functions caused them to try to print a string instead
of a number.     M100 is working again.   Let's keep it that way!

M100 has been expanded to now have a function  int
free_memory_is_corrupted()  that can be called from other code to see if
the free space is still contiguous.  It may make sense to add a flag to
control its verbose nature but right now, the extra chit chat is very
helpful to know int free_memory_is_corrupted()  is doing the right thing
and what it found at various points when it was called.     A 'Show &
Tell' is coming up with int free_memory_is_corrupted().
---
 Marlin/M100_Free_Mem_Chk.cpp | 100 ++++++++++++++++++++++++-----------
 1 file changed, 68 insertions(+), 32 deletions(-)

diff --git a/Marlin/M100_Free_Mem_Chk.cpp b/Marlin/M100_Free_Mem_Chk.cpp
index d0d0bf6bb2..01709c5d28 100644
--- a/Marlin/M100_Free_Mem_Chk.cpp
+++ b/Marlin/M100_Free_Mem_Chk.cpp
@@ -55,7 +55,8 @@ extern char __bss_end;
 
 char* top_of_stack();
 int how_many_E5s_are_here(char*);
-
+int free_memory_is_corrupted();         // int not bool!!!!  it will tell us how many blocks of
+                                        // free memory it found.
 void gcode_M100() {
   static bool m100_not_initialized = true;
   char* sp, *ptr;
@@ -69,18 +70,26 @@ void gcode_M100() {
   // probably caused by bad pointers.  Any unexpected values will be flagged in
   // the right hand column to help spotting them.
   //
+    SERIAL_ECHOPAIR("\n__brkval : 0x", hex_word((uint16_t)__brkval) );
+    SERIAL_ECHOPAIR("\n__bss_end : 0x", hex_word((uint16_t)&__bss_end));
+  //
+  // With out malloc() we need to be smart and use &__bss_end
+  //
+    ptr = __brkval ? __brkval : &__bss_end;    
+    SERIAL_ECHOPAIR("\nstart of free space : 0x", hex_word((uint16_t)ptr));
+
+    sp = top_of_stack();
+    SERIAL_ECHOLNPAIR("\nStack Pointer : 0x", hex_word((uint16_t)sp));
+
   #if ENABLED(M100_FREE_MEMORY_DUMPER) // Disable to remove Dump sub-command
     if (code_seen('D')) {
-      ptr = __brkval ? __brkval : &__bss_end;
       //
       // We want to start and end the dump on a nice 16 byte boundry even though
       // the values we are using are not 16 byte aligned.
       //
-      SERIAL_ECHOPAIR("\nbss_end : 0x", hex_word((uint16_t)ptr));
-      ptr = (char*)((uint32_t)ptr & 0xfff0);
-      sp = top_of_stack();
-      SERIAL_ECHOLNPAIR("\nStack Pointer : 0x", hex_word((uint16_t)sp));
-      sp = (char*)((uint32_t)sp | 0x000f);
+      ptr = (char*) ((uint16_t) ptr & 0xfff0);
+      sp  = (char*) ((uint16_t) sp  | 0x000f);
+
       n = sp - ptr;
       //
       // This is the main loop of the Dump command.
@@ -89,6 +98,8 @@ void gcode_M100() {
         print_hex_word((uint16_t)ptr); // Print the address
         SERIAL_CHAR(':');
         for (i = 0; i < 16; i++) {      // and 16 data bytes
+          if (i==8)
+            SERIAL_CHAR('-');
           print_hex_byte(*(ptr + i));
           SERIAL_CHAR(' ');
         }
@@ -97,6 +108,7 @@ void gcode_M100() {
           SERIAL_CHAR((*(ptr + i) == (char)0xe5) ? ' ' : '?');
         SERIAL_EOL;
         ptr += 16;
+        idle();
       }
       return;
     }
@@ -106,11 +118,8 @@ void gcode_M100() {
   // other vital statistics that define the memory pool.
   //
   if (code_seen('F')) {
-    #if 0
-      int max_addr = (int)  __brkval ? __brkval : &__bss_end;
-      int max_cnt = 0;
-    #endif
-    int block_cnt = 0;
+    int max_cnt = -1, block_cnt = 0;
+    uint16_t max_addr=0;
     ptr =  __brkval ? __brkval : &__bss_end;
     sp = top_of_stack();
     n = sp - ptr;
@@ -121,19 +130,21 @@ void gcode_M100() {
         if (j > 8) {
           SERIAL_ECHOPAIR("Found ", j);
           SERIAL_ECHOLNPAIR(" bytes free at 0x", hex_word((uint16_t)(ptr + i)));
+          if (j > max_cnt) {  
+            max_cnt  = j;    
+            max_addr = (uint16_t) ptr + i;
+          }
           i += j;
           block_cnt++;
         }
-        #if 0
-          if (j > max_cnt) {      // We don't do anything with this information yet
-            max_cnt  = j;     // but we do know where the biggest free memory block is.
-            max_addr = (int) ptr + i;
-          }
-        #endif
       }
     }
-    if (block_cnt > 1)
+    if (block_cnt > 1) {
       SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
+      SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
+      SERIAL_ECHOLNPAIR(" bytes big at 0x", hex_word(max_addr));
+    }
+    SERIAL_ECHOLNPAIR("free_memory_is_corrupted() = ", free_memory_is_corrupted()); 
     return;
   }
   //
@@ -144,14 +155,10 @@ void gcode_M100() {
     if (code_seen('C')) {
       int x = code_value_int(); // x gets the # of locations to corrupt within the memory pool
       SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
-      ptr = __brkval ? __brkval : &__bss_end;
-      SERIAL_ECHOPAIR("\nbss_end : ", ptr);
       ptr += 8;
       sp = top_of_stack();
-      SERIAL_ECHOPAIR("\nStack Pointer : ", sp);
-      SERIAL_ECHOLNPGM("\n");
-      n = sp - ptr - 64;    // -64 just to keep us from finding interrupt activity that
-      // has altered the stack.
+      n = sp - ptr - 250;    // -250 just to keep us from finding interrupt activity that
+                            // has altered the stack.
       j = n / (x + 1);
       for (i = 1; i <= x; i++) {
         *(ptr + (i * j)) = i;
@@ -167,13 +174,11 @@ void gcode_M100() {
   //
   if (m100_not_initialized || code_seen('I')) {            // If no sub-command is specified, the first time
     SERIAL_ECHOLNPGM("Initializing free memory block.\n"); // this happens, it will Initialize.
-    ptr = __brkval ? __brkval : &__bss_end;                // Repeated M100 with no sub-command will not destroy the
-    SERIAL_ECHOPAIR("\nbss_end : ", ptr);                  // state of the initialized free memory pool.
+                                                           // Repeated M100 with no sub-command will not destroy the
+                                                           // state of the initialized free memory pool.
     ptr += 8;
-    sp = top_of_stack();
-    SERIAL_ECHOPAIR("\nStack Pointer : ", sp);
     SERIAL_ECHOLNPGM("\n");
-    n = sp - ptr - 64;    // -64 just to keep us from finding interrupt activity that
+    n = sp - ptr - 250;    // -250 just to keep us from finding interrupt activity that
     // has altered the stack.
     SERIAL_ECHO(n);
     SERIAL_ECHOLNPGM(" bytes of memory initialized.\n");
@@ -181,8 +186,8 @@ void gcode_M100() {
       *(ptr + i) = (char)0xe5;
     for (i = 0; i < n; i++) {
       if (*(ptr + i) != (char)0xe5) {
-        SERIAL_ECHOPAIR("? address : ", ptr + i);
-        SERIAL_ECHOPAIR("=", *(ptr + i));
+        SERIAL_ECHOPAIR("? address : ", hex_word(ptr+i) );
+        SERIAL_ECHOPAIR("=", hex_byte(*(ptr + i)) );
         SERIAL_ECHOLNPGM("\n");
       }
     }
@@ -212,5 +217,36 @@ int how_many_E5s_are_here(char* p) {
   return -1;
 }
 
+
+int free_memory_is_corrupted() {
+  char *sp, *ptr;
+  int block_cnt = 0, i, j, n;
+
+    ptr = __brkval ? __brkval : &__bss_end;    
+    sp = top_of_stack();
+
+    n = sp - ptr;
+
+    // Scan through the range looking for the biggest block of 0xE5's we can find
+    for (i = 0; i < n; i++) {
+      if (*(ptr + i) == (char)0xe5) {
+        j = how_many_E5s_are_here(ptr + i);
+        if (j > 8) {
+//        SERIAL_ECHOPAIR("Found ", j);
+//        SERIAL_ECHOLNPAIR(" bytes free at 0x", hex_word((uint16_t)(ptr + i)));
+
+          i += j;
+          block_cnt++;
+        }
+      }
+    }
+
+//  if (block_cnt > 1) {
+//    SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
+//   SERIAL_ECHOLNPAIR("\nLargest free block is ", max_cnt);
+//  }
+    return block_cnt;
+  }
+
 #endif