...
diff --git a/32blit-stm32/Src/usb-serial.cpp b/32blit-stm32/Src/usb-serial.cpp
index c68a8d5..b373299 100644
--- a/32blit-stm32/Src/usb-serial.cpp
+++ b/32blit-stm32/Src/usb-serial.cpp
@@ -87,7 +87,7 @@
 

   std::map<std::string, CommandHandler> handlers;

 

-  constexpr uint32_t MAX_PACKETS = 16;

+  constexpr uint32_t MAX_PACKETS = 64;

   Packet packets[MAX_PACKETS];

   std::vector<Packet*> free_packets;

 

@@ -174,7 +174,7 @@
           // the command handle must return true when it has finished processing

           // the entire command (even if this is across multiple packets)

           CommandState result = handler(CommandState::STREAM, packet->data, packet->length);

-

+          

           if(result == CommandState::END) {    

             // command stream is complete, detach handler

             handler = nullptr;

diff --git a/32blit.toolchain b/32blit.toolchain
index 30feb5f..4df35df 100644
--- a/32blit.toolchain
+++ b/32blit.toolchain
@@ -37,7 +37,6 @@
 set(CMAKE_C_FLAGS_INIT "${COMMON_FLAGS}")
 set(CMAKE_CXX_FLAGS_INIT "${COMMON_FLAGS}")
 set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og")
-#set(CMAKE_EXE_LINKER_FLAGS_INIT "-specs=nano.specs -T ${MCU_LINKER_SCRIPT} -Wl,--gc-sections")
 
 set(CMAKE_EXE_LINKER_FLAGS_INIT "-specs=nosys.specs -Wl,--gc-sections,--no-wchar-size-warning")
 
diff --git a/examples/flash b/examples/flash
index a7136a0..786c751 100644
--- a/examples/flash
+++ b/examples/flash
@@ -7,20 +7,23 @@
 import os
 import os.path
 import time
+import threading
 from tqdm import tqdm
 
 
 parser = argparse.ArgumentParser(description='32blit binary flasher.')
-parser.add_argument("file", help="Path to the binary file (e.g. 'build.stm32/voxel.bin')")
+parser.add_argument("command", help="Command to perform (e.g. 'save', 'dfu', 'reset')", default="save")
 parser.add_argument("port", help="Name of the serial port (e.g. 'COM4', '/dev/ttyS4', etc.)")
 parser.add_argument("--target", help="Target either 'sd' or 'flash'", default="sd", choices=["sd", "flash"])
+parser.add_argument("--no-read", help="Do not wait to read responses", action="store_true")
+parser.add_argument("file", help="Path to the binary file (e.g. 'build.stm32/voxel.bin')", nargs='?')
 args = parser.parse_args()
 
 print("32blit binary flasher.")
 
 port = args.port
+command = args.command
 target = args.target
-path = args.file
 
 valid_ports = [port[0] for port in serial.tools.list_ports.comports()]
 
@@ -28,40 +31,83 @@
   print("Specified port '{0}' not valid".format(port))
   sys.exit(os.EX_OSFILE)
 
-if not os.path.isfile(path):
-  print("Specified binary file '{0}' does not exist".format(path))
-  sys.exit(os.EX_NOINPUT)
-
 serial_port = None
 
 try:
   serial_port = serial.Serial(port, 115200, timeout=1)
+  serial_port.reset_output_buffer()
 except OSError:
   print("Could not open port '{0}'".format(port))
   print("The port may not exist or you may not have permission to access it.")
   sys.exit(os.EX_OSFILE)
 
-file_size = os.stat(path).st_size 
-file_name = os.path.basename(path)
-sent_byte_count = 0
-chunk_size = 64
+if not args.no_read:
+  # read thread constantly polls for responses from 32blit and
+  # outputs them to the screen
+  def read_serial(serial_port):
+    while True:
+      data = serial_port.read_until(b"\0").decode()
+      if data:
+        print(data)
 
-# such reset, very boot
-#serial_port.write(b"32BL_RST\x00")
-#sys.exit()
+  read_thread = threading.Thread(target=read_serial, args=(serial_port,))
+  read_thread.start()
 
-serial_port.reset_output_buffer()
+print(command)
 
-command = "SAVE\x00{0}\x00{1}\x00".format(file_name, file_size)
-serial_port.write(bytes(command, "ascii"))
+if command == "save":
+  path = args.file
+  if not os.path.isfile(path):
+    print("Specified binary file '{0}' does not exist".format(path))
+    sys.exit(os.EX_NOINPUT)
 
-with open(path, "rb") as f:
-  progress = tqdm(total=file_size, desc="Flashing...", unit_scale=True, unit_divisor=1024, unit="B", ncols=70, dynamic_ncols=True)
+  file_size = os.stat(path).st_size 
+  file_name = os.path.basename(path)
 
-  while sent_byte_count < file_size:
-    data = f.read(chunk_size)
-    serial_port.write(data)    
-    sent_byte_count += chunk_size
-    progress.update(chunk_size)
-    
-  progress.close()
\ No newline at end of file
+  # send the save command to 32blit
+  data = "SAVE\x00{0}\x00{1}\x00".format(file_name, file_size)
+  serial_port.write(bytes(data, "ascii"))
+
+  # transmit the file data to 32blit
+  sent_byte_count = 0
+  chunk_size = 64
+  with open(path, "rb") as f:
+    progress = tqdm(total=file_size, desc="Flashing...", unit_scale=True, unit_divisor=1024, unit="B", ncols=70, dynamic_ncols=True)
+
+    while sent_byte_count < file_size:
+      # last chunk may be smaller than 64 bytes
+      if file_size - sent_byte_count < chunk_size:
+        chunk_size = file_size - sent_byte_count
+
+      # read chunk from file
+      data = f.read(chunk_size)
+
+      # transmit it to 32blit
+      serial_port.write(data)    
+      sent_byte_count += chunk_size
+
+      # update progress bar    
+      progress.update(chunk_size)
+      
+    progress.close()
+
+if command == "dfu":
+  # send the dfu command to 32blit
+  data = "DFU\x00"
+  serial_port.write(bytes(data, "ascii"))
+
+if command == "reset":
+  # send the dfu command to 32blit
+  data = "RESET\x00"
+  serial_port.write(bytes(data, "ascii"))
+
+if not args.no_read:
+  # wait for any responses
+  print("Reading responses from 32blit. Press Ctrl+C to quit.")
+  try:
+    while True:
+      pass 
+  except KeyboardInterrupt:
+    read_thread.stop()    
+
+time.sleep(1)
\ No newline at end of file
diff --git a/firmware/firmware.cpp b/firmware/firmware.cpp
index a5aac92..995d886 100644
--- a/firmware/firmware.cpp
+++ b/firmware/firmware.cpp
@@ -54,14 +54,23 @@
 
 Vec2 list_offset(5.0f, 0.0f);
 
-CommandState usb_cdc_save_to_sd_card(CommandState state, char *data, uint32_t length);
+CommandState usb_serial_save_to_sd_card(CommandState state, char *data, uint32_t length);
 
-CommandState usb_cdc_reset(CommandState state, char *data, uint32_t length) {
+CommandState usb_serial_reset(CommandState state, char *data, uint32_t length) {
   NVIC_SystemReset();
   return CommandState::END;
 }
 
-CommandState usb_cdc_application_location(CommandState state, char *data, uint32_t length) {
+CommandState usb_serial_dfu(CommandState state, char *data, uint32_t length) {
+  *((uint32_t *)0x2001FFFC) = 0xCAFEBABE;
+
+  SCB_CleanDCache();
+  NVIC_SystemReset();
+
+  return CommandState::END;
+}
+
+CommandState usb_serial_application_location(CommandState state, char *data, uint32_t length) {
   while(USBD_BUSY == transmit("32BL_EXT")) {};
 	HAL_Delay(250);
 	return CommandState::END;
@@ -73,9 +82,10 @@
   set_screen_mode(ScreenMode::hires);
   load_file_list();
 
-  register_command_handler("RESET", usb_cdc_reset);
-  register_command_handler("INFO", usb_cdc_application_location);
-  register_command_handler("SAVE", usb_cdc_save_to_sd_card);
+  register_command_handler("RESET", usb_serial_reset);
+  register_command_handler( "INFO", usb_serial_application_location);
+  register_command_handler( "SAVE", usb_serial_save_to_sd_card);
+  register_command_handler(  "DFU", usb_serial_dfu);
 }
 
 void background(uint32_t time_ms) {  
@@ -193,7 +203,7 @@
 
 bool flash_from_sd_to_qspi_flash(const std::string &filename)
 {  
-  static uint8_t file_buffer[4 * 1024];
+  static uint8_t file_buffer[256];
 
 	FIL file;
 	if(f_open(&file, filename.c_str(), FA_READ) != FR_OK) {
@@ -248,7 +258,7 @@
 
 // when a command is issued (e.g. "PROG" or "SAVE") then this function is called
 // whenever new data is received to allow the firmware to process it.
-CommandState usb_cdc_save_to_sd_card(CommandState state, char *data, uint32_t length) {
+CommandState usb_serial_save_to_sd_card(CommandState state, char *data, uint32_t length) {
   static char   filename[64] = {'\0'};
   static FIL    file;
   
@@ -301,6 +311,8 @@
         f_close(&file);
         return CommandState::ERROR;        
       }
+
+      //f_sync(&file);
       
       bytes_processed += bytes_written;
 
@@ -310,6 +322,7 @@
         load_file_list();
         select_file(filename);
         f_close(&file);
+        
         memset(filename, 0, sizeof(filename));
         return CommandState::END;
       }else{