diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp
index dcd8599af02c6a8ff16ecb09ab294a31cde1e3bd..25cb7344419e36b9a051ed3acb4d7d852fcef52f 100644
--- a/Marlin/src/Marlin.cpp
+++ b/Marlin/src/Marlin.cpp
@@ -272,24 +272,6 @@ static millis_t stepper_inactive_time = (DEFAULT_STEPPER_DEACTIVE_TIME) * 1000UL
           baricuda_e_to_p_pressure = 0;
 #endif
 
-#if ENABLED(FWRETRACT)                      // Initialized by settings.load()...
-  bool autoretract_enabled,                 // M209 S - Autoretract switch
-       retracted[EXTRUDERS] = { false };    // Which extruders are currently retracted
-  float retract_length,                     // M207 S - G10 Retract length
-        retract_feedrate_mm_s,              // M207 F - G10 Retract feedrate
-        retract_zlift,                      // M207 Z - G10 Retract hop size
-        retract_recover_length,             // M208 S - G11 Recover length
-        retract_recover_feedrate_mm_s,      // M208 F - G11 Recover feedrate
-        swap_retract_length,                // M207 W - G10 Swap Retract length
-        swap_retract_recover_length,        // M208 W - G11 Swap Recover length
-        swap_retract_recover_feedrate_mm_s; // M208 R - G11 Swap Recover feedrate
-  #if EXTRUDERS > 1
-    bool retracted_swap[EXTRUDERS] = { false }; // Which extruders are swap-retracted
-  #else
-    constexpr bool retracted_swap[1] = { false };
-  #endif
-#endif // FWRETRACT
-
 #if HAS_POWER_SWITCH
   bool powersupply_on =
     #if ENABLED(PS_DEFAULT_OFF)
@@ -2322,132 +2304,6 @@ static void homeaxis(const AxisEnum axis) {
   #endif
 } // homeaxis()
 
-#if ENABLED(FWRETRACT)
-
-  /**
-   * Retract or recover according to firmware settings
-   *
-   * This function handles retract/recover moves for G10 and G11,
-   * plus auto-retract moves sent from G0/G1 when E-only moves are done.
-   *
-   * To simplify the logic, doubled retract/recover moves are ignored.
-   *
-   * Note: Z lift is done transparently to the planner. Aborting
-   *       a print between G10 and G11 may corrupt the Z position.
-   *
-   * Note: Auto-retract will apply the set Z hop in addition to any Z hop
-   *       included in the G-code. Use M207 Z0 to to prevent double hop.
-   */
-  void retract(const bool retracting
-    #if EXTRUDERS > 1
-      , bool swapping = false
-    #endif
-  ) {
-
-    static float hop_height,        // Remember where the Z height started
-                 hop_amount = 0.0;  // Total amount lifted, for use in recover
-
-    // Simply never allow two retracts or recovers in a row
-    if (retracted[active_extruder] == retracting) return;
-
-    #if EXTRUDERS < 2
-      bool swapping = false;
-    #endif
-    if (!retracting) swapping = retracted_swap[active_extruder];
-
-    /* // debugging
-      SERIAL_ECHOLNPAIR("retracting ", retracting);
-      SERIAL_ECHOLNPAIR("swapping ", swapping);
-      SERIAL_ECHOLNPAIR("active extruder ", active_extruder);
-      for (uint8_t i = 0; i < EXTRUDERS; ++i) {
-        SERIAL_ECHOPAIR("retracted[", i);
-        SERIAL_ECHOLNPAIR("] ", retracted[i]);
-        SERIAL_ECHOPAIR("retracted_swap[", i);
-        SERIAL_ECHOLNPAIR("] ", retracted_swap[i]);
-      }
-      SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]);
-      SERIAL_ECHOLNPAIR("hop_amount ", hop_amount);
-    //*/
-
-    const bool has_zhop = retract_zlift > 0.01;     // Is there a hop set?
-
-    const float old_feedrate_mm_s = feedrate_mm_s;
-    const int16_t old_flow = flow_percentage[active_extruder];
-
-    // Don't apply flow multiplication to retract/recover
-    flow_percentage[active_extruder] = 100;
-
-    // The current position will be the destination for E and Z moves
-    set_destination_to_current();
-
-    if (retracting) {
-      // Remember the Z height since G-code may include its own Z-hop
-      // For best results turn off Z hop if G-code already includes it
-      hop_height = destination[Z_AXIS];
-
-      // Retract by moving from a faux E position back to the current E position
-      feedrate_mm_s = retract_feedrate_mm_s;
-      current_position[E_AXIS] += (swapping ? swap_retract_length : retract_length) / volumetric_multiplier[active_extruder];
-      sync_plan_position_e();
-      prepare_move_to_destination();
-
-      // Is a Z hop set, and has the hop not yet been done?
-      if (has_zhop) {
-        hop_amount += retract_zlift;                // Carriage is raised for retraction hop
-        current_position[Z_AXIS] -= retract_zlift;  // Pretend current pos is lower. Next move raises Z.
-        SYNC_PLAN_POSITION_KINEMATIC();             // Set the planner to the new position
-        prepare_move_to_destination();              // Raise up to the old current pos
-      }
-    }
-    else {
-      // If a hop was done and Z hasn't changed, undo the Z hop
-      if (hop_amount && NEAR(hop_height, destination[Z_AXIS])) {
-        current_position[Z_AXIS] += hop_amount;     // Pretend current pos is higher. Next move lowers Z.
-        SYNC_PLAN_POSITION_KINEMATIC();             // Set the planner to the new position
-        prepare_move_to_destination();              // Lower to the old current pos
-        hop_amount = 0.0;
-      }
-
-      // A retract multiplier has been added here to get faster swap recovery
-      feedrate_mm_s = swapping ? swap_retract_recover_feedrate_mm_s : retract_recover_feedrate_mm_s;
-
-      const float move_e = swapping ? swap_retract_length + swap_retract_recover_length : retract_length + retract_recover_length;
-      current_position[E_AXIS] -= move_e / volumetric_multiplier[active_extruder];
-      sync_plan_position_e();
-
-      prepare_move_to_destination();  // Recover E
-    }
-
-    // Restore flow and feedrate
-    flow_percentage[active_extruder] = old_flow;
-    feedrate_mm_s = old_feedrate_mm_s;
-
-    // The active extruder is now retracted or recovered
-    retracted[active_extruder] = retracting;
-
-    // If swap retract/recover then update the retracted_swap flag too
-    #if EXTRUDERS > 1
-      if (swapping) retracted_swap[active_extruder] = retracting;
-    #endif
-
-    /* // debugging
-      SERIAL_ECHOLNPAIR("retracting ", retracting);
-      SERIAL_ECHOLNPAIR("swapping ", swapping);
-      SERIAL_ECHOLNPAIR("active_extruder ", active_extruder);
-      for (uint8_t i = 0; i < EXTRUDERS; ++i) {
-        SERIAL_ECHOPAIR("retracted[", i);
-        SERIAL_ECHOLNPAIR("] ", retracted[i]);
-        SERIAL_ECHOPAIR("retracted_swap[", i);
-        SERIAL_ECHOLNPAIR("] ", retracted_swap[i]);
-      }
-      SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]);
-      SERIAL_ECHOLNPAIR("hop_amount ", hop_amount);
-    //*/
-
-  } // retract()
-
-#endif // FWRETRACT
-
 #if ENABLED(MIXING_EXTRUDER)
 
   void normalize_mix() {
@@ -2547,10 +2403,6 @@ void dwell(millis_t time) {
   #include "gcode/motion/G5.h"
 #endif
 
-#if ENABLED(FWRETRACT)
-  #include "gcode/feature/fwretract/G10_G11.h"
-#endif
-
 #if ENABLED(NOZZLE_CLEAN_FEATURE)
   #include "gcode/feature/clean/G12.h"
 #endif
diff --git a/Marlin/src/Marlin.h b/Marlin/src/Marlin.h
index cd131be933a7b68885ce6ec333d7c8b3fb88712c..f56dd4e1d7a0425a17b71e7765ea8aca0fbd21e3 100644
--- a/Marlin/src/Marlin.h
+++ b/Marlin/src/Marlin.h
@@ -302,18 +302,6 @@ extern float soft_endstop_min[XYZ], soft_endstop_max[XYZ];
   extern int lpq_len;
 #endif
 
-#if ENABLED(FWRETRACT)
-  extern bool autoretract_enabled;                 // M209 S - Autoretract switch
-  extern float retract_length,                     // M207 S - G10 Retract length
-               retract_feedrate_mm_s,              // M207 F - G10 Retract feedrate
-               retract_zlift,                      // M207 Z - G10 Retract hop size
-               retract_recover_length,             // M208 S - G11 Recover length
-               retract_recover_feedrate_mm_s,      // M208 F - G11 Recover feedrate
-               swap_retract_length,                // M207 W - G10 Swap Retract length
-               swap_retract_recover_length,        // M208 W - G11 Swap Recover length
-               swap_retract_recover_feedrate_mm_s; // M208 R - G11 Swap Recover feedrate
-#endif
-
 // Print job timer
 #if ENABLED(PRINTCOUNTER)
   extern PrintCounter print_job_timer;
diff --git a/Marlin/src/feature/fwretract.cpp b/Marlin/src/feature/fwretract.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..60191f84e81cc9f3070775f3c6ba0c8b7100524e
--- /dev/null
+++ b/Marlin/src/feature/fwretract.cpp
@@ -0,0 +1,185 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * fwretract.cpp - Implement firmware-based retraction
+ */
+
+#include "../inc/MarlinConfig.h"
+
+#if ENABLED(FWRETRACT)
+
+#include "fwretract.h"
+
+FWRetract fwretract; // Single instance
+
+#include "../module/motion.h"
+
+bool FWRetract::autoretract_enabled,                 // M209 S - Autoretract switch
+     FWRetract::retracted[EXTRUDERS] = { false };    // Which extruders are currently retracted
+float FWRetract::retract_length,                     // M207 S - G10 Retract length
+      FWRetract::retract_feedrate_mm_s,              // M207 F - G10 Retract feedrate
+      FWRetract::retract_zlift,                      // M207 Z - G10 Retract hop size
+      FWRetract::retract_recover_length,             // M208 S - G11 Recover length
+      FWRetract::retract_recover_feedrate_mm_s,      // M208 F - G11 Recover feedrate
+      FWRetract::swap_retract_length,                // M207 W - G10 Swap Retract length
+      FWRetract::swap_retract_recover_length,        // M208 W - G11 Swap Recover length
+      FWRetract::swap_retract_recover_feedrate_mm_s; // M208 R - G11 Swap Recover feedrate
+#if EXTRUDERS > 1
+  bool FWRetract::retracted_swap[EXTRUDERS] = { false }; // Which extruders are swap-retracted
+#endif
+
+void FWRetract::reset() {
+  autoretract_enabled = false;
+  retract_length = RETRACT_LENGTH;
+  retract_feedrate_mm_s = RETRACT_FEEDRATE;
+  retract_zlift = RETRACT_ZLIFT;
+  retract_recover_length = RETRACT_RECOVER_LENGTH;
+  retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE;
+  swap_retract_length = RETRACT_LENGTH_SWAP;
+  swap_retract_recover_length = RETRACT_RECOVER_LENGTH_SWAP;
+  swap_retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE_SWAP;
+}
+
+/**
+ * Retract or recover according to firmware settings
+ *
+ * This function handles retract/recover moves for G10 and G11,
+ * plus auto-retract moves sent from G0/G1 when E-only moves are done.
+ *
+ * To simplify the logic, doubled retract/recover moves are ignored.
+ *
+ * Note: Z lift is done transparently to the planner. Aborting
+ *       a print between G10 and G11 may corrupt the Z position.
+ *
+ * Note: Auto-retract will apply the set Z hop in addition to any Z hop
+ *       included in the G-code. Use M207 Z0 to to prevent double hop.
+ */
+void FWRetract::retract(const bool retracting
+  #if EXTRUDERS > 1
+    , bool swapping /* =false */
+  #endif
+) {
+
+  static float hop_height,        // Remember where the Z height started
+               hop_amount = 0.0;  // Total amount lifted, for use in recover
+
+  // Simply never allow two retracts or recovers in a row
+  if (retracted[active_extruder] == retracting) return;
+
+  #if EXTRUDERS < 2
+    bool swapping = false;
+  #endif
+  if (!retracting) swapping = retracted_swap[active_extruder];
+
+  /* // debugging
+    SERIAL_ECHOLNPAIR("retracting ", retracting);
+    SERIAL_ECHOLNPAIR("swapping ", swapping);
+    SERIAL_ECHOLNPAIR("active extruder ", active_extruder);
+    for (uint8_t i = 0; i < EXTRUDERS; ++i) {
+      SERIAL_ECHOPAIR("retracted[", i);
+      SERIAL_ECHOLNPAIR("] ", retracted[i]);
+      SERIAL_ECHOPAIR("retracted_swap[", i);
+      SERIAL_ECHOLNPAIR("] ", retracted_swap[i]);
+    }
+    SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]);
+    SERIAL_ECHOLNPAIR("hop_amount ", hop_amount);
+  //*/
+
+  const bool has_zhop = retract_zlift > 0.01;     // Is there a hop set?
+
+  const float old_feedrate_mm_s = feedrate_mm_s;
+  const int16_t old_flow = flow_percentage[active_extruder];
+
+  // Don't apply flow multiplication to retract/recover
+  flow_percentage[active_extruder] = 100;
+
+  // The current position will be the destination for E and Z moves
+  set_destination_to_current();
+
+  if (retracting) {
+    // Remember the Z height since G-code may include its own Z-hop
+    // For best results turn off Z hop if G-code already includes it
+    hop_height = destination[Z_AXIS];
+
+    // Retract by moving from a faux E position back to the current E position
+    feedrate_mm_s = retract_feedrate_mm_s;
+    current_position[E_AXIS] += (swapping ? swap_retract_length : retract_length) / volumetric_multiplier[active_extruder];
+    sync_plan_position_e();
+    prepare_move_to_destination();
+
+    // Is a Z hop set, and has the hop not yet been done?
+    if (has_zhop) {
+      hop_amount += retract_zlift;                // Carriage is raised for retraction hop
+      current_position[Z_AXIS] -= retract_zlift;  // Pretend current pos is lower. Next move raises Z.
+      SYNC_PLAN_POSITION_KINEMATIC();             // Set the planner to the new position
+      prepare_move_to_destination();              // Raise up to the old current pos
+    }
+  }
+  else {
+    // If a hop was done and Z hasn't changed, undo the Z hop
+    if (hop_amount && NEAR(hop_height, destination[Z_AXIS])) {
+      current_position[Z_AXIS] += hop_amount;     // Pretend current pos is higher. Next move lowers Z.
+      SYNC_PLAN_POSITION_KINEMATIC();             // Set the planner to the new position
+      prepare_move_to_destination();              // Lower to the old current pos
+      hop_amount = 0.0;
+    }
+
+    // A retract multiplier has been added here to get faster swap recovery
+    feedrate_mm_s = swapping ? swap_retract_recover_feedrate_mm_s : retract_recover_feedrate_mm_s;
+
+    const float move_e = swapping ? swap_retract_length + swap_retract_recover_length : retract_length + retract_recover_length;
+    current_position[E_AXIS] -= move_e / volumetric_multiplier[active_extruder];
+    sync_plan_position_e();
+
+    prepare_move_to_destination();  // Recover E
+  }
+
+  // Restore flow and feedrate
+  flow_percentage[active_extruder] = old_flow;
+  feedrate_mm_s = old_feedrate_mm_s;
+
+  // The active extruder is now retracted or recovered
+  retracted[active_extruder] = retracting;
+
+  // If swap retract/recover then update the retracted_swap flag too
+  #if EXTRUDERS > 1
+    if (swapping) retracted_swap[active_extruder] = retracting;
+  #endif
+
+  /* // debugging
+    SERIAL_ECHOLNPAIR("retracting ", retracting);
+    SERIAL_ECHOLNPAIR("swapping ", swapping);
+    SERIAL_ECHOLNPAIR("active_extruder ", active_extruder);
+    for (uint8_t i = 0; i < EXTRUDERS; ++i) {
+      SERIAL_ECHOPAIR("retracted[", i);
+      SERIAL_ECHOLNPAIR("] ", retracted[i]);
+      SERIAL_ECHOPAIR("retracted_swap[", i);
+      SERIAL_ECHOLNPAIR("] ", retracted_swap[i]);
+    }
+    SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]);
+    SERIAL_ECHOLNPAIR("hop_amount ", hop_amount);
+  //*/
+
+} // retract()
+
+#endif // FWRETRACT
diff --git a/Marlin/src/feature/fwretract.h b/Marlin/src/feature/fwretract.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed6b50d062bf9119dfb971f57c7608f1f7758d5a
--- /dev/null
+++ b/Marlin/src/feature/fwretract.h
@@ -0,0 +1,65 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * fwretract.h - Define firmware-based retraction interface
+ */
+
+#ifndef FWRETRACT_H
+#define FWRETRACT_H
+
+#include "../inc/MarlinConfig.h"
+
+class FWRetract {
+public:
+  static bool autoretract_enabled,                 // M209 S - Autoretract switch
+              retracted[EXTRUDERS];                // Which extruders are currently retracted
+  static float retract_length,                     // M207 S - G10 Retract length
+               retract_feedrate_mm_s,              // M207 F - G10 Retract feedrate
+               retract_zlift,                      // M207 Z - G10 Retract hop size
+               retract_recover_length,             // M208 S - G11 Recover length
+               retract_recover_feedrate_mm_s,      // M208 F - G11 Recover feedrate
+               swap_retract_length,                // M207 W - G10 Swap Retract length
+               swap_retract_recover_length,        // M208 W - G11 Swap Recover length
+               swap_retract_recover_feedrate_mm_s; // M208 R - G11 Swap Recover feedrate
+
+  #if EXTRUDERS > 1
+    static bool retracted_swap[EXTRUDERS];         // Which extruders are swap-retracted
+  #else
+    static bool constexpr retracted_swap[1] = { false };
+  #endif
+
+  FWRetract() {}
+
+  static void reset();
+
+  static void retract(const bool retracting
+    #if EXTRUDERS > 1
+      , bool swapping = false
+    #endif
+  );
+
+};
+
+extern FWRetract fwretract;
+
+#endif // FWRETRACT_H
diff --git a/Marlin/src/gcode/feature/fwretract/G10_G11.h b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp
similarity index 74%
rename from Marlin/src/gcode/feature/fwretract/G10_G11.h
rename to Marlin/src/gcode/feature/fwretract/G10_G11.cpp
index be8df503953dfb3f01fa6b830d8e4376a5686e36..86c1037a75dd37b689436c08813fc26a7389ce23 100644
--- a/Marlin/src/gcode/feature/fwretract/G10_G11.h
+++ b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp
@@ -20,15 +20,23 @@
  *
  */
 
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(FWRETRACT)
+
+#include "../../../feature/fwretract.h"
+#include "../../gcode.h"
+#include "../../../module/motion.h"
+
 /**
  * G10 - Retract filament according to settings of M207
  */
-void gcode_G10() {
+void GcodeSuite::G10() {
   #if EXTRUDERS > 1
     const bool rs = parser.boolval('S');
-    retracted_swap[active_extruder] = rs; // Use 'S' for swap, default to false
+    fwretract.retracted_swap[active_extruder] = rs; // Use 'S' for swap, default to false
   #endif
-  retract(true
+  fwretract.retract(true
     #if EXTRUDERS > 1
       , rs
     #endif
@@ -38,4 +46,6 @@ void gcode_G10() {
 /**
  * G11 - Recover filament according to settings of M208
  */
-void gcode_G11() { retract(false); }
+void GcodeSuite::G11() { fwretract.retract(false); }
+
+#endif // FWRETRACT
diff --git a/Marlin/src/gcode/feature/fwretract/M207.h b/Marlin/src/gcode/feature/fwretract/M207.cpp
similarity index 66%
rename from Marlin/src/gcode/feature/fwretract/M207.h
rename to Marlin/src/gcode/feature/fwretract/M207.cpp
index 07ca82e334a605a91a47760badd6bbdf5a0938e3..07a043fafecfa5da34076388d46fe7e064885d88 100644
--- a/Marlin/src/gcode/feature/fwretract/M207.h
+++ b/Marlin/src/gcode/feature/fwretract/M207.cpp
@@ -20,6 +20,13 @@
  *
  */
 
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(FWRETRACT)
+
+#include "../../../feature/fwretract.h"
+#include "../../gcode.h"
+
 /**
  * M207: Set firmware retraction values
  *
@@ -28,9 +35,11 @@
  *   F[units/min] retract_feedrate_mm_s
  *   Z[units]     retract_zlift
  */
-void gcode_M207() {
-  if (parser.seen('S')) retract_length = parser.value_axis_units(E_AXIS);
-  if (parser.seen('F')) retract_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
-  if (parser.seen('Z')) retract_zlift = parser.value_linear_units();
-  if (parser.seen('W')) swap_retract_length = parser.value_axis_units(E_AXIS);
+void GcodeSuite::M207() {
+  if (parser.seen('S')) fwretract.retract_length = parser.value_axis_units(E_AXIS);
+  if (parser.seen('F')) fwretract.retract_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
+  if (parser.seen('Z')) fwretract.retract_zlift = parser.value_linear_units();
+  if (parser.seen('W')) fwretract.swap_retract_length = parser.value_axis_units(E_AXIS);
 }
+
+#endif // FWRETRACT
diff --git a/Marlin/src/gcode/feature/fwretract/M208.h b/Marlin/src/gcode/feature/fwretract/M208.cpp
similarity index 65%
rename from Marlin/src/gcode/feature/fwretract/M208.h
rename to Marlin/src/gcode/feature/fwretract/M208.cpp
index c5cabb71482f14e25ab01563fb8923c5483c490e..85d234f41471ed73cd8befcbdb3de960bd53f079 100644
--- a/Marlin/src/gcode/feature/fwretract/M208.h
+++ b/Marlin/src/gcode/feature/fwretract/M208.cpp
@@ -20,6 +20,13 @@
  *
  */
 
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(FWRETRACT)
+
+#include "../../../feature/fwretract.h"
+#include "../../gcode.h"
+
 /**
  * M208: Set firmware un-retraction values
  *
@@ -28,9 +35,11 @@
  *   F[units/min] retract_recover_feedrate_mm_s
  *   R[units/min] swap_retract_recover_feedrate_mm_s
  */
-void gcode_M208() {
-  if (parser.seen('S')) retract_recover_length = parser.value_axis_units(E_AXIS);
-  if (parser.seen('F')) retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
-  if (parser.seen('R')) swap_retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
-  if (parser.seen('W')) swap_retract_recover_length = parser.value_axis_units(E_AXIS);
+void GcodeSuite::M208() {
+  if (parser.seen('S')) fwretract.retract_recover_length = parser.value_axis_units(E_AXIS);
+  if (parser.seen('F')) fwretract.retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
+  if (parser.seen('R')) fwretract.swap_retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
+  if (parser.seen('W')) fwretract.swap_retract_recover_length = parser.value_axis_units(E_AXIS);
 }
+
+#endif // FWRETRACT
diff --git a/Marlin/src/gcode/feature/fwretract/M209.h b/Marlin/src/gcode/feature/fwretract/M209.cpp
similarity index 77%
rename from Marlin/src/gcode/feature/fwretract/M209.h
rename to Marlin/src/gcode/feature/fwretract/M209.cpp
index 9e92d100cca49dcccb6908255627409f565dd805..7d5a334b86979dd0c9275d0cc87d73dd964e95a0 100644
--- a/Marlin/src/gcode/feature/fwretract/M209.h
+++ b/Marlin/src/gcode/feature/fwretract/M209.cpp
@@ -20,16 +20,25 @@
  *
  */
 
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(FWRETRACT)
+
+#include "../../../feature/fwretract.h"
+#include "../../gcode.h"
+
 /**
  * M209: Enable automatic retract (M209 S1)
  *   For slicers that don't support G10/11, reversed extrude-only
  *   moves will be classified as retraction.
  */
-void gcode_M209() {
+void GcodeSuite::M209() {
   if (MIN_AUTORETRACT <= MAX_AUTORETRACT) {
     if (parser.seen('S')) {
-      autoretract_enabled = parser.value_bool();
-      for (uint8_t i = 0; i < EXTRUDERS; i++) retracted[i] = false;
+      fwretract.autoretract_enabled = parser.value_bool();
+      for (uint8_t i = 0; i < EXTRUDERS; i++) fwretract.retracted[i] = false;
     }
   }
 }
+
+#endif // FWRETRACT
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index 367725fe5cebc08bd333d9ec4a140e0b18432314..7fd4f0df3d6830e78950ab75931009fe9df63962 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -754,16 +754,10 @@ void GcodeSuite::process_next_command() {
       #endif
 
       #if ENABLED(FWRETRACT)
-        case 207: // M207: Set Retract Length, Feedrate, and Z lift
-          M207();
-          break;
-        case 208: // M208: Set Recover (unretract) Additional Length and Feedrate
-          M208();
-          break;
-        case 209: // M209: Turn Automatic Retract Detection on/off
-          if (MIN_AUTORETRACT <= MAX_AUTORETRACT) M209();
-          break;
-      #endif // FWRETRACT
+        case 207: M207(); break;  // M207: Set Retract Length, Feedrate, and Z lift
+        case 208: M208(); break;  // M208: Set Recover (unretract) Additional Length and Feedrate
+        case 209: if (MIN_AUTORETRACT <= MAX_AUTORETRACT) M209(); break;  // M209: Turn Automatic Retract Detection on/off
+      #endif
 
       case 211: // M211: Enable, Disable, and/or Report software endstops
         gcode_M211();
diff --git a/Marlin/src/gcode/motion/G0_G1.cpp b/Marlin/src/gcode/motion/G0_G1.cpp
index 4ec0221e4e8b02ad520b077d9bbcff4f8be6c946..d2fe3f98b25f3c069f7ccc7b875aa2ce30260c0e 100644
--- a/Marlin/src/gcode/motion/G0_G1.cpp
+++ b/Marlin/src/gcode/motion/G0_G1.cpp
@@ -25,6 +25,10 @@
 
 #include "../../Marlin.h"
 
+#if ENABLED(FWRETRACT)
+  #include "../../feature/fwretract.h"
+#endif
+
 #include "../../sd/cardreader.h"
 
 extern float destination[XYZE];
@@ -41,18 +45,20 @@ void GcodeSuite::G0_G1(
     get_destination_from_command(); // For X Y Z E F
 
     #if ENABLED(FWRETRACT)
+
       if (MIN_AUTORETRACT <= MAX_AUTORETRACT) {
         // When M209 Autoretract is enabled, convert E-only moves to firmware retract/recover moves
-        if (autoretract_enabled && parser.seen('E') && !(parser.seen('X') || parser.seen('Y') || parser.seen('Z'))) {
+        if (fwretract.autoretract_enabled && parser.seen('E') && !(parser.seen('X') || parser.seen('Y') || parser.seen('Z'))) {
           const float echange = destination[E_AXIS] - current_position[E_AXIS];
           // Is this a retract or recover move?
-          if (WITHIN(FABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && retracted[active_extruder] == (echange > 0.0)) {
+          if (WITHIN(FABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) {
             current_position[E_AXIS] = destination[E_AXIS]; // Hide a G1-based retract/recover from calculations
             sync_plan_position_e();                         // AND from the planner
-            return retract(echange < 0.0);                  // Firmware-based retract/recover (double-retract ignored)
+            return fwretract.retract(echange < 0.0);        // Firmware-based retract/recover (double-retract ignored)
           }
         }
       }
+
     #endif // FWRETRACT
 
     #if IS_SCARA
diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp
index 34012b4ba2d293542a6c09c28e49ff123d638075..72a2288c61cf1b9579756046aad5d2ca81b5dd84 100644
--- a/Marlin/src/lcd/ultralcd.cpp
+++ b/Marlin/src/lcd/ultralcd.cpp
@@ -164,6 +164,7 @@ uint16_t max_display_update_time = 0;
   #endif
 
   #if ENABLED(FWRETRACT)
+    #include "../feature/fwretract.h"
     void lcd_control_retract_menu();
   #endif
 
@@ -3477,18 +3478,18 @@ void kill_screen(const char* lcd_msg) {
     void lcd_control_retract_menu() {
       START_MENU();
       MENU_BACK(MSG_CONTROL);
-      MENU_ITEM_EDIT(bool, MSG_AUTORETRACT, &autoretract_enabled);
-      MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT, &retract_length, 0, 100);
+      MENU_ITEM_EDIT(bool, MSG_AUTORETRACT, &fwretract.autoretract_enabled);
+      MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT, &fwretract.retract_length, 0, 100);
       #if EXTRUDERS > 1
-        MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_SWAP, &swap_retract_length, 0, 100);
+        MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_SWAP, &fwretract.swap_retract_length, 0, 100);
       #endif
-      MENU_ITEM_EDIT(float3, MSG_CONTROL_RETRACTF, &retract_feedrate_mm_s, 1, 999);
-      MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_ZLIFT, &retract_zlift, 0, 999);
-      MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_RECOVER, &retract_recover_length, -100, 100);
+      MENU_ITEM_EDIT(float3, MSG_CONTROL_RETRACTF, &fwretract.retract_feedrate_mm_s, 1, 999);
+      MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_ZLIFT, &fwretract.retract_zlift, 0, 999);
+      MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_RECOVER, &fwretract.retract_recover_length, -100, 100);
       #if EXTRUDERS > 1
-        MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_RECOVER_SWAP, &swap_retract_recover_length, -100, 100);
+        MENU_ITEM_EDIT(float52, MSG_CONTROL_RETRACT_RECOVER_SWAP, &fwretract.swap_retract_recover_length, -100, 100);
       #endif
-      MENU_ITEM_EDIT(float3, MSG_CONTROL_RETRACT_RECOVERF, &retract_recover_feedrate_mm_s, 1, 999);
+      MENU_ITEM_EDIT(float3, MSG_CONTROL_RETRACT_RECOVERF, &fwretract.retract_recover_feedrate_mm_s, 1, 999);
       END_MENU();
     }
 
diff --git a/Marlin/src/module/configuration_store.cpp b/Marlin/src/module/configuration_store.cpp
index 362f629de73161e4cee29e1e0b4e5f7efea6c782..898c496cd1fe9744da965ac807f85f7e25002a0c 100644
--- a/Marlin/src/module/configuration_store.cpp
+++ b/Marlin/src/module/configuration_store.cpp
@@ -205,6 +205,10 @@ MarlinSettings settings;
   extern void refresh_bed_level();
 #endif
 
+#if ENABLED(FWRETRACT)
+  #include "../gcode/feature/fwretract/fwretract.h"
+#endif
+
 /**
  * Post-process after Retrieve or Reset
  */
@@ -492,24 +496,20 @@ void MarlinSettings::postprocess() {
 
     #if DISABLED(FWRETRACT)
       const bool autoretract_enabled = false;
-      const float retract_length = 3,
-                  retract_feedrate_mm_s = 45,
-                  retract_zlift = 0,
-                  retract_recover_length = 0,
-                  retract_recover_feedrate_mm_s = 0,
-                  swap_retract_length = 13,
-                  swap_retract_recover_length = 0,
-                  swap_retract_recover_feedrate_mm_s = 8;
+      const float autoretract_defaults[] = { 3, 45, 0, 0, 0, 13, 0, 8 };
+      EEPROM_WRITE(autoretract_enabled);
+      EEPROM_WRITE(autoretract_defaults);
+    #else
+      EEPROM_WRITE(fwretract.autoretract_enabled);
+      EEPROM_WRITE(fwretract.retract_length);
+      EEPROM_WRITE(fwretract.retract_feedrate_mm_s);
+      EEPROM_WRITE(fwretract.retract_zlift);
+      EEPROM_WRITE(fwretract.retract_recover_length);
+      EEPROM_WRITE(fwretract.retract_recover_feedrate_mm_s);
+      EEPROM_WRITE(fwretract.swap_retract_length);
+      EEPROM_WRITE(fwretract.swap_retract_recover_length);
+      EEPROM_WRITE(fwretract.swap_retract_recover_feedrate_mm_s);
     #endif
-    EEPROM_WRITE(autoretract_enabled);
-    EEPROM_WRITE(retract_length);
-    EEPROM_WRITE(retract_feedrate_mm_s);
-    EEPROM_WRITE(retract_zlift);
-    EEPROM_WRITE(retract_recover_length);
-    EEPROM_WRITE(retract_recover_feedrate_mm_s);
-    EEPROM_WRITE(swap_retract_length);
-    EEPROM_WRITE(swap_retract_recover_length);
-    EEPROM_WRITE(swap_retract_recover_feedrate_mm_s);
 
     EEPROM_WRITE(volumetric_enabled);
 
@@ -883,15 +883,15 @@ void MarlinSettings::postprocess() {
       EEPROM_READ(lcd_contrast);
 
       #if ENABLED(FWRETRACT)
-        EEPROM_READ(autoretract_enabled);
-        EEPROM_READ(retract_length);
-        EEPROM_READ(retract_feedrate_mm_s);
-        EEPROM_READ(retract_zlift);
-        EEPROM_READ(retract_recover_length);
-        EEPROM_READ(retract_recover_feedrate_mm_s);
-        EEPROM_READ(swap_retract_length);
-        EEPROM_READ(swap_retract_recover_length);
-        EEPROM_READ(swap_retract_recover_feedrate_mm_s);
+        EEPROM_READ(fwretract.autoretract_enabled);
+        EEPROM_READ(fwretract.retract_length);
+        EEPROM_READ(fwretract.retract_feedrate_mm_s);
+        EEPROM_READ(fwretract.retract_zlift);
+        EEPROM_READ(fwretract.retract_recover_length);
+        EEPROM_READ(fwretract.retract_recover_feedrate_mm_s);
+        EEPROM_READ(fwretract.swap_retract_length);
+        EEPROM_READ(fwretract.swap_retract_recover_length);
+        EEPROM_READ(fwretract.swap_retract_recover_feedrate_mm_s);
       #else
         EEPROM_READ(dummyb);
         for (uint8_t q=8; q--;) EEPROM_READ(dummy);
@@ -1256,16 +1256,8 @@ void MarlinSettings::reset() {
   #endif
 
   #if ENABLED(FWRETRACT)
-    autoretract_enabled = false;
-    retract_length = RETRACT_LENGTH;
-    retract_feedrate_mm_s = RETRACT_FEEDRATE;
-    retract_zlift = RETRACT_ZLIFT;
-    retract_recover_length = RETRACT_RECOVER_LENGTH;
-    retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE;
-    swap_retract_length = RETRACT_LENGTH_SWAP;
-    swap_retract_recover_length = RETRACT_RECOVER_LENGTH_SWAP;
-    swap_retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE_SWAP;
-  #endif // FWRETRACT
+    fwretract.reset();
+  #endif
 
   volumetric_enabled =
     #if ENABLED(VOLUMETRIC_DEFAULT_ON)
@@ -1716,26 +1708,26 @@ void MarlinSettings::reset() {
         SERIAL_ECHOLNPGM("Retract: S<length> F<units/m> Z<lift>");
       }
       CONFIG_ECHO_START;
-      SERIAL_ECHOPAIR("  M207 S", LINEAR_UNIT(retract_length));
-      SERIAL_ECHOPAIR(" W", LINEAR_UNIT(swap_retract_length));
-      SERIAL_ECHOPAIR(" F", MMS_TO_MMM(LINEAR_UNIT(retract_feedrate_mm_s)));
-      SERIAL_ECHOLNPAIR(" Z", LINEAR_UNIT(retract_zlift));
+      SERIAL_ECHOPAIR("  M207 S", LINEAR_UNIT(fwretract.retract_length));
+      SERIAL_ECHOPAIR(" W", LINEAR_UNIT(fwretract.swap_retract_length));
+      SERIAL_ECHOPAIR(" F", MMS_TO_MMM(LINEAR_UNIT(fwretract.retract_feedrate_mm_s)));
+      SERIAL_ECHOLNPAIR(" Z", LINEAR_UNIT(fwretract.retract_zlift));
 
       if (!forReplay) {
         CONFIG_ECHO_START;
         SERIAL_ECHOLNPGM("Recover: S<length> F<units/m>");
       }
       CONFIG_ECHO_START;
-      SERIAL_ECHOPAIR("  M208 S", LINEAR_UNIT(retract_recover_length));
-      SERIAL_ECHOPAIR(" W", LINEAR_UNIT(swap_retract_recover_length));
-      SERIAL_ECHOLNPAIR(" F", MMS_TO_MMM(LINEAR_UNIT(retract_recover_feedrate_mm_s)));
+      SERIAL_ECHOPAIR("  M208 S", LINEAR_UNIT(fwretract.retract_recover_length));
+      SERIAL_ECHOPAIR(" W", LINEAR_UNIT(fwretract.swap_retract_recover_length));
+      SERIAL_ECHOLNPAIR(" F", MMS_TO_MMM(LINEAR_UNIT(fwretract.retract_recover_feedrate_mm_s)));
 
       if (!forReplay) {
         CONFIG_ECHO_START;
         SERIAL_ECHOLNPGM("Auto-Retract: S=0 to disable, 1 to interpret E-only moves as retract/recover");
       }
       CONFIG_ECHO_START;
-      SERIAL_ECHOLNPAIR("  M209 S", autoretract_enabled ? 1 : 0);
+      SERIAL_ECHOLNPAIR("  M209 S", fwretract.autoretract_enabled ? 1 : 0);
 
     #endif // FWRETRACT