diff --git a/Marlin/src/feature/binary_protocol.h b/Marlin/src/feature/binary_protocol.h
index c558a3eeae111dd710b4d02bc8b0efb44a759648..0025fbe138096df17c346f54a78c91997be461b1 100644
--- a/Marlin/src/feature/binary_protocol.h
+++ b/Marlin/src/feature/binary_protocol.h
@@ -77,7 +77,7 @@ private:
   static bool file_open(char* filename) {
     if (!dummy_transfer) {
       card.mount();
-      card.openFile(filename, false);
+      card.openFileWrite(filename);
       if (!card.isFileOpen()) return false;
     }
     transfer_active = true;
diff --git a/Marlin/src/gcode/sdcard/M23.cpp b/Marlin/src/gcode/sdcard/M23.cpp
index f170345c57ad62e18728d4b6b9487f2090833d4d..7dd4ad9203ab77fd6d24af644f3aeb3e5793b574 100644
--- a/Marlin/src/gcode/sdcard/M23.cpp
+++ b/Marlin/src/gcode/sdcard/M23.cpp
@@ -36,7 +36,7 @@
 void GcodeSuite::M23() {
   // Simplify3D includes the size, so zero out all spaces (#7227)
   for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0';
-  card.openFile(parser.string_arg, true);
+  card.openFileRead(parser.string_arg);
 
   #if ENABLED(LCD_SET_PROGRESS_MANUALLY)
     ui.set_progress(0);
diff --git a/Marlin/src/gcode/sdcard/M28_M29.cpp b/Marlin/src/gcode/sdcard/M28_M29.cpp
index e23b0b4a146f152934326a81f132e8d8d3924769..c64ce7bb8639459da23c57233054c281be73f49a 100644
--- a/Marlin/src/gcode/sdcard/M28_M29.cpp
+++ b/Marlin/src/gcode/sdcard/M28_M29.cpp
@@ -54,11 +54,11 @@ void GcodeSuite::M28() {
       #endif
     }
     else
-      card.openFile(p, false);
+      card.openFileWrite(p);
 
   #else
 
-    card.openFile(parser.string_arg, false);
+    card.openFileWrite(parser.string_arg);
 
   #endif
 }
diff --git a/Marlin/src/gcode/sdcard/M32.cpp b/Marlin/src/gcode/sdcard/M32.cpp
index 7b180f441599fb1e000d83a87ca928811cc48caf..559d1d503bf7828c800ffa3b92d4101e664ad055 100644
--- a/Marlin/src/gcode/sdcard/M32.cpp
+++ b/Marlin/src/gcode/sdcard/M32.cpp
@@ -26,8 +26,7 @@
 
 #include "../gcode.h"
 #include "../../sd/cardreader.h"
-#include "../../module/printcounter.h"
-#include "../../module/planner.h"
+#include "../../module/planner.h" // for synchronize()
 
 #include "../../Marlin.h" // for startOrResumeJob
 
@@ -45,9 +44,9 @@ void GcodeSuite::M32() {
   if (IS_SD_PRINTING()) planner.synchronize();
 
   if (card.isMounted()) {
-    const bool call_procedure = parser.boolval('P');
+    const uint8_t call_procedure = parser.boolval('P');
 
-    card.openFile(parser.string_arg, true, call_procedure);
+    card.openFileRead(parser.string_arg, call_procedure);
 
     if (parser.seenval('S')) card.setIndex(parser.value_long());
 
diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp
index a0cb19b95d8a720db6857989d1cc422d481c2501..7a01d3622f6d9f01ce8c7111fe0d9e9406c2fbb6 100644
--- a/Marlin/src/sd/cardreader.cpp
+++ b/Marlin/src/sd/cardreader.cpp
@@ -418,7 +418,7 @@ void CardReader::stopSDPrint(
 
 void CardReader::openLogFile(char * const path) {
   flag.logging = true;
-  openFile(path, false);
+  openFileWrite(path);
 }
 
 //
@@ -444,16 +444,42 @@ void CardReader::getAbsFilename(char *dst) {
   *dst = '\0';
 }
 
+void openFailed(const char * const fname) {
+  SERIAL_ECHOLNPAIR(MSG_SD_OPEN_FILE_FAIL, fname, ".");
+}
+
+void announceOpen(const uint8_t doing, const char * const path) {
+  if (doing) {
+    SERIAL_ECHO_START();
+    SERIAL_ECHOPGM("Now ");
+    serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh"));
+    SERIAL_ECHOLNPAIR(" file: ", path);
+  }
+}
+
 //
-// Open a file by DOS path - for read or write
+// Open a file by DOS path for read
+// The 'subcall_type' flag indicates...
+//   - 0 : Standard open from host or user interface.
+//   - 1 : (file open) Opening a new sub-procedure.
+//   - 1 : (no file open) Opening a macro (M98).
+//   - 2 : Resuming from a sub-procedure
 //
-void CardReader::openFile(char * const path, const bool read, const bool subcall/*=false*/) {
-
+void CardReader::openFileRead(char * const path, const uint8_t subcall_type/*=0*/) {
   if (!isMounted()) return;
 
-  uint8_t doing = 0;
-  if (isFileOpen()) {                     // Replacing current file or doing a subroutine
-    if (subcall) {
+  switch (subcall_type) {
+    case 0:      // Starting a new print. "Now fresh file: ..."
+      announceOpen(2, path);
+      file_subcall_ctr = 0;
+      break;
+
+    case 1:      // Starting a sub-procedure
+
+      // With no file is open it's a simple macro. "Now doing file: ..."
+      if (!isFileOpen()) { announceOpen(1, path); break; }
+
+      // Too deep? The firmware has to bail.
       if (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) {
         SERIAL_ERROR_MSG("trying to call sub-gcode files with too many levels. MAX level is:" STRINGIFY(SD_PROCEDURE_DEPTH));
         kill();
@@ -464,25 +490,15 @@ void CardReader::openFile(char * const path, const bool read, const bool subcall
       getAbsFilename(proc_filenames[file_subcall_ctr]);
       filespos[file_subcall_ctr] = sdpos;
 
+      // For sub-procedures say 'SUBROUTINE CALL target: "..." parent: "..." pos12345'
       SERIAL_ECHO_START();
       SERIAL_ECHOLNPAIR("SUBROUTINE CALL target:\"", path, "\" parent:\"", proc_filenames[file_subcall_ctr], "\" pos", sdpos);
       file_subcall_ctr++;
-    }
-    else
-      doing = 1;
-  }
-  else if (subcall)       // Returning from a subcall?
-    SERIAL_ECHO_MSG("END SUBROUTINE");
-  else {                  // Opening fresh file
-    doing = 2;
-    file_subcall_ctr = 0; // Reset procedure depth in case user cancels print while in procedure
-  }
+      break;
 
-  if (doing) {
-    SERIAL_ECHO_START();
-    SERIAL_ECHOPGM("Now ");
-    serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh"));
-    SERIAL_ECHOLNPAIR(" file: ", path);
+    case 2:      // Resuming previous file after sub-procedure
+      SERIAL_ECHO_MSG("END SUBROUTINE");
+      break;
   }
 
   stopSDPrint();
@@ -491,35 +507,45 @@ void CardReader::openFile(char * const path, const bool read, const bool subcall
   const char * const fname = diveToFile(curDir, path);
   if (!fname) return;
 
-  if (read) {
-    if (file.open(curDir, fname, O_READ)) {
-      filesize = file.fileSize();
-      sdpos = 0;
-      SERIAL_ECHOLNPAIR(MSG_SD_FILE_OPENED, fname, MSG_SD_SIZE, filesize);
-      SERIAL_ECHOLNPGM(MSG_SD_FILE_SELECTED);
-
-      selectFileByName(fname);
-      ui.set_status(longFilename[0] ? longFilename : fname);
-      //if (longFilename[0]) {
-      //  SERIAL_ECHOPAIR(MSG_SD_FILE_LONG_NAME, longFilename);
-      //}
-    }
-    else
-      SERIAL_ECHOLNPAIR(MSG_SD_OPEN_FILE_FAIL, fname, ".");
+  if (file.open(curDir, fname, O_READ)) {
+    filesize = file.fileSize();
+    sdpos = 0;
+    SERIAL_ECHOLNPAIR(MSG_SD_FILE_OPENED, fname, MSG_SD_SIZE, filesize);
+    SERIAL_ECHOLNPGM(MSG_SD_FILE_SELECTED);
+
+    selectFileByName(fname);
+    ui.set_status(longFilename[0] ? longFilename : fname);
   }
-  else { //write
-    if (!file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC))
-      SERIAL_ECHOLNPAIR(MSG_SD_OPEN_FILE_FAIL, fname, ".");
-    else {
-      flag.saving = true;
-      selectFileByName(fname);
-      #if ENABLED(EMERGENCY_PARSER)
-        emergency_parser.disable();
-      #endif
-      SERIAL_ECHOLNPAIR(MSG_SD_WRITE_TO_FILE, fname);
-      ui.set_status(fname);
-    }
+  else
+    openFailed(fname);
+}
+
+//
+// Open a file by DOS path for write
+//
+void CardReader::openFileWrite(char * const path) {
+  if (!isMounted()) return;
+
+  announceOpen(2, path);
+  file_subcall_ctr = 0;
+
+  stopSDPrint();
+
+  SdFile *curDir;
+  const char * const fname = diveToFile(curDir, path);
+  if (!fname) return;
+
+  if (file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)) {
+    flag.saving = true;
+    selectFileByName(fname);
+    #if ENABLED(EMERGENCY_PARSER)
+      emergency_parser.disable();
+    #endif
+    SERIAL_ECHOLNPAIR(MSG_SD_WRITE_TO_FILE, fname);
+    ui.set_status(fname);
   }
+  else
+    openFailed(fname);
 }
 
 //
@@ -1035,9 +1061,9 @@ uint16_t CardReader::get_num_Files() {
 void CardReader::printingHasFinished() {
   planner.synchronize();
   file.close();
-  if (file_subcall_ctr > 0) { // Heading up to a parent file that called current as a procedure.
+  if (file_subcall_ctr > 0) { // Resume calling file after closing procedure
     file_subcall_ctr--;
-    openFile(proc_filenames[file_subcall_ctr], true, true);
+    openFileRead(proc_filenames[file_subcall_ctr], 2); // 2 = Returning from sub-procedure
     setIndex(filespos[file_subcall_ctr]);
     startFileprint();
   }
diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h
index 8ef8dbeae31a261f69dd8bc6df1dc7883555dcea..f7081b95b3da80550625f378398e4e339ac0897f 100644
--- a/Marlin/src/sd/cardreader.h
+++ b/Marlin/src/sd/cardreader.h
@@ -83,7 +83,8 @@ public:
   static void checkautostart();
 
   // Basic file ops
-  static void openFile(char * const path, const bool read, const bool subcall=false);
+  static void openFileRead(char * const path, const uint8_t subcall=0);
+  static void openFileWrite(char * const path);
   static void closefile(const bool store_location=false);
   static void removeFile(const char * const name);