diff --git a/py65/monitor.py b/py65/monitor.py
index 05f7652..e31aa2a 100644
--- a/py65/monitor.py
+++ b/py65/monitor.py
@@ -55,6 +55,8 @@ class Monitor(cmd.Cmd):
         self._add_shortcuts()
         cmd.Cmd.__init__(self, stdin=stdin, stdout=stdout)
 
+        console.save_mode(self.stdin)
+
         if argv is None:
             argv = sys.argv
         load, rom, goto = self._parse_args(argv)
@@ -131,6 +133,7 @@ class Monitor(cmd.Cmd):
             result = cmd.Cmd.onecmd(self, line)
         except KeyboardInterrupt:
             self._output("Interrupt")
+
         except Exception:
             (file, fun, line), t, v, tbinfo = compact_traceback()
             error = 'Error: %s, %s: file: %s line: %s' % (t, v, file, line)
@@ -139,6 +142,9 @@ class Monitor(cmd.Cmd):
         if not line.startswith("quit"):
             self._output_mpu_status()
 
+        # Switch back to the previous input mode.
+        console.restore_mode(self.stdin)    
+
         return result
 
     def _reset(self, mpu_type, getc_addr=0xF004, putc_addr=0xF001):
@@ -885,6 +891,7 @@ def main(args=None):
         c.cmdloop()
     except KeyboardInterrupt:
         c._output('')
+        console.restore_mode(c.stdin)
 
 if __name__ == "__main__":
     main()
diff --git a/py65/utils/console.py b/py65/utils/console.py
index 3d41a3e..cf6d905 100644
--- a/py65/utils/console.py
+++ b/py65/utils/console.py
@@ -4,6 +4,10 @@ import time
 if sys.platform[:3] == "win":
     import msvcrt
 
+    def save_mode(stdin):
+        """ get_mode is a no-op on Windows. """
+        return
+
     def noncanonical_mode(stdin):
         """ noncanonical_mode is a no-op on Windows. """
         return
@@ -38,8 +42,30 @@ else:
     import termios
     import fcntl
 
-    oldattr_stack = [ ]
+    oldattr = None
 
+    def save_mode(stdin):
+        """ For operating systems that support it, save the original
+        input termios settings so they can be restored later.  This 
+        allows us to switch to noncanonical mode when software is
+        running in the simulator and back to the original mode when
+        accepting commands.
+        """
+        # For non-Windows systems, save the original input settings,
+        # which will typically be blocking reads with echo.
+        global oldattr
+
+        # When the input is not a pty/tty, this will fail.
+        # In that case, it's ok to ignore the failure.
+        try:
+            # Save the current terminal setup.
+            fd = stdin.fileno()
+            oldattr = termios.tcgetattr(fd)
+        except:
+            # Quietly ignore termios errors, such as stdin not being
+            # a tty.
+            print("DEBUG: Exception getting termios settings")
+            pass
 
     def noncanonical_mode(stdin):
         """For operating systems that support it, switch to noncanonical
@@ -50,36 +76,39 @@ else:
         """
         # For non-windows systems, switch to non-canonical
         # and no-echo non-blocking-read mode.
+        try:
+            # Save the current terminal setup.
+            fd = stdin.fileno()
+            currentattr = termios.tcgetattr(fd)
+            # Switch to noncanonical (instant) mode with no echo.
+            newattr = currentattr[:]
+            newattr[3] &= ~termios.ICANON & ~termios.ECHO
 
-        global oldattr_stack
-
-        # Save the current terminal setup.
-        fd = stdin.fileno()
-        oldattr = termios.tcgetattr(fd)
-        oldattr_stack.append(oldattr)
+            # Switch to non-blocking reads with 0.1 second timeout.
+            newattr[6][termios.VMIN] = 0
+            newattr[6][termios.VTIME] = 1
+            termios.tcsetattr(fd, termios.TCSANOW, newattr)
+        except:
+            # Quietly ignore termios errors, such as stdin not being
+            # a tty.
+            pass
         
-        # Switch to noncanonical (instant) mode with no echo.
-        newattr = oldattr[:]
-        newattr[3] &= ~termios.ICANON & ~termios.ECHO
-
-        # Switch to non-blocking reads with 0.1 second timeout.
-        newattr[6][termios.VMIN] = 0
-        newattr[6][termios.VTIME] = 1
-        termios.tcsetattr(fd, termios.TCSANOW, newattr)
-
     def restore_mode(stdin):
         """For operating systems that support it, restore the previous 
         input mode.
         """
 
         # Restore the previous input setup.
-        global oldattr_stack
-        fd = stdin.fileno()
-        # If there is a previous setting, restore it.
-        if oldattr_stack:
-            termios.tcsetattr(fd, termios.TCSANOW, oldattr_stack.pop())
-
+        global oldattr
 
+        try:
+            fd = stdin.fileno()
+            # If there is a previous setting, restore it.
+            if oldattr != None:
+                termios.tcsetattr(fd, termios.TCSANOW, oldattr)
+        except:
+            # Quietly ignore termios errors, such as stdin not being a tty.
+            pass
 
     def getch(stdin):
         """ Read one character from stdin, blocking until one is available.
@@ -88,9 +117,11 @@ else:
         # Try to get a character with a non-blocking read.
         char = ''
         noncanonical_mode(stdin)
+        # If we didn't get a character, ask again.
         while char == '':
             char = stdin.read(1)
-        restore_mode(stdin)
+            # stdin has already been set up with a 0.1s delay, so we
+            # don't need an additional delay here.
         return char
 
     def getch_noblock(stdin):
@@ -99,10 +130,9 @@ else:
         """
         char = ''
 
-        # Using non-blocking read as set up in Monitor._run
+        # Using non-blocking read
         noncanonical_mode(stdin)
         char = stdin.read(1)
-        restore_mode(stdin)
 
         if char == "\n":
             char = "\r"