2008-09-07 06:07:58 +00:00
|
|
|
#!/usr/bin/env python -u
|
|
|
|
|
2012-01-01 23:56:56 +00:00
|
|
|
"""py65mon -- interact with a simulated 6502-based system
|
|
|
|
|
|
|
|
Usage: %s [options]
|
|
|
|
|
|
|
|
Options:
|
|
|
|
-h, --help : Show this message
|
|
|
|
-m, --mpu <device> : Choose which MPU device (default is 6502)
|
|
|
|
-l, --load <file> : Load a file at address 0
|
|
|
|
-r, --rom <file> : Load a rom at the top of address space and reset into it
|
|
|
|
-g, --goto <address> : Perform a goto command after loading any files
|
|
|
|
"""
|
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
import cmd
|
2012-01-02 00:56:28 +00:00
|
|
|
import getopt
|
2008-09-07 06:07:58 +00:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import shlex
|
2008-11-17 05:59:59 +00:00
|
|
|
import sys
|
2012-01-03 01:13:40 +00:00
|
|
|
import urllib2
|
2009-08-22 17:56:39 +00:00
|
|
|
from asyncore import compact_traceback
|
2009-04-09 03:50:44 +00:00
|
|
|
from py65.devices.mpu6502 import MPU as NMOS6502
|
|
|
|
from py65.devices.mpu65c02 import MPU as CMOS65C02
|
2011-08-19 16:40:07 +00:00
|
|
|
from py65.devices.mpu65Org16 import MPU as V65Org16
|
2008-11-18 06:57:17 +00:00
|
|
|
from py65.disassembler import Disassembler
|
2008-11-21 05:44:25 +00:00
|
|
|
from py65.assembler import Assembler
|
2009-04-05 19:04:36 +00:00
|
|
|
from py65.utils.addressing import AddressParser
|
2009-08-22 17:56:39 +00:00
|
|
|
from py65.utils import console
|
2009-04-05 19:04:36 +00:00
|
|
|
from py65.utils.conversions import itoa
|
2008-09-10 02:34:04 +00:00
|
|
|
from py65.memory import ObservableMemory
|
2008-09-07 06:07:58 +00:00
|
|
|
|
|
|
|
class Monitor(cmd.Cmd):
|
2012-01-01 23:47:24 +00:00
|
|
|
|
|
|
|
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02, '65Org16': V65Org16}
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2011-08-21 20:33:37 +00:00
|
|
|
def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None, stdout=None, argv=None):
|
|
|
|
self.mpu_type=mpu_type
|
|
|
|
if argv is None:
|
|
|
|
argv = sys.argv
|
|
|
|
self._reset(self.mpu_type)
|
2008-11-29 23:32:46 +00:00
|
|
|
self._width = 78
|
2008-09-07 06:07:58 +00:00
|
|
|
self._update_prompt()
|
2008-11-29 07:06:38 +00:00
|
|
|
self._add_shortcuts()
|
2008-09-07 06:07:58 +00:00
|
|
|
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
2012-01-07 03:21:03 +00:00
|
|
|
self._parse_args(argv)
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2012-01-07 03:21:03 +00:00
|
|
|
def _parse_args(self, argv):
|
2011-08-28 19:20:27 +00:00
|
|
|
try:
|
|
|
|
options, args = getopt.getopt(argv[1:], 'hm:l:r:g:',
|
|
|
|
['help', 'mpu=', 'load=', 'rom=', 'goto='])
|
|
|
|
except getopt.GetoptError, err:
|
2012-01-05 05:18:24 +00:00
|
|
|
self._output(str(err))
|
2012-01-05 05:22:56 +00:00
|
|
|
self._usage()
|
|
|
|
self._exit(1)
|
2011-08-28 19:20:27 +00:00
|
|
|
|
2011-08-21 20:33:37 +00:00
|
|
|
for opt, value in options:
|
2011-08-28 19:20:27 +00:00
|
|
|
if opt in ('-l','--load'):
|
2012-01-10 17:56:52 +00:00
|
|
|
cmd = "load %s" % value
|
|
|
|
self.onecmd(cmd)
|
2011-08-28 19:20:27 +00:00
|
|
|
if opt in ('-r','--rom'):
|
|
|
|
# load a ROM and run from the reset vector
|
2012-01-10 17:56:52 +00:00
|
|
|
cmd = "load %s %d" % (value, -1)
|
|
|
|
self.onecmd(cmd)
|
2011-08-28 19:20:27 +00:00
|
|
|
physMask = self._mpu.memory.physMask
|
2012-01-10 17:56:52 +00:00
|
|
|
reset = self._mpu.ResetTo & physMask
|
|
|
|
dest = self._mpu.memory[reset] + (self._mpu.memory[reset+1] << self.byteWidth)
|
|
|
|
cmd = "goto %08x" % dest
|
|
|
|
self.onecmd(cmd)
|
2011-08-28 19:20:27 +00:00
|
|
|
if opt in ('-g','--goto'):
|
2012-01-10 17:56:52 +00:00
|
|
|
cmd = "goto %s" % value
|
|
|
|
self.onecmd(cmd)
|
2011-08-21 20:33:37 +00:00
|
|
|
if opt in ('-m','--mpu'):
|
2012-01-01 23:47:24 +00:00
|
|
|
if self._get_mpu(value) is None:
|
2012-01-05 05:18:24 +00:00
|
|
|
mpus = ', '.join(self.Microprocessors.keys())
|
|
|
|
msg = "Fatal: no such MPU. Available MPUs: " + mpus
|
|
|
|
self._output(msg)
|
2011-08-28 19:20:27 +00:00
|
|
|
sys.exit(1)
|
2012-01-10 17:56:52 +00:00
|
|
|
cmd = "mpu %s" % value
|
|
|
|
self.onecmd(cmd)
|
2011-08-28 19:20:27 +00:00
|
|
|
elif opt in ("-h", "--help"):
|
2012-01-05 05:22:56 +00:00
|
|
|
self._usage()
|
|
|
|
self._exit(1)
|
2011-08-28 19:20:27 +00:00
|
|
|
|
2012-01-05 05:22:56 +00:00
|
|
|
def _usage(self):
|
2012-01-01 23:56:56 +00:00
|
|
|
usage = __doc__ % sys.argv[0]
|
|
|
|
self._output(usage)
|
2011-08-21 20:33:37 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def onecmd(self, line):
|
2008-09-08 17:43:34 +00:00
|
|
|
line = self._preprocess_line(line)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
result = None
|
|
|
|
try:
|
|
|
|
result = cmd.Cmd.onecmd(self, line)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
self._output("Interrupt")
|
|
|
|
except Exception,e:
|
2009-08-22 17:56:39 +00:00
|
|
|
(file, fun, line), t, v, tbinfo = compact_traceback()
|
2008-09-08 03:44:22 +00:00
|
|
|
error = 'Error: %s, %s: file: %s line: %s' % (t, v, file, line)
|
|
|
|
self._output(error)
|
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
self._update_prompt()
|
|
|
|
return result
|
|
|
|
|
2009-04-09 03:50:44 +00:00
|
|
|
def _reset(self, mpu_type):
|
|
|
|
self._mpu = mpu_type()
|
2011-08-20 20:50:26 +00:00
|
|
|
self.addrWidth = self._mpu.addrWidth
|
|
|
|
self.byteWidth = self._mpu.byteWidth
|
|
|
|
self.addrFmt = self._mpu.addrFmt
|
|
|
|
self.byteFmt = self._mpu.byteFmt
|
|
|
|
self.addrMask = self._mpu.addrMask
|
|
|
|
self.byteMask = self._mpu.byteMask
|
2009-04-09 03:50:44 +00:00
|
|
|
self._install_mpu_observers()
|
|
|
|
self._address_parser = AddressParser()
|
|
|
|
self._disassembler = Disassembler(self._mpu, self._address_parser)
|
|
|
|
self._assembler = Assembler(self._mpu, self._address_parser)
|
|
|
|
|
2008-11-29 07:06:38 +00:00
|
|
|
def _add_shortcuts(self):
|
|
|
|
self._shortcuts = {'~': 'tilde',
|
|
|
|
'?': 'help',
|
2009-08-11 00:38:57 +00:00
|
|
|
'a': 'assemble',
|
2008-11-29 07:06:38 +00:00
|
|
|
'al': 'add_label',
|
|
|
|
'd': 'disassemble',
|
|
|
|
'dl': 'delete_label',
|
|
|
|
'f': 'fill',
|
2009-11-26 02:26:29 +00:00
|
|
|
'>': 'fill',
|
2008-11-29 07:06:38 +00:00
|
|
|
'g': 'goto',
|
|
|
|
'l': 'load',
|
|
|
|
'm': 'mem',
|
2012-01-10 17:58:45 +00:00
|
|
|
'q': 'quit',
|
2008-11-29 07:06:38 +00:00
|
|
|
'r': 'registers',
|
|
|
|
'ret': 'return',
|
|
|
|
'rad': 'radix',
|
2009-08-23 03:15:55 +00:00
|
|
|
's': 'save',
|
2008-11-29 07:06:38 +00:00
|
|
|
'shl': 'show_labels',
|
|
|
|
'x': 'quit',
|
|
|
|
'z': 'step'}
|
|
|
|
|
2008-09-08 17:43:34 +00:00
|
|
|
def _preprocess_line(self, line):
|
2009-08-22 03:49:26 +00:00
|
|
|
# line comments
|
|
|
|
quoted = False
|
|
|
|
for pos, char in enumerate(line):
|
|
|
|
if char in ('"', "'"):
|
|
|
|
quoted = not quoted
|
|
|
|
if (not quoted) and (char == ';'):
|
|
|
|
line = line[:pos]
|
|
|
|
break
|
|
|
|
|
2009-08-22 18:27:32 +00:00
|
|
|
# whitespace & leading dots
|
2009-08-22 03:49:26 +00:00
|
|
|
line = line.strip(' \t').lstrip('.')
|
2009-08-22 18:27:32 +00:00
|
|
|
|
|
|
|
# special case for vice compatibility
|
|
|
|
if line.startswith('~'):
|
|
|
|
line = self._shortcuts['~'] + ' ' + line[1:]
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-11-29 07:06:38 +00:00
|
|
|
# command shortcuts
|
|
|
|
for shortcut, command in self._shortcuts.iteritems():
|
2009-08-22 03:49:26 +00:00
|
|
|
if line == shortcut:
|
|
|
|
line = command
|
|
|
|
break
|
|
|
|
|
2008-11-29 07:06:38 +00:00
|
|
|
pattern = '^%s\s+' % re.escape(shortcut)
|
|
|
|
matches = re.match(pattern, line)
|
|
|
|
if matches:
|
|
|
|
start, end = matches.span()
|
|
|
|
line = "%s %s" % (command, line[end:])
|
2008-09-08 17:43:34 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
return line
|
|
|
|
|
2012-01-01 23:47:24 +00:00
|
|
|
def _get_mpu(self, name):
|
|
|
|
requested = name.lower()
|
|
|
|
mpu = None
|
|
|
|
for key, klass in self.Microprocessors.items():
|
|
|
|
if key.lower() == requested:
|
|
|
|
mpu = klass
|
|
|
|
break
|
|
|
|
return mpu
|
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def _install_mpu_observers(self):
|
2009-08-06 22:30:16 +00:00
|
|
|
def putc(address, value):
|
2008-09-08 03:44:22 +00:00
|
|
|
self.stdout.write(chr(value))
|
2008-11-17 05:59:59 +00:00
|
|
|
self.stdout.flush()
|
|
|
|
|
2009-08-06 22:30:16 +00:00
|
|
|
def getc(address):
|
2009-08-22 17:56:39 +00:00
|
|
|
char = console.getch_noblock(self.stdin)
|
2009-08-19 05:19:05 +00:00
|
|
|
if char:
|
|
|
|
byte = ord(char)
|
|
|
|
else:
|
|
|
|
byte = 0
|
|
|
|
return byte
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2011-08-22 14:45:20 +00:00
|
|
|
def blocking_getc(address):
|
|
|
|
return ord(console.getch(self.stdin))
|
|
|
|
|
2011-08-20 20:50:26 +00:00
|
|
|
m = ObservableMemory(addrWidth=self.addrWidth)
|
2009-04-05 21:57:13 +00:00
|
|
|
m.subscribe_to_write([0xF001], putc)
|
|
|
|
m.subscribe_to_read([0xF004], getc)
|
2011-08-22 14:45:20 +00:00
|
|
|
m.subscribe_to_read([0xF005], blocking_getc)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
self._mpu.memory = m
|
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def _update_prompt(self):
|
2009-08-19 04:33:48 +00:00
|
|
|
self.prompt = "\n%s\n." % repr(self._mpu)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def _output(self, stuff):
|
|
|
|
if stuff is not None:
|
2009-08-19 04:33:48 +00:00
|
|
|
self.stdout.write(stuff + "\n")
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2012-01-05 05:20:00 +00:00
|
|
|
def _exit(self, exitcode=0):
|
|
|
|
sys.exit(exitcode)
|
|
|
|
|
2009-08-22 20:12:26 +00:00
|
|
|
def do_help(self, args):
|
|
|
|
args = self._shortcuts.get(args.strip(), args)
|
|
|
|
return cmd.Cmd.do_help(self, args)
|
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_version(self):
|
|
|
|
self._output("version\t\tDisplay Py65 version information.")
|
|
|
|
|
|
|
|
def do_version(self, args):
|
2009-08-19 04:33:48 +00:00
|
|
|
self._output("\nPy65 Monitor")
|
2008-09-07 06:07:58 +00:00
|
|
|
|
|
|
|
def help_help(self):
|
|
|
|
self._output("help\t\tPrint a list of available actions.")
|
|
|
|
self._output("help <action>\tPrint help for <action>.")
|
|
|
|
|
|
|
|
def help_reset(self):
|
|
|
|
self._output("reset\t\tReset the microprocessor")
|
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def do_reset(self, args):
|
2009-04-09 03:50:44 +00:00
|
|
|
klass = self._mpu.__class__
|
|
|
|
self._reset(mpu_type=klass)
|
|
|
|
|
2012-01-01 23:35:32 +00:00
|
|
|
def do_mpu(self, args):
|
2009-04-09 03:50:44 +00:00
|
|
|
def available_mpus():
|
2012-01-01 23:47:24 +00:00
|
|
|
mpu_list = ', '.join(self.Microprocessors.keys())
|
2012-01-01 23:35:32 +00:00
|
|
|
self._output("Available MPUs: %s" % mpu_list)
|
|
|
|
|
|
|
|
if args == '':
|
2009-04-09 03:50:44 +00:00
|
|
|
self._output("Current MPU is %s" % self._mpu.name)
|
|
|
|
available_mpus()
|
|
|
|
else:
|
2012-01-01 23:47:24 +00:00
|
|
|
new_mpu = self._get_mpu(args)
|
2009-04-09 03:50:44 +00:00
|
|
|
if new_mpu is None:
|
|
|
|
self._output("Unknown MPU: %s" % args)
|
|
|
|
available_mpus()
|
|
|
|
else:
|
|
|
|
self._reset(new_mpu)
|
|
|
|
self._output("Reset with new MPU %s" % self._mpu.name)
|
|
|
|
|
|
|
|
def help_mpu(self):
|
|
|
|
self._output("mpu\t\tPrint available microprocessors.")
|
|
|
|
self._output("mpu <type>\tSelect a new microprocessor.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_EOF(self, args):
|
|
|
|
self._output('')
|
|
|
|
return 1
|
|
|
|
|
|
|
|
def help_EOF(self):
|
|
|
|
self._output("To quit, type ^D or use the quit command.")
|
|
|
|
|
|
|
|
def do_quit(self, args):
|
2009-03-16 05:26:47 +00:00
|
|
|
return self.do_EOF(args)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_quit(self):
|
|
|
|
return self.help_EOF()
|
|
|
|
|
2008-11-21 05:44:25 +00:00
|
|
|
def do_assemble(self, args):
|
2008-11-21 18:21:25 +00:00
|
|
|
split = args.split(None, 1)
|
|
|
|
if len(split) != 2:
|
2009-08-19 06:12:55 +00:00
|
|
|
return self._interactive_assemble(args)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-11-21 18:21:25 +00:00
|
|
|
start, statement = split
|
2009-03-30 05:06:47 +00:00
|
|
|
try:
|
|
|
|
start = self._address_parser.number(start)
|
|
|
|
except KeyError:
|
|
|
|
self._output("Bad label: %s" % start)
|
|
|
|
return
|
2008-11-21 05:44:25 +00:00
|
|
|
|
2009-08-16 00:21:49 +00:00
|
|
|
bytes = self._assembler.assemble(statement, start)
|
2008-11-21 05:44:25 +00:00
|
|
|
if bytes is None:
|
2009-03-30 05:06:47 +00:00
|
|
|
self._output("Assemble failed: %s" % statement)
|
2008-11-21 05:44:25 +00:00
|
|
|
else:
|
|
|
|
end = start + len(bytes)
|
|
|
|
self._mpu.memory[start:end] = bytes
|
2011-08-20 20:50:26 +00:00
|
|
|
self.do_disassemble((self.addrFmt+":"+self.addrFmt) % (start, end))
|
2008-11-21 05:44:25 +00:00
|
|
|
|
2008-11-21 18:21:25 +00:00
|
|
|
def help_assemble(self):
|
|
|
|
self._output("assemble <address> <statement>")
|
|
|
|
self._output("Assemble a statement at the address.")
|
|
|
|
|
2009-08-19 06:12:55 +00:00
|
|
|
def _interactive_assemble(self, args):
|
|
|
|
if args == '':
|
|
|
|
start = self._mpu.pc
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
start = self._address_parser.number(args)
|
|
|
|
except KeyError:
|
|
|
|
self._output("Bad label: %s" % start)
|
|
|
|
return
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2009-08-19 06:12:55 +00:00
|
|
|
assembling = True
|
|
|
|
|
|
|
|
while assembling:
|
2011-08-20 20:50:26 +00:00
|
|
|
prompt = "\r$" + ( self.addrFmt % start ) + " " + (" " * (1 + self.byteWidth/4) * 3)
|
2012-01-01 23:35:32 +00:00
|
|
|
line = console.line_input(prompt,
|
2009-08-22 17:56:39 +00:00
|
|
|
stdin=self.stdin, stdout=self.stdout)
|
2009-08-19 06:12:55 +00:00
|
|
|
|
|
|
|
if not line:
|
|
|
|
self.stdout.write("\n")
|
|
|
|
return
|
2009-08-22 17:56:39 +00:00
|
|
|
|
|
|
|
# assemble into memory
|
2011-08-20 21:35:03 +00:00
|
|
|
bytes = self._assembler.assemble(line, pc=start)
|
2009-08-19 06:12:55 +00:00
|
|
|
if bytes is None:
|
2011-08-20 20:50:26 +00:00
|
|
|
self.stdout.write("\r$" + (self.addrFmt % start) + " ???\n")
|
2009-08-22 04:01:09 +00:00
|
|
|
continue
|
2009-08-19 06:12:55 +00:00
|
|
|
end = start + len(bytes)
|
2012-01-01 23:35:32 +00:00
|
|
|
self._mpu.memory[start:end] = bytes
|
2009-08-19 06:12:55 +00:00
|
|
|
|
2009-08-22 17:56:39 +00:00
|
|
|
# print disassembly
|
2009-08-19 06:12:55 +00:00
|
|
|
bytes, disasm = self._disassembler.instruction_at(start)
|
2009-08-22 17:56:39 +00:00
|
|
|
disassembly = self._format_disassembly(start, bytes, disasm)
|
|
|
|
self.stdout.write("\r" + (' ' * (len(prompt+line) + 5) ) + "\r")
|
|
|
|
self.stdout.write(disassembly + "\n")
|
2009-08-19 06:12:55 +00:00
|
|
|
|
|
|
|
start += bytes
|
|
|
|
|
2008-11-18 06:57:17 +00:00
|
|
|
def do_disassemble(self, args):
|
|
|
|
start, end = self._address_parser.range(args)
|
|
|
|
if start == end:
|
|
|
|
end += 1
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-11-18 06:57:17 +00:00
|
|
|
address = start
|
|
|
|
while address < end:
|
|
|
|
bytes, disasm = self._disassembler.instruction_at(address)
|
2012-01-01 23:35:32 +00:00
|
|
|
self._output(self._format_disassembly(address, bytes, disasm))
|
2008-11-18 06:57:17 +00:00
|
|
|
address += bytes
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2009-08-22 17:56:39 +00:00
|
|
|
def _format_disassembly(self, address, bytes, disasm):
|
|
|
|
mem = ''
|
|
|
|
for byte in self._mpu.memory[address:address+bytes]:
|
2011-08-20 20:50:26 +00:00
|
|
|
mem += self.byteFmt % byte + " "
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2011-08-20 20:50:26 +00:00
|
|
|
fieldwidth = 1 + (1 + self.byteWidth/4) * 3
|
|
|
|
fieldfmt = "%%-%ds" % fieldwidth
|
|
|
|
return "$" + self.addrFmt % address + " " + fieldfmt % mem + disasm
|
2008-11-18 06:57:17 +00:00
|
|
|
|
|
|
|
def help_disassemble(self):
|
|
|
|
self._output("disassemble <address_range>")
|
|
|
|
self._output("Disassemble instructions in the address range.")
|
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_step(self):
|
2009-08-23 05:14:33 +00:00
|
|
|
self._output("step")
|
2008-09-07 06:07:58 +00:00
|
|
|
self._output("Single-step through instructions.")
|
|
|
|
|
|
|
|
def do_step(self, args):
|
|
|
|
self._mpu.step()
|
2011-08-20 20:50:26 +00:00
|
|
|
self.do_disassemble(self.addrFmt % self._mpu.pc)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_return(self):
|
2009-08-23 05:14:33 +00:00
|
|
|
self._output("return")
|
2008-09-07 06:07:58 +00:00
|
|
|
self._output("Continues execution and returns to the monitor just")
|
|
|
|
self._output("before the next RTS or RTI is executed.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_return(self, args):
|
2008-09-08 03:44:22 +00:00
|
|
|
returns = [0x60, 0x40] # RTS, RTI
|
|
|
|
self._run(stopcodes=returns)
|
|
|
|
|
2008-11-18 06:57:17 +00:00
|
|
|
def help_goto(self):
|
|
|
|
self._output("goto <address>")
|
|
|
|
self._output("Change the PC to address and continue execution.")
|
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def do_goto(self, args):
|
2008-09-10 03:54:36 +00:00
|
|
|
self._mpu.pc = self._address_parser.number(args)
|
2008-09-08 03:44:22 +00:00
|
|
|
brks = [0x00] # BRK
|
|
|
|
self._run(stopcodes=brks)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def _run(self, stopcodes=[]):
|
2008-09-07 06:07:58 +00:00
|
|
|
last_instruct = None
|
2008-09-08 03:44:22 +00:00
|
|
|
while last_instruct not in stopcodes:
|
2008-09-07 06:07:58 +00:00
|
|
|
self._mpu.step()
|
|
|
|
last_instruct = self._mpu.memory[self._mpu.pc]
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_radix(self):
|
|
|
|
self._output("radix [H|D|O|B]")
|
|
|
|
self._output("Set the default radix to hex, decimal, octal, or binary.")
|
|
|
|
self._output("With no argument, the current radix is printed.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_cycles(self):
|
|
|
|
self._output("Display the total number of cycles executed.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_cycles(self, args):
|
|
|
|
self._output(str(self._mpu.processorCycles))
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_radix(self, args):
|
|
|
|
radixes = {'Hexadecimal': 16, 'Decimal': 10, 'Octal': 8, 'Binary': 2}
|
|
|
|
|
|
|
|
if args != '':
|
|
|
|
new = args[0].lower()
|
|
|
|
changed = False
|
|
|
|
for name, radix in radixes.iteritems():
|
|
|
|
if name[0].lower() == new:
|
2008-09-10 03:54:36 +00:00
|
|
|
self._address_parser.radix = radix
|
2008-09-07 06:07:58 +00:00
|
|
|
changed = True
|
|
|
|
if not changed:
|
|
|
|
self._output("Illegal radix: %s" % args)
|
|
|
|
|
|
|
|
for name, radix in radixes.iteritems():
|
2008-09-10 03:54:36 +00:00
|
|
|
if self._address_parser.radix == radix:
|
2008-09-07 06:07:58 +00:00
|
|
|
self._output("Default radix is %s" % name)
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_tilde(self):
|
|
|
|
self._output("~ <number>")
|
|
|
|
self._output("Display the specified number in decimal, hex, octal, and binary.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
|
|
|
def do_tilde(self, args):
|
2008-09-07 06:07:58 +00:00
|
|
|
try:
|
2008-09-10 03:54:36 +00:00
|
|
|
num = self._address_parser.number(args)
|
2008-09-07 06:07:58 +00:00
|
|
|
except ValueError:
|
|
|
|
self._output("Syntax error: %s" % args)
|
|
|
|
return
|
|
|
|
|
|
|
|
self._output("+%u" % num)
|
2011-08-20 20:50:26 +00:00
|
|
|
self._output("$" + self.byteFmt % num)
|
2008-09-07 06:07:58 +00:00
|
|
|
self._output("%04o" % num)
|
2008-09-08 03:44:22 +00:00
|
|
|
self._output(itoa(num, 2).zfill(8))
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_registers(self):
|
|
|
|
self._output("registers[<reg_name> = <number> [, <reg_name> = <number>]*]")
|
|
|
|
self._output("Assign respective registers. With no parameters,")
|
|
|
|
self._output("display register values.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_registers(self, args):
|
|
|
|
if args == '':
|
|
|
|
return
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
pairs = re.findall('([^=,\s]*)=([^=,\s]*)', args)
|
|
|
|
if pairs == []:
|
|
|
|
return self._output("Syntax error: %s" % args)
|
|
|
|
|
|
|
|
for register, value in pairs:
|
2009-08-17 01:52:47 +00:00
|
|
|
if register not in ('pc', 'sp', 'a', 'x', 'y', 'p'):
|
2008-09-07 06:07:58 +00:00
|
|
|
self._output("Invalid register: %s" % register)
|
|
|
|
else:
|
|
|
|
try:
|
2011-08-20 20:50:26 +00:00
|
|
|
intval = self._address_parser.number(value) & self.addrMask
|
2008-09-07 06:07:58 +00:00
|
|
|
if len(register) == 1:
|
2011-08-20 20:50:26 +00:00
|
|
|
intval &= self.byteMask
|
2008-09-07 06:07:58 +00:00
|
|
|
setattr(self._mpu, register, intval)
|
2009-03-16 05:26:47 +00:00
|
|
|
except KeyError, why:
|
|
|
|
self._output(why[0])
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_cd(self, args):
|
|
|
|
self._output("cd <directory>")
|
|
|
|
self._output("Change the working directory.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_cd(self, args):
|
|
|
|
try:
|
|
|
|
os.chdir(args)
|
|
|
|
except OSError, why:
|
|
|
|
msg = "Cannot change directory: [%d] %s" % (why[0], why[1])
|
|
|
|
self._output(msg)
|
|
|
|
self.do_pwd()
|
|
|
|
|
|
|
|
def help_pwd(self):
|
|
|
|
self._output("Show the current working directory.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_pwd(self, args=None):
|
|
|
|
cwd = os.getcwd()
|
|
|
|
self._output(cwd)
|
|
|
|
|
|
|
|
def help_load(self):
|
|
|
|
self._output("load \"filename\" <address>")
|
|
|
|
self._output("Load the specified file into memory at the specified address.")
|
|
|
|
self._output("Commodore-style load address bytes are ignored.")
|
|
|
|
|
|
|
|
def do_load(self, args):
|
|
|
|
split = shlex.split(args)
|
|
|
|
if len(split) > 2:
|
|
|
|
self._output("Syntax error: %s" % args)
|
|
|
|
return
|
|
|
|
|
|
|
|
filename = split[0]
|
|
|
|
if len(split) == 2:
|
2011-08-28 19:20:27 +00:00
|
|
|
# if the start address is -1, we will adjust it later
|
2008-09-10 03:54:36 +00:00
|
|
|
start = self._address_parser.number(split[1])
|
2008-09-07 06:07:58 +00:00
|
|
|
else:
|
|
|
|
start = self._mpu.pc
|
|
|
|
|
2012-01-03 01:13:40 +00:00
|
|
|
if "://" in filename:
|
|
|
|
try:
|
|
|
|
f = urllib2.urlopen(filename)
|
|
|
|
bytes = f.read()
|
|
|
|
f.close()
|
|
|
|
except (urllib2.URLError, urllib2.HTTPError), why:
|
|
|
|
msg = "Cannot fetch remote file: %s" % str(why)
|
|
|
|
self._output(msg)
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
f = open(filename, 'rb')
|
|
|
|
bytes = f.read()
|
|
|
|
f.close()
|
|
|
|
except (OSError, IOError), why:
|
|
|
|
msg = "Cannot load file: [%d] %s" % (why[0], why[1])
|
|
|
|
self._output(msg)
|
|
|
|
return
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2011-08-28 19:20:27 +00:00
|
|
|
# if the start address was -1, we load to top of memory
|
|
|
|
if start == -1:
|
|
|
|
start = self.addrMask - len(bytes)/(self.byteWidth/8) + 1
|
|
|
|
|
2011-08-21 09:51:15 +00:00
|
|
|
if self.byteWidth==8:
|
|
|
|
bytes=map(ord, bytes)
|
|
|
|
elif self.byteWidth==16:
|
2011-08-22 10:02:04 +00:00
|
|
|
bytes=map(lambda msb,lsb: (ord(msb)<<8)+ord(lsb),bytes[0::2],bytes[1::2])
|
2011-08-21 09:51:15 +00:00
|
|
|
|
|
|
|
self._fill(start, start, bytes)
|
2009-08-23 03:15:55 +00:00
|
|
|
|
|
|
|
def do_save(self, args):
|
|
|
|
split = shlex.split(args)
|
|
|
|
if len(split) != 3:
|
|
|
|
self._output("Syntax error: %s" % args)
|
|
|
|
return
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2009-08-23 03:15:55 +00:00
|
|
|
filename = split[0]
|
|
|
|
start = self._address_parser.number(split[1])
|
|
|
|
end = self._address_parser.number(split[2])
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2009-08-23 03:15:55 +00:00
|
|
|
bytes = self._mpu.memory[start:end+1]
|
|
|
|
try:
|
|
|
|
f = open(filename, 'wb')
|
|
|
|
for byte in bytes:
|
2011-08-22 10:02:04 +00:00
|
|
|
# output each octect from msb first
|
|
|
|
for shift in range (self.byteWidth-8,-1,-8):
|
|
|
|
f.write(chr((byte>>shift) & 0xff))
|
2009-08-23 03:15:55 +00:00
|
|
|
f.close()
|
|
|
|
except (OSError, IOError), why:
|
|
|
|
msg = "Cannot save file: [%d] %s" % (why[0], why[1])
|
|
|
|
self._output(msg)
|
|
|
|
return
|
2012-01-01 23:35:32 +00:00
|
|
|
|
|
|
|
self._output("Saved +%d bytes to %s" % (len(bytes), filename))
|
2009-08-23 03:15:55 +00:00
|
|
|
|
|
|
|
def help_save(self):
|
|
|
|
self._output("save \"filename\" <start> <end>")
|
|
|
|
self._output("Save the specified memory range to disk as a binary file.")
|
|
|
|
self._output("Commodore-style load address bytes are not written.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_fill(self):
|
2008-11-29 07:06:38 +00:00
|
|
|
self._output("fill <address_range> <data_list>")
|
|
|
|
self._output("Fill memory in the specified address range with the data in")
|
|
|
|
self._output("<data_list>. If the size of the address range is greater")
|
|
|
|
self._output("than the size of the data_list, the data_list is repeated.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_fill(self, args):
|
|
|
|
split = shlex.split(args)
|
2008-09-08 03:44:22 +00:00
|
|
|
if len(split) < 2:
|
2008-09-07 06:07:58 +00:00
|
|
|
self._output("Syntax error: %s" % args)
|
|
|
|
return
|
|
|
|
|
2008-09-10 03:54:36 +00:00
|
|
|
start, end = self._address_parser.range(split[0])
|
|
|
|
filler = map(self._address_parser.number, split[1:])
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
self._fill(start, end, filler)
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def _fill(self, start, end, filler):
|
2008-09-07 06:07:58 +00:00
|
|
|
address = start
|
2008-09-08 03:44:22 +00:00
|
|
|
length, index = len(filler), 0
|
|
|
|
|
|
|
|
if start == end:
|
|
|
|
end = start + length - 1
|
2011-08-20 20:50:26 +00:00
|
|
|
if (end > self.addrMask):
|
|
|
|
end = self.addrMask
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
while address <= end:
|
2011-08-20 20:50:26 +00:00
|
|
|
address &= self.addrMask
|
|
|
|
self._mpu.memory[address] = (filler[index] & self.byteMask)
|
2008-09-08 03:44:22 +00:00
|
|
|
index += 1
|
|
|
|
if index == length:
|
|
|
|
index = 0
|
2008-09-07 06:07:58 +00:00
|
|
|
address += 1
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
fmt = (end - start + 1, start, end)
|
2011-08-20 20:50:26 +00:00
|
|
|
starttoend = "$" + self.addrFmt + " to $" + self.addrFmt
|
|
|
|
self._output(("Wrote +%d bytes from " + starttoend) % fmt)
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def help_mem(self):
|
|
|
|
self._output("mem <address_range>")
|
|
|
|
self._output("Display the contents of memory.")
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-07 06:07:58 +00:00
|
|
|
def do_mem(self, args):
|
2008-09-10 03:54:36 +00:00
|
|
|
start, end = self._address_parser.range(args)
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2011-08-20 20:50:26 +00:00
|
|
|
line = self.addrFmt % start + ":"
|
2008-11-29 23:32:46 +00:00
|
|
|
for address in range(start, end+1):
|
|
|
|
byte = self._mpu.memory[address]
|
2012-01-01 23:35:32 +00:00
|
|
|
more = " " + self.byteFmt % byte
|
|
|
|
|
2008-11-29 23:32:46 +00:00
|
|
|
exceeded = len(line) + len(more) > self._width
|
|
|
|
if exceeded:
|
|
|
|
self._output(line)
|
2011-08-20 20:50:26 +00:00
|
|
|
line = self.addrFmt % address + ":"
|
2008-11-29 23:32:46 +00:00
|
|
|
line += more
|
|
|
|
self._output(line)
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2008-11-18 06:57:17 +00:00
|
|
|
def help_add_label(self):
|
|
|
|
self._output("add_label <address> <label>")
|
|
|
|
self._output("Map a given address to a label.")
|
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def do_add_label(self, args):
|
|
|
|
split = shlex.split(args)
|
|
|
|
if len(split) != 2:
|
|
|
|
self._output("Syntax error: %s" % args)
|
|
|
|
return
|
2012-01-01 23:35:32 +00:00
|
|
|
|
|
|
|
address = self._address_parser.number(split[0])
|
2008-09-08 03:44:22 +00:00
|
|
|
label = split[1]
|
|
|
|
|
2008-09-10 03:54:36 +00:00
|
|
|
self._address_parser.labels[label] = address
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2008-11-18 06:57:17 +00:00
|
|
|
def help_show_labels(self):
|
|
|
|
self._output("show_labels")
|
|
|
|
self._output("Display current label mappings.")
|
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def do_show_labels(self, args):
|
2008-09-10 03:54:36 +00:00
|
|
|
values = self._address_parser.labels.values()
|
|
|
|
keys = self._address_parser.labels.keys()
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2008-09-10 03:54:36 +00:00
|
|
|
byaddress = zip(values, keys)
|
2008-09-08 03:44:22 +00:00
|
|
|
byaddress.sort()
|
|
|
|
for address, label in byaddress:
|
2011-08-20 20:50:26 +00:00
|
|
|
self._output(self.addrFmt % address + ": " + label)
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2008-11-18 06:57:17 +00:00
|
|
|
def help_delete_label(self):
|
|
|
|
self._output("delete_label <label>")
|
|
|
|
self._output("Remove the specified label from the label tables.")
|
|
|
|
|
2008-09-08 03:44:22 +00:00
|
|
|
def do_delete_label(self, args):
|
2009-03-17 06:23:17 +00:00
|
|
|
if args == '':
|
|
|
|
return self.help_delete_label()
|
2012-01-01 23:35:32 +00:00
|
|
|
|
2012-01-06 04:12:51 +00:00
|
|
|
if args in self._address_parser.labels:
|
2008-09-10 03:54:36 +00:00
|
|
|
del self._address_parser.labels[args]
|
2008-09-08 03:44:22 +00:00
|
|
|
|
2008-11-29 23:32:46 +00:00
|
|
|
def do_width(self, args):
|
|
|
|
if args != '':
|
|
|
|
try:
|
|
|
|
new_width = int(args)
|
|
|
|
if new_width >= 10:
|
|
|
|
self._width = new_width
|
|
|
|
else:
|
|
|
|
self._output("Minimum terminal width is 10")
|
|
|
|
except ValueError:
|
|
|
|
self._output("Illegal width: %s" % args)
|
|
|
|
|
|
|
|
self._output("Terminal width is %d" % self._width)
|
|
|
|
|
|
|
|
def help_width(self):
|
|
|
|
self._output("width <columns>")
|
|
|
|
self._output("Set the width used by some commands to wrap output.")
|
|
|
|
self._output("With no argument, the current width is printed.")
|
2008-09-07 06:07:58 +00:00
|
|
|
|
2009-04-09 03:50:44 +00:00
|
|
|
def main(args=None):
|
|
|
|
c = Monitor()
|
2008-09-07 06:07:58 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
import readline
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
c.onecmd('version')
|
|
|
|
c.cmdloop()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
c._output('')
|
|
|
|
pass
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|