diff --git a/src/com/t_oster/liblasercut/LaserCutter.java b/src/com/t_oster/liblasercut/LaserCutter.java
index 5c5b1a87bb2a849b7eb8dc754c47f7105ef6a84d..55ebb57b85996a79c95c2d026f1693a4ca2ee078 100644
--- a/src/com/t_oster/liblasercut/LaserCutter.java
+++ b/src/com/t_oster/liblasercut/LaserCutter.java
@@ -228,6 +228,29 @@ public abstract class LaserCutter implements Cloneable, Customizable {
       }
     }
     
+    /**
+     * Adjust defaults after deserializing driver from XML
+     * Use this if you add new fields to a driver and need them to be properly
+     * initialized to *non-falsy* values before use.
+     * 
+     * i.e. you add a new key that by default isn't 0/0.0/false/"". Without
+     * adding a default in here, then no matter what your constructor/initializer
+     * does, it will always be set to a falsey value after deserializing an old
+     * XML file.
+     */
+    protected void setKeysMissingFromDeserialization()
+    {
+    }
+    
     @Override
     public abstract LaserCutter clone();
+  
+    /**
+     * Called by XStream when deserializing XML settings files. Hook here to
+     * call setKeysMissingFromDeserialization.
+     */
+    private Object readResolve() {
+      setKeysMissingFromDeserialization();
+      return this;
+    }
 }
diff --git a/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java b/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java
index 9bd596afca2253474eb1eb355a40455b049cc3a1..7594ad36c26ac66e2ba8b1a80f2f737dd77a5877 100644
--- a/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java
+++ b/src/com/t_oster/liblasercut/drivers/GenericGcodeDriver.java
@@ -69,6 +69,7 @@ public class GenericGcodeDriver extends LaserCutter {
   protected static final String SETTING_BLANK_LASER_DURING_RAPIDS = "Force laser off during G0 moves";
   protected static final String SETTING_FILE_EXPORT_PATH = "Path to save exported gcode";
   protected static final String SETTING_USE_BIDIRECTIONAL_RASTERING = "Use bidirectional rastering";
+  protected static final String SETTING_SPINDLE_MAX = "S value for 100% laser power";
   
   protected static Locale FORMAT_LOCALE = Locale.US;
   
@@ -339,6 +340,22 @@ public class GenericGcodeDriver extends LaserCutter {
     this.useBidirectionalRastering = useBidirectionalRastering;
   }
   
+   /*
+   * Value to use for feedrate when laser is 100% on.
+   * Varies between firmwares... 1, 100, 255, 10000, etc.
+   */
+  protected double spindleMax = 1.0;
+  
+  public double getSpindleMax()
+  {
+    return spindleMax;
+  }
+  
+  public void setSpindleMax(double spindleMax)
+  {
+    this.spindleMax = spindleMax;
+  }
+  
   @Override
   /**
    * We do not support Frequency atm, so we return power,speed and focus
@@ -392,7 +409,7 @@ public class GenericGcodeDriver extends LaserCutter {
   }
 
   protected void setPower(double powerInPercent) {
-    nextPower = powerInPercent;
+    nextPower = powerInPercent/100.0*spindleMax;
   }
   
   protected void setFocus(PrintStream out, double focus, double resolution) throws IOException {
@@ -424,7 +441,7 @@ public class GenericGcodeDriver extends LaserCutter {
     String append = "";
     if (nextPower != currentPower)
     {
-      append += String.format(FORMAT_LOCALE, " S%f", nextPower/100.0);
+      append += String.format(FORMAT_LOCALE, " S%f", nextPower);
       currentPower = nextPower;
     }
     if (nextSpeed != currentSpeed)
@@ -862,6 +879,7 @@ public void saveJob(java.io.PrintStream fileOutputStream, LaserJob job) throws I
     SETTING_LINEEND,
     SETTING_MAX_SPEED,
     SETTING_TRAVEL_SPEED,
+    SETTING_SPINDLE_MAX,
     SETTING_BLANK_LASER_DURING_RAPIDS,
     SETTING_PRE_JOB_GCODE,
     SETTING_POST_JOB_GCODE,
@@ -925,6 +943,8 @@ public void saveJob(java.io.PrintStream fileOutputStream, LaserJob job) throws I
       return this.getExportPath();
     } else if (SETTING_USE_BIDIRECTIONAL_RASTERING.equals(attribute)) {
       return this.getUseBidirectionalRastering();
+    } else if (SETTING_SPINDLE_MAX.equals(attribute)) {
+      return this.getSpindleMax();
     }
     
     return null;
@@ -978,9 +998,22 @@ public void saveJob(java.io.PrintStream fileOutputStream, LaserJob job) throws I
       this.setExportPath((String) value);
     } else if (SETTING_USE_BIDIRECTIONAL_RASTERING.equals(attribute)) {
       this.setUseBidirectionalRastering((Boolean) value);
+    } else if (SETTING_SPINDLE_MAX.equals(attribute)) {
+      this.setSpindleMax((Double) value);
     }
   }
-
+  
+  /**
+   * Adjust defaults after deserializing driver from an old version of XML file
+   */
+  @Override
+  protected void setKeysMissingFromDeserialization()
+  {
+    // added field spindleMax, needs to be set to 1.0 by default
+    // but xstream initializes it to 0.0 when it is missing from XML
+    if (this.spindleMax <= 0.0) this.spindleMax = 1.0;
+  }
+  
   @Override
   public GenericGcodeDriver clone() {
     GenericGcodeDriver clone = new GenericGcodeDriver();
diff --git a/src/com/t_oster/liblasercut/drivers/Marlin.java b/src/com/t_oster/liblasercut/drivers/Marlin.java
index 00b90e75e8baf538519ce7b76e6c1bc79b562257..c4238694530019563d29575ad181aa7a594aba35 100644
--- a/src/com/t_oster/liblasercut/drivers/Marlin.java
+++ b/src/com/t_oster/liblasercut/drivers/Marlin.java
@@ -43,12 +43,24 @@ public class Marlin extends GenericGcodeDriver {
     setPostJobGcode(getPostJobGcode()+",M5,G28 XY");
     setSerialTimeout(35000);
     setBlankLaserDuringRapids(false);
+    setSpindleMax(100.0); // marlin interprets power from 0-100 instead of 0-1
     
     //Marlin has no way to upload over the network so remove the upload url text
     setHttpUploadUrl("");
     setHost("");
   }
   
+  /**
+   * Adjust defaults after deserializing driver from an old version of XML file
+   */
+  @Override
+  protected void setKeysMissingFromDeserialization()
+  {
+    // added field spindleMax, needs to be set to 100.0 for Marlin
+    // but xstream initializes it to 0.0 when it is missing from XML
+    if (this.spindleMax <= 0.0) this.spindleMax = 100.0;
+  }
+  
   @Override
   public String getIdentificationLine()
   {
@@ -66,6 +78,7 @@ public class Marlin extends GenericGcodeDriver {
     result.remove(GenericGcodeDriver.SETTING_INIT_DELAY);
     result.remove(GenericGcodeDriver.SETTING_HTTP_UPLOAD_URL);
     result.remove(GenericGcodeDriver.SETTING_HOST);
+    result.remove(GenericGcodeDriver.SETTING_SPINDLE_MAX);
     result.remove(GenericGcodeDriver.SETTING_BLANK_LASER_DURING_RAPIDS);
     return result.toArray(new String[0]);
   }
@@ -94,13 +107,6 @@ public class Marlin extends GenericGcodeDriver {
     return null;
   }
 
-  @Override
-  protected void setPower(double powerInPercent)
-  {
-    //marlin interprets power from 0-100 instead of 0-1
-    super.setPower(powerInPercent*100);
-  }
-
   @Override
   public String getModelName()
   {
diff --git a/src/com/t_oster/liblasercut/drivers/SmoothieBoard.java b/src/com/t_oster/liblasercut/drivers/SmoothieBoard.java
index 5609e320cb32226d2f13c7cb163675f77ba6c696..08bc5dd6b1637893b6d6798d285e8ea88e48b443 100644
--- a/src/com/t_oster/liblasercut/drivers/SmoothieBoard.java
+++ b/src/com/t_oster/liblasercut/drivers/SmoothieBoard.java
@@ -41,6 +41,7 @@ public class SmoothieBoard extends GenericGcodeDriver {
     setPreJobGcode(getPreJobGcode()+",M3");
     setPostJobGcode(getPostJobGcode()+",M5");
     setBlankLaserDuringRapids(false);
+    setSpindleMax(1.0);
   }
   
   @Override
@@ -78,6 +79,7 @@ public class SmoothieBoard extends GenericGcodeDriver {
     result.remove(GenericGcodeDriver.SETTING_BAUDRATE);
     result.remove(GenericGcodeDriver.SETTING_LINEEND);
     result.remove(GenericGcodeDriver.SETTING_INIT_DELAY);
+    result.remove(GenericGcodeDriver.SETTING_SPINDLE_MAX);
     result.remove(GenericGcodeDriver.SETTING_BLANK_LASER_DURING_RAPIDS);
     return result.toArray(new String[0]);
   }