mirror of
https://github.com/mnaberez/py65.git
synced 2024-06-09 10:29:35 +00:00
Merge pull request #48 from SamCoVT/master
Proposed fix for #47 Save and restore termios settings with helper functions in utils.console
This commit is contained in:
commit
5ae7eeb6f7
|
@ -53,28 +53,60 @@ class Monitor(cmd.Cmd):
|
||||||
self._width = 78
|
self._width = 78
|
||||||
self.prompt = "."
|
self.prompt = "."
|
||||||
self._add_shortcuts()
|
self._add_shortcuts()
|
||||||
cmd.Cmd.__init__(self, stdin=stdin, stdout=stdout)
|
|
||||||
|
|
||||||
if argv is None:
|
# Save the current system input mode so it can be restored after
|
||||||
argv = sys.argv
|
# after processing commands and before exiting.
|
||||||
load, rom, goto = self._parse_args(argv)
|
console.save_mode(sys.stdin)
|
||||||
|
|
||||||
self._reset(self.mpu_type, self.getc_addr, self.putc_addr)
|
# Attempt to get a copy of stdin that is unbuffered on systems
|
||||||
|
# that support it. This allows for immediate response to
|
||||||
|
# typed input as well as pasted input. If unable to get an
|
||||||
|
# unbuffered version of stdin, the original version is returned.
|
||||||
|
self.unbuffered_stdin = console.get_unbuffered_stdin(stdin)
|
||||||
|
|
||||||
if load is not None:
|
cmd.Cmd.__init__(self, stdin=self.unbuffered_stdin, stdout=stdout)
|
||||||
self.do_load("%r" % load)
|
|
||||||
|
|
||||||
if goto is not None:
|
# Check for any exceptions thrown during __init__ while
|
||||||
self.do_goto(goto)
|
# processing the arguments.
|
||||||
|
try:
|
||||||
|
|
||||||
|
if argv is None:
|
||||||
|
argv = sys.argv
|
||||||
|
load, rom, goto = self._parse_args(argv)
|
||||||
|
|
||||||
|
self._reset(self.mpu_type, self.getc_addr, self.putc_addr)
|
||||||
|
|
||||||
|
if load is not None:
|
||||||
|
self.do_load("%r" % load)
|
||||||
|
|
||||||
|
if goto is not None:
|
||||||
|
self.do_goto(goto)
|
||||||
|
|
||||||
|
if rom is not None:
|
||||||
|
# load a ROM and run from the reset vector
|
||||||
|
self.do_load("%r top" % rom)
|
||||||
|
physMask = self._mpu.memory.physMask
|
||||||
|
reset = self._mpu.RESET & physMask
|
||||||
|
dest = self._mpu.memory[reset] + \
|
||||||
|
(self._mpu.memory[reset + 1] << self.byteWidth)
|
||||||
|
self.do_goto("$%x" % dest)
|
||||||
|
except:
|
||||||
|
# Restore input mode on any exception and then rethrow the
|
||||||
|
# exception.
|
||||||
|
console.restore_mode()
|
||||||
|
raise
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
try:
|
||||||
|
# Restore the input mode.
|
||||||
|
console.restore_mode()
|
||||||
|
# Close the unbuffered input file handle, if it exists.
|
||||||
|
if self.unbuffered_stdin != None:
|
||||||
|
if self.unbuffered_stdin != sys.stdin:
|
||||||
|
self.unbuffered_stdin.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if rom is not None:
|
|
||||||
# load a ROM and run from the reset vector
|
|
||||||
self.do_load("%r top" % rom)
|
|
||||||
physMask = self._mpu.memory.physMask
|
|
||||||
reset = self._mpu.RESET & physMask
|
|
||||||
dest = self._mpu.memory[reset] + \
|
|
||||||
(self._mpu.memory[reset + 1] << self.byteWidth)
|
|
||||||
self.do_goto("$%x" % dest)
|
|
||||||
|
|
||||||
def _parse_args(self, argv):
|
def _parse_args(self, argv):
|
||||||
try:
|
try:
|
||||||
|
@ -139,6 +171,9 @@ class Monitor(cmd.Cmd):
|
||||||
if not line.startswith("quit"):
|
if not line.startswith("quit"):
|
||||||
self._output_mpu_status()
|
self._output_mpu_status()
|
||||||
|
|
||||||
|
# Switch back to the previous input mode.
|
||||||
|
console.restore_mode()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _reset(self, mpu_type, getc_addr=0xF004, putc_addr=0xF001):
|
def _reset(self, mpu_type, getc_addr=0xF004, putc_addr=0xF001):
|
||||||
|
@ -467,6 +502,10 @@ class Monitor(cmd.Cmd):
|
||||||
mpu = self._mpu
|
mpu = self._mpu
|
||||||
mem = self._mpu.memory
|
mem = self._mpu.memory
|
||||||
|
|
||||||
|
# Switch to immediate (noncanonical) no-echo input mode on POSIX
|
||||||
|
# operating systems. This has no effect on Windows.
|
||||||
|
console.noncanonical_mode(self.stdin)
|
||||||
|
|
||||||
if not breakpoints:
|
if not breakpoints:
|
||||||
while True:
|
while True:
|
||||||
mpu.step()
|
mpu.step()
|
||||||
|
@ -483,6 +522,9 @@ class Monitor(cmd.Cmd):
|
||||||
self._output(msg % self._breakpoints.index(pc))
|
self._output(msg % self._breakpoints.index(pc))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Switch back to the previous input mode.
|
||||||
|
console.restore_mode()
|
||||||
|
|
||||||
def help_radix(self):
|
def help_radix(self):
|
||||||
self._output("radix [H|D|O|B]")
|
self._output("radix [H|D|O|B]")
|
||||||
self._output("Set default radix to hex, decimal, octal, or binary.")
|
self._output("Set default radix to hex, decimal, octal, or binary.")
|
||||||
|
@ -877,6 +919,7 @@ def main(args=None):
|
||||||
c.cmdloop()
|
c.cmdloop()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
c._output('')
|
c._output('')
|
||||||
|
console.restore_mode()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -3,6 +3,22 @@ import sys
|
||||||
if sys.platform[:3] == "win":
|
if sys.platform[:3] == "win":
|
||||||
import msvcrt
|
import msvcrt
|
||||||
|
|
||||||
|
def get_unbuffered_stdin(stdin):
|
||||||
|
""" get_unbuffered_stdin returns the given stdin on Windows. """
|
||||||
|
return stdin
|
||||||
|
|
||||||
|
def save_mode(stdin):
|
||||||
|
""" save_mode is a no-op on Windows. """
|
||||||
|
return
|
||||||
|
|
||||||
|
def noncanonical_mode(stdin):
|
||||||
|
""" noncanonical_mode is a no-op on Windows. """
|
||||||
|
return
|
||||||
|
|
||||||
|
def restore_mode(stdin):
|
||||||
|
""" restore_mode is a no-op on Windows. """
|
||||||
|
return
|
||||||
|
|
||||||
def getch(stdin):
|
def getch(stdin):
|
||||||
""" Read one character from the Windows console, blocking until one
|
""" Read one character from the Windows console, blocking until one
|
||||||
is available. Does not echo the character. The stdin argument is
|
is available. Does not echo the character. The stdin argument is
|
||||||
|
@ -24,51 +40,156 @@ if sys.platform[:3] == "win":
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
else:
|
else:
|
||||||
import select
|
|
||||||
import os
|
|
||||||
import termios
|
import termios
|
||||||
import fcntl
|
import os
|
||||||
|
from select import select
|
||||||
|
|
||||||
|
oldattr = None
|
||||||
|
oldstdin = None
|
||||||
|
|
||||||
|
def get_unbuffered_stdin(stdin):
|
||||||
|
""" Attempt to get and return a copy of stdin that is
|
||||||
|
unbuffered. This allows for immediate response to typed input
|
||||||
|
as well as pasted input. If unable to get an unbuffered
|
||||||
|
version of stdin, return the original version.
|
||||||
|
"""
|
||||||
|
if stdin != None:
|
||||||
|
try:
|
||||||
|
# Reopen stdin with no buffer.
|
||||||
|
return os.fdopen(os.dup(stdin.fileno()), 'rb', 0)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
# Unable to reopen this file handle with no buffer.
|
||||||
|
# Just use the original file handle.
|
||||||
|
return stdin
|
||||||
|
else:
|
||||||
|
# If stdin is None, try using sys.stdin for input.
|
||||||
|
try:
|
||||||
|
# Reopen the system's stdin with no buffer.
|
||||||
|
return os.fdopen(os.dup(sys.stdin.fileno()), 'rb', 0)
|
||||||
|
except:
|
||||||
|
# If unable to get an unbuffered stdin, just return
|
||||||
|
# None, which is what we started with if we got here.
|
||||||
|
return 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
|
||||||
|
global oldstdin
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
oldstdin = stdin
|
||||||
|
fd = stdin.fileno()
|
||||||
|
oldattr = termios.tcgetattr(fd)
|
||||||
|
except:
|
||||||
|
# Quietly ignore termios errors, such as stdin not being
|
||||||
|
# a tty.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def noncanonical_mode(stdin):
|
||||||
|
"""For operating systems that support it, switch to noncanonical
|
||||||
|
mode. In this mode, characters are given immediately to the
|
||||||
|
program and no processing of editing characters (like backspace)
|
||||||
|
is performed. Echo is also turned off in this mode. The
|
||||||
|
previous input behavior can be restored with restore_mode.
|
||||||
|
"""
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Switch to non-blocking reads with no timeout.
|
||||||
|
newattr[6][termios.VMIN] = 0
|
||||||
|
newattr[6][termios.VTIME] = 0
|
||||||
|
termios.tcsetattr(fd, termios.TCSANOW, newattr)
|
||||||
|
except:
|
||||||
|
# Quietly ignore termios errors, such as stdin not being
|
||||||
|
# a tty.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def restore_mode():
|
||||||
|
"""For operating systems that support it, restore the previous
|
||||||
|
input mode.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Restore the previous input setup.
|
||||||
|
global oldattr
|
||||||
|
global oldstdin
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Restore the original system stdin.
|
||||||
|
oldfd = oldstdin.fileno()
|
||||||
|
# If there is a previous setting, restore it.
|
||||||
|
if oldattr != None:
|
||||||
|
# Restore it on the original system stdin.
|
||||||
|
termios.tcsetattr(oldfd, termios.TCSANOW, oldattr)
|
||||||
|
except:
|
||||||
|
# Quietly ignore termios errors, such as stdin not being a tty.
|
||||||
|
pass
|
||||||
|
|
||||||
def getch(stdin):
|
def getch(stdin):
|
||||||
""" Read one character from stdin, blocking until one is available.
|
""" Read one character from stdin, blocking until one is available.
|
||||||
Does not echo the character.
|
Does not echo the character.
|
||||||
"""
|
"""
|
||||||
fd = stdin.fileno()
|
# Try to get a character with a non-blocking read.
|
||||||
oldattr = termios.tcgetattr(fd)
|
char = ''
|
||||||
newattr = oldattr[:]
|
noncanonical_mode(stdin)
|
||||||
newattr[3] &= ~termios.ICANON & ~termios.ECHO
|
# If we didn't get a character, ask again.
|
||||||
try:
|
while char == '':
|
||||||
termios.tcsetattr(fd, termios.TCSANOW, newattr)
|
try:
|
||||||
char = stdin.read(1)
|
# On OSX, calling read when no data is available causes the
|
||||||
finally:
|
# file handle to never return any future data, so we need to
|
||||||
termios.tcsetattr(fd, termios.TCSAFLUSH, oldattr)
|
# use select to make sure there is at least one char to read.
|
||||||
|
rd,wr,er = select([stdin], [], [], 0.01)
|
||||||
|
if rd != []:
|
||||||
|
char = stdin.read(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# Pass along a CTRL-C interrupt.
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return char
|
return char
|
||||||
|
|
||||||
def getch_noblock(stdin):
|
def getch_noblock(stdin):
|
||||||
""" Read one character from stdin without blocking. Does not echo the
|
""" Read one character from stdin without blocking. Does not echo the
|
||||||
character. If no character is available, an empty string is returned.
|
character. If no character is available, an empty string is returned.
|
||||||
"""
|
"""
|
||||||
|
char = ''
|
||||||
|
|
||||||
fd = stdin.fileno()
|
# Using non-blocking read
|
||||||
|
noncanonical_mode(stdin)
|
||||||
oldterm = termios.tcgetattr(fd)
|
|
||||||
newattr = oldterm[:]
|
|
||||||
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
|
|
||||||
termios.tcsetattr(fd, termios.TCSANOW, newattr)
|
|
||||||
|
|
||||||
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
|
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
char = ''
|
# On OSX, calling read when no data is available causes the
|
||||||
r, w, e = select.select([fd], [], [], 0.1)
|
# file handle to never return any future data, so we need to
|
||||||
if r:
|
# use select to make sure there is at least one char to read.
|
||||||
|
rd,wr,er = select([stdin], [], [], 0.01)
|
||||||
|
if rd != []:
|
||||||
char = stdin.read(1)
|
char = stdin.read(1)
|
||||||
if char == "\n":
|
except KeyboardInterrupt:
|
||||||
char = "\r"
|
# Pass along a CTRL-C interrupt.
|
||||||
finally:
|
raise
|
||||||
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
|
except:
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
|
pass
|
||||||
|
|
||||||
|
# Convert linefeeds to carriage returns.
|
||||||
|
if char != '' and ord(char) == 10:
|
||||||
|
char = '\r'
|
||||||
return char
|
return char
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user