mirror of
https://github.com/RevCurtisP/C02.git
synced 2024-11-23 23:33:03 +00:00
Added customized py65 monitor
Script bin/py65m contains stub class FileSystem to allow file system access functionality
This commit is contained in:
parent
fa19c4bbde
commit
f713118fe6
910
bin/py65m.py
Normal file
910
bin/py65m.py
Normal file
@ -0,0 +1,910 @@
|
||||
#!/usr/bin/env python -u
|
||||
|
||||
"""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
|
||||
-i, --input <address> : define location of getc (default $f004)
|
||||
-o, --output <address> : define location of putc (default $f001)
|
||||
"""
|
||||
|
||||
import cmd
|
||||
import getopt
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from asyncore import compact_traceback
|
||||
from py65.devices.mpu6502 import MPU as NMOS6502
|
||||
from py65.devices.mpu65c02 import MPU as CMOS65C02
|
||||
from py65.devices.mpu65org16 import MPU as V65Org16
|
||||
from py65.disassembler import Disassembler
|
||||
from py65.assembler import Assembler
|
||||
from py65.utils.addressing import AddressParser
|
||||
from py65.utils import console
|
||||
from py65.utils.conversions import itoa
|
||||
from py65.memory import ObservableMemory
|
||||
|
||||
try:
|
||||
from urllib2 import urlopen
|
||||
except ImportError: # Python 3
|
||||
from urllib.request import urlopen
|
||||
|
||||
class FileSystem():
|
||||
|
||||
def __init__(self):
|
||||
self.registers = 8
|
||||
self.register = {}
|
||||
|
||||
def write_mem(self, address, value):
|
||||
self.register[address] = value;
|
||||
|
||||
def read_mem(self, address):
|
||||
return self.register[address]
|
||||
|
||||
class Monitor(cmd.Cmd):
|
||||
|
||||
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02,
|
||||
'65Org16': V65Org16}
|
||||
|
||||
def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None,
|
||||
stdout=None, argv=None):
|
||||
self.mpu_type = mpu_type
|
||||
self.putc_addr = 0xF001
|
||||
self.getc_addr = 0xF004
|
||||
self.fsio_addr = 0xF100
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
self._breakpoints = []
|
||||
self._width = 78
|
||||
self.prompt = "."
|
||||
self._add_shortcuts()
|
||||
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
||||
self._parse_args(argv)
|
||||
self._reset(self.mpu_type,self.getc_addr,self.putc_addr,self.fsio_addr)
|
||||
|
||||
def _parse_args(self, argv):
|
||||
try:
|
||||
shortopts = 'hf:i:o:m:l:r:g:'
|
||||
longopts = ['help', 'mpu=', 'fileio=', 'input=', 'output=', 'load=', 'rom=', 'goto=']
|
||||
options, args = getopt.getopt(argv[1:], shortopts, longopts)
|
||||
except getopt.GetoptError as exc:
|
||||
self._output(exc.args[0])
|
||||
self._usage()
|
||||
self._exit(1)
|
||||
|
||||
load = None
|
||||
rom = None
|
||||
goto = None
|
||||
mpu = None
|
||||
|
||||
for opt, value in options:
|
||||
if opt in ('-i', '--input'):
|
||||
self.getc_addr = int(value, 16)
|
||||
|
||||
if opt in ('-o', '--output'):
|
||||
self.putc_addr = int(value, 16)
|
||||
|
||||
if opt in ('-f', '--fileio'):
|
||||
self.fsio_addr = int(value, 16)
|
||||
|
||||
if opt in ('-l', '--load'):
|
||||
load = value
|
||||
|
||||
if opt in ('-r', '--rom'):
|
||||
rom = value
|
||||
|
||||
if opt in ('-g', '--goto'):
|
||||
goto = value
|
||||
|
||||
if opt in ('-m', '--mpu'):
|
||||
mpu = value
|
||||
|
||||
elif opt in ("-h", "--help"):
|
||||
self._usage()
|
||||
self._exit(0)
|
||||
|
||||
if (mpu is not None) or (rom is not None):
|
||||
if mpu is None:
|
||||
mpu = "6502"
|
||||
if self._get_mpu(mpu) is None:
|
||||
mpus = list(self.Microprocessors.keys())
|
||||
mpus.sort()
|
||||
msg = "Fatal: no such MPU. Available MPUs: %s"
|
||||
self._output(msg % ', '.join(mpus))
|
||||
sys.exit(1)
|
||||
cmd = "mpu %s" % mpu
|
||||
self.onecmd(cmd)
|
||||
|
||||
if load is not None:
|
||||
cmd = "load %s" % load
|
||||
self.onecmd(cmd)
|
||||
|
||||
if goto is not None:
|
||||
cmd = "goto %s" % goto
|
||||
self.onecmd(cmd)
|
||||
|
||||
if rom is not None:
|
||||
# load a ROM and run from the reset vector
|
||||
cmd = "load '%s' top" % rom
|
||||
self.onecmd(cmd)
|
||||
physMask = self._mpu.memory.physMask
|
||||
reset = self._mpu.RESET & physMask
|
||||
dest = self._mpu.memory[reset] + \
|
||||
(self._mpu.memory[reset + 1] << self.byteWidth)
|
||||
cmd = "goto %08x" % dest
|
||||
self.onecmd(cmd)
|
||||
|
||||
def _usage(self):
|
||||
usage = __doc__ % sys.argv[0]
|
||||
self._output(usage)
|
||||
|
||||
def onecmd(self, line):
|
||||
line = self._preprocess_line(line)
|
||||
|
||||
result = None
|
||||
try:
|
||||
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)
|
||||
self._output(error)
|
||||
|
||||
if not line.startswith("quit"):
|
||||
self._output_mpu_status()
|
||||
|
||||
return result
|
||||
|
||||
def _reset(self, mpu_type,getc_addr=0xF004,putc_addr=0xF001,fsio_addr=0xF100):
|
||||
self._mpu = mpu_type()
|
||||
self.addrWidth = self._mpu.ADDR_WIDTH
|
||||
self.byteWidth = self._mpu.BYTE_WIDTH
|
||||
self.addrFmt = self._mpu.ADDR_FORMAT
|
||||
self.byteFmt = self._mpu.BYTE_FORMAT
|
||||
self.addrMask = self._mpu.addrMask
|
||||
self.byteMask = self._mpu.byteMask
|
||||
self._install_mpu_observers(getc_addr,putc_addr,fsio_addr)
|
||||
self._address_parser = AddressParser()
|
||||
self._disassembler = Disassembler(self._mpu, self._address_parser)
|
||||
self._assembler = Assembler(self._mpu, self._address_parser)
|
||||
|
||||
def _add_shortcuts(self):
|
||||
self._shortcuts = {'EOF': 'quit',
|
||||
'~': 'tilde',
|
||||
'a': 'assemble',
|
||||
'ab': 'add_breakpoint',
|
||||
'al': 'add_label',
|
||||
'd': 'disassemble',
|
||||
'db': 'delete_breakpoint',
|
||||
'dl': 'delete_label',
|
||||
'exit': 'quit',
|
||||
'f': 'fill',
|
||||
'>': 'fill',
|
||||
'g': 'goto',
|
||||
'h': 'help',
|
||||
'?': 'help',
|
||||
'l': 'load',
|
||||
'm': 'mem',
|
||||
'q': 'quit',
|
||||
'r': 'registers',
|
||||
'ret': 'return',
|
||||
'rad': 'radix',
|
||||
's': 'save',
|
||||
'shb': 'show_breakpoints',
|
||||
'shl': 'show_labels',
|
||||
'x': 'quit',
|
||||
'z': 'step'}
|
||||
|
||||
def _preprocess_line(self, line):
|
||||
# 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
|
||||
|
||||
# whitespace & leading dots
|
||||
line = line.strip(' \t').lstrip('.')
|
||||
|
||||
# special case for vice compatibility
|
||||
if line.startswith('~'):
|
||||
line = self._shortcuts['~'] + ' ' + line[1:]
|
||||
|
||||
# command shortcuts
|
||||
for shortcut, command in self._shortcuts.items():
|
||||
if line == shortcut:
|
||||
line = command
|
||||
break
|
||||
|
||||
pattern = '^%s\s+' % re.escape(shortcut)
|
||||
matches = re.match(pattern, line)
|
||||
if matches:
|
||||
start, end = matches.span()
|
||||
line = "%s %s" % (command, line[end:])
|
||||
break
|
||||
|
||||
return line
|
||||
|
||||
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
|
||||
|
||||
def _install_mpu_observers(self,getc_addr,putc_addr,fsio_addr):
|
||||
def putc(address, value):
|
||||
try:
|
||||
self.stdout.write(chr(value))
|
||||
except UnicodeEncodeError: # Python 3
|
||||
self.stdout.write("?")
|
||||
self.stdout.flush()
|
||||
|
||||
def getc(address):
|
||||
char = console.getch_noblock(self.stdin)
|
||||
if char:
|
||||
byte = ord(char)
|
||||
else:
|
||||
byte = 0
|
||||
return byte
|
||||
|
||||
|
||||
m = ObservableMemory(addrWidth=self.addrWidth)
|
||||
m.subscribe_to_write([self.putc_addr], putc)
|
||||
m.subscribe_to_read([self.getc_addr], getc)
|
||||
|
||||
fs = FileSystem()
|
||||
fsio_range = range(fsio_addr, fsio_addr+fs.registers)
|
||||
m.subscribe_to_write(fsio_range, fs.write_mem)
|
||||
m.subscribe_to_read(fsio_range, fs.read_mem)
|
||||
|
||||
self._mpu.memory = m
|
||||
|
||||
def _output_mpu_status(self):
|
||||
self._output("\n" + repr(self._mpu))
|
||||
|
||||
def _output(self, stuff):
|
||||
self.stdout.write("%s\n" % stuff)
|
||||
|
||||
def _exit(self, exitcode=0):
|
||||
sys.exit(exitcode)
|
||||
|
||||
def do_help(self, args):
|
||||
args = self._shortcuts.get(args.strip(), args)
|
||||
return cmd.Cmd.do_help(self, args)
|
||||
|
||||
def help_version(self):
|
||||
self._output("version\t\tDisplay Py65 version information.")
|
||||
|
||||
def do_version(self, args):
|
||||
self._output("\nPy65 Monitor")
|
||||
|
||||
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")
|
||||
|
||||
def do_reset(self, args):
|
||||
klass = self._mpu.__class__
|
||||
self._reset(mpu_type=klass)
|
||||
|
||||
def do_mpu(self, args):
|
||||
def available_mpus():
|
||||
mpus = list(self.Microprocessors.keys())
|
||||
mpus.sort()
|
||||
self._output("Available MPUs: %s" % ', '.join(mpus))
|
||||
|
||||
if args == '':
|
||||
self._output("Current MPU is %s" % self._mpu.name)
|
||||
available_mpus()
|
||||
else:
|
||||
new_mpu = self._get_mpu(args)
|
||||
if new_mpu is None:
|
||||
self._output("Unknown MPU: %s" % args)
|
||||
available_mpus()
|
||||
else:
|
||||
self._reset(new_mpu,self.getc_addr,self.putc_addr,self.fsio_addr)
|
||||
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.")
|
||||
|
||||
def do_quit(self, args):
|
||||
self._output('')
|
||||
return 1
|
||||
|
||||
def help_quit(self):
|
||||
self._output("To quit, type ^D or use the quit command.")
|
||||
|
||||
def do_assemble(self, args):
|
||||
splitted = args.split(None, 1)
|
||||
if len(splitted) != 2:
|
||||
return self._interactive_assemble(args)
|
||||
|
||||
statement = splitted[1]
|
||||
try:
|
||||
start = self._address_parser.number(splitted[0])
|
||||
bytes = self._assembler.assemble(statement, start)
|
||||
end = start + len(bytes)
|
||||
self._mpu.memory[start:end] = bytes
|
||||
self.do_disassemble(self.addrFmt % start)
|
||||
except KeyError as exc:
|
||||
self._output(exc.args[0]) # "Label not found: foo"
|
||||
except OverflowError:
|
||||
self._output("Overflow error: %s" % args)
|
||||
except SyntaxError:
|
||||
self._output("Syntax error: %s" % statement)
|
||||
|
||||
def help_assemble(self):
|
||||
self._output("assemble\t\t\t"
|
||||
"Start interactive assembly at the program counter.")
|
||||
self._output("assemble <address>\t\t"
|
||||
"Start interactive assembly at the address.")
|
||||
self._output("assemble <address> <statement>\t"
|
||||
"Assemble a statement at the address.")
|
||||
|
||||
def _interactive_assemble(self, args):
|
||||
if args == '':
|
||||
start = self._mpu.pc
|
||||
else:
|
||||
try:
|
||||
start = self._address_parser.number(args)
|
||||
except KeyError as exc:
|
||||
self._output(exc.args[0]) # "Label not found: foo"
|
||||
return
|
||||
|
||||
while True:
|
||||
prompt = "\r$" + (self.addrFmt % start) + " " + \
|
||||
(" " * int(1 + self.byteWidth / 4) * 3)
|
||||
|
||||
line = console.line_input(prompt,
|
||||
stdin=self.stdin, stdout=self.stdout)
|
||||
|
||||
if not line.strip():
|
||||
self.stdout.write("\n")
|
||||
return
|
||||
|
||||
# assemble into memory
|
||||
try:
|
||||
bytes = self._assembler.assemble(line, pc=start)
|
||||
numbytes = len(bytes)
|
||||
|
||||
end = start + numbytes
|
||||
self._mpu.memory[start:end] = bytes
|
||||
|
||||
# print disassembly
|
||||
_, disasm = self._disassembler.instruction_at(start)
|
||||
fdisasm = self._format_disassembly(start, numbytes, disasm)
|
||||
indent = ' ' * (len(prompt + line) + 5)
|
||||
self.stdout.write("\r" + indent + "\r")
|
||||
self.stdout.write(fdisasm + "\n")
|
||||
|
||||
# advance to next address
|
||||
start += numbytes
|
||||
if start >= (2 ** self._mpu.ADDR_WIDTH):
|
||||
start = 0
|
||||
except KeyError:
|
||||
addr = self.addrFmt % start
|
||||
self.stdout.write("\r$%s ?Label\n" % addr)
|
||||
except OverflowError:
|
||||
addr = self.addrFmt % start
|
||||
self.stdout.write("\r$%s ?Overflow\n" % addr)
|
||||
except SyntaxError:
|
||||
addr = self.addrFmt % start
|
||||
self.stdout.write("\r$%s ?Syntax\n" % addr)
|
||||
|
||||
def do_disassemble(self, args):
|
||||
splitted = shlex.split(args)
|
||||
if len(splitted) != 1:
|
||||
return self.help_disassemble()
|
||||
|
||||
address_parts = splitted[0].split(":")
|
||||
start = self._address_parser.number(address_parts[0])
|
||||
if len(address_parts) > 1:
|
||||
end = self._address_parser.number(address_parts[1])
|
||||
else:
|
||||
end = start
|
||||
|
||||
max_address = (2 ** self._mpu.ADDR_WIDTH) - 1
|
||||
cur_address = start
|
||||
needs_wrap = start > end
|
||||
|
||||
while needs_wrap or cur_address <= end:
|
||||
length, disasm = self._disassembler.instruction_at(cur_address)
|
||||
self._output(self._format_disassembly(cur_address, length, disasm))
|
||||
|
||||
remaining = length
|
||||
while remaining:
|
||||
remaining -= 1
|
||||
cur_address += 1
|
||||
if start > end and cur_address > max_address:
|
||||
needs_wrap = False
|
||||
cur_address = 0
|
||||
|
||||
def _format_disassembly(self, address, length, disasm):
|
||||
cur_address = address
|
||||
max_address = (2 ** self._mpu.ADDR_WIDTH) - 1
|
||||
|
||||
bytes_remaining = length
|
||||
dump = ''
|
||||
|
||||
while bytes_remaining:
|
||||
if cur_address > max_address:
|
||||
cur_address = 0
|
||||
dump += self.byteFmt % self._mpu.memory[cur_address] + " "
|
||||
cur_address += 1
|
||||
bytes_remaining -= 1
|
||||
|
||||
fieldwidth = 1 + int(1 + self.byteWidth / 4) * 3
|
||||
fieldfmt = "%%-%ds" % fieldwidth
|
||||
return "$" + self.addrFmt % address + " " + fieldfmt % dump + disasm
|
||||
|
||||
def help_disassemble(self):
|
||||
self._output("disassemble <address_range>")
|
||||
self._output("Disassemble instructions in the address range.")
|
||||
self._output('Range is specified like "<start>:<end>".')
|
||||
|
||||
def help_step(self):
|
||||
self._output("step")
|
||||
self._output("Single-step through instructions.")
|
||||
|
||||
def do_step(self, args):
|
||||
self._mpu.step()
|
||||
self.do_disassemble(self.addrFmt % self._mpu.pc)
|
||||
|
||||
def help_return(self):
|
||||
self._output("return")
|
||||
self._output("Continues execution and returns to the monitor just")
|
||||
self._output("before the next RTS or RTI is executed.")
|
||||
|
||||
def do_return(self, args):
|
||||
returns = [0x60, 0x40] # RTS, RTI
|
||||
self._run(stopcodes=returns)
|
||||
|
||||
def help_goto(self):
|
||||
self._output("goto <address>")
|
||||
self._output("Change the PC to address and continue execution.")
|
||||
|
||||
def do_goto(self, args):
|
||||
if args == '':
|
||||
return self.help_goto()
|
||||
|
||||
self._mpu.pc = self._address_parser.number(args)
|
||||
brks = [0x00] # BRK
|
||||
self._run(stopcodes=brks)
|
||||
|
||||
def _run(self, stopcodes):
|
||||
stopcodes = set(stopcodes)
|
||||
breakpoints = set(self._breakpoints)
|
||||
mpu = self._mpu
|
||||
mem = self._mpu.memory
|
||||
|
||||
if not breakpoints:
|
||||
while True:
|
||||
mpu.step()
|
||||
if mem[mpu.pc] in stopcodes:
|
||||
break
|
||||
else:
|
||||
while True:
|
||||
mpu.step()
|
||||
pc = mpu.pc
|
||||
if mem[pc] in stopcodes:
|
||||
break
|
||||
if pc in breakpoints:
|
||||
msg = "Breakpoint %d reached."
|
||||
self._output(msg % self._breakpoints.index(pc))
|
||||
break
|
||||
|
||||
def help_radix(self):
|
||||
self._output("radix [H|D|O|B]")
|
||||
self._output("Set default radix to hex, decimal, octal, or binary.")
|
||||
self._output("With no argument, the current radix is printed.")
|
||||
|
||||
def help_cycles(self):
|
||||
self._output("Display the total number of cycles executed.")
|
||||
|
||||
def do_cycles(self, args):
|
||||
self._output(str(self._mpu.processorCycles))
|
||||
|
||||
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.items():
|
||||
if name[0].lower() == new:
|
||||
self._address_parser.radix = radix
|
||||
changed = True
|
||||
if not changed:
|
||||
self._output("Illegal radix: %s" % args)
|
||||
|
||||
for name, radix in radixes.items():
|
||||
if self._address_parser.radix == radix:
|
||||
self._output("Default radix is %s" % name)
|
||||
|
||||
def help_tilde(self):
|
||||
self._output("~ <number>")
|
||||
self._output("Display a number in decimal, hex, octal, and binary.")
|
||||
|
||||
def do_tilde(self, args):
|
||||
if args == '':
|
||||
return self.help_tilde()
|
||||
|
||||
try:
|
||||
num = self._address_parser.number(args)
|
||||
self._output("+%u" % num)
|
||||
self._output("$" + self.byteFmt % num)
|
||||
self._output("%04o" % num)
|
||||
self._output(itoa(num, 2).zfill(8))
|
||||
except KeyError:
|
||||
self._output("Bad label: %s" % args)
|
||||
except OverflowError:
|
||||
self._output("Overflow error: %s" % args)
|
||||
|
||||
def help_registers(self):
|
||||
self._output("registers[<name>=<value> [, <name>=<value>]*]")
|
||||
self._output("Assign respective registers. With no parameters,")
|
||||
self._output("display register values.")
|
||||
|
||||
def do_registers(self, args):
|
||||
if args == '':
|
||||
return
|
||||
|
||||
pairs = re.findall('([^=,\s]*)=([^=,\s]*)', args)
|
||||
if pairs == []:
|
||||
return self._output("Syntax error: %s" % args)
|
||||
|
||||
for register, value in pairs:
|
||||
if register not in ('pc', 'sp', 'a', 'x', 'y', 'p'):
|
||||
self._output("Invalid register: %s" % register)
|
||||
else:
|
||||
try:
|
||||
intval = self._address_parser.number(value)
|
||||
except KeyError as exc: # label not found
|
||||
self._output(exc.args[0])
|
||||
continue
|
||||
except OverflowError as exc: # wider than address space
|
||||
msg = "Overflow: %r too wide for register %r"
|
||||
self._output(msg % (value, register))
|
||||
continue
|
||||
|
||||
if register != 'pc':
|
||||
if intval != (intval & self.byteMask):
|
||||
msg = "Overflow: %r too wide for register %r"
|
||||
self._output(msg % (value, register))
|
||||
continue
|
||||
|
||||
setattr(self._mpu, register, intval)
|
||||
|
||||
def help_cd(self):
|
||||
self._output("cd <directory>")
|
||||
self._output("Change the working directory.")
|
||||
|
||||
def do_cd(self, args):
|
||||
if args == '':
|
||||
return self.help_cd()
|
||||
|
||||
try:
|
||||
os.chdir(args)
|
||||
except OSError as exc:
|
||||
msg = "Cannot change directory: [%d] %s" % (exc.errno,
|
||||
exc.strerror)
|
||||
self._output(msg)
|
||||
self.do_pwd()
|
||||
|
||||
def help_pwd(self):
|
||||
self._output("Show the current working directory.")
|
||||
|
||||
def do_pwd(self, args=None):
|
||||
cwd = os.getcwd()
|
||||
self._output(cwd)
|
||||
|
||||
def help_load(self):
|
||||
self._output("load <filename|url> <address|top>")
|
||||
self._output("Load a file into memory at the specified address.")
|
||||
self._output('An address of "top" loads into the top of memory.')
|
||||
self._output("Commodore-style load address bytes are ignored.")
|
||||
|
||||
def do_load(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) not in (1, 2):
|
||||
self._output("Syntax error: %s" % args)
|
||||
return
|
||||
|
||||
filename = split[0]
|
||||
|
||||
if "://" in filename:
|
||||
try:
|
||||
f = urlopen(filename)
|
||||
bytes = f.read()
|
||||
f.close()
|
||||
except Exception as exc:
|
||||
msg = "Cannot fetch remote file: %s" % str(exc)
|
||||
self._output(msg)
|
||||
return
|
||||
else:
|
||||
try:
|
||||
f = open(filename, 'rb')
|
||||
bytes = f.read()
|
||||
f.close()
|
||||
except (OSError, IOError) as exc:
|
||||
msg = "Cannot load file: [%d] %s" % (exc.errno, exc.strerror)
|
||||
self._output(msg)
|
||||
return
|
||||
|
||||
if len(split) == 2:
|
||||
if split[1] == "top":
|
||||
# load a ROM to top of memory
|
||||
top_address = self.addrMask
|
||||
program_size = len(bytes) // (self.byteWidth // 8)
|
||||
start = top_address - program_size + 1
|
||||
else:
|
||||
start = self._address_parser.number(split[1])
|
||||
else:
|
||||
start = self._mpu.pc
|
||||
|
||||
if self.byteWidth == 8:
|
||||
if isinstance(bytes, str):
|
||||
bytes = map(ord, bytes)
|
||||
else: # Python 3
|
||||
bytes = [ b for b in bytes ]
|
||||
|
||||
elif self.byteWidth == 16:
|
||||
def format(msb, lsb):
|
||||
if isinstance(bytes, str):
|
||||
return (ord(msb) << 8) + ord(lsb)
|
||||
else: # Python 3
|
||||
return (msb << 8) + lsb
|
||||
bytes = list(map(format, bytes[0::2], bytes[1::2]))
|
||||
|
||||
self._fill(start, start, bytes)
|
||||
|
||||
def help_save(self):
|
||||
self._output("save \"filename\" <start> <end>")
|
||||
self._output("Save the specified memory range as a binary file.")
|
||||
self._output("Commodore-style load address bytes are not written.")
|
||||
|
||||
def do_save(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 3:
|
||||
self._output("Syntax error: %s" % args)
|
||||
return
|
||||
|
||||
filename = split[0]
|
||||
start = self._address_parser.number(split[1])
|
||||
end = self._address_parser.number(split[2])
|
||||
|
||||
mem = self._mpu.memory[start:end + 1]
|
||||
try:
|
||||
f = open(filename, 'wb')
|
||||
for m in mem:
|
||||
# output each octect from msb first
|
||||
for shift in range(self.byteWidth - 8, -1, -8):
|
||||
f.write(bytearray([(m >> shift) & 0xff]))
|
||||
f.close()
|
||||
except (OSError, IOError) as exc:
|
||||
msg = "Cannot save file: [%d] %s" % (exc.errno, exc.strerror)
|
||||
self._output(msg)
|
||||
return
|
||||
|
||||
self._output("Saved +%d bytes to %s" % (len(mem), filename))
|
||||
|
||||
def help_fill(self):
|
||||
self._output("fill <address_range> <data_list>")
|
||||
self._output("Fill memory in the address range with the data in")
|
||||
self._output("<data_list>. If the size of the address range is")
|
||||
self._output("greater than the size of the data_list, the data_list ")
|
||||
self._output("is repeated.")
|
||||
|
||||
def do_fill(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) < 2:
|
||||
return self.help_fill()
|
||||
|
||||
try:
|
||||
start, end = self._address_parser.range(split[0])
|
||||
filler = list(map(self._address_parser.number, split[1:]))
|
||||
except KeyError as exc:
|
||||
self._output(exc.args[0]) # "Label not found: foo"
|
||||
else:
|
||||
self._fill(start, end, filler)
|
||||
|
||||
def _fill(self, start, end, filler):
|
||||
address = start
|
||||
length, index = len(filler), 0
|
||||
|
||||
if start == end:
|
||||
end = start + length - 1
|
||||
if (end > self.addrMask):
|
||||
end = self.addrMask
|
||||
|
||||
while address <= end:
|
||||
address &= self.addrMask
|
||||
self._mpu.memory[address] = (filler[index] & self.byteMask)
|
||||
index += 1
|
||||
if index == length:
|
||||
index = 0
|
||||
address += 1
|
||||
|
||||
fmt = (end - start + 1, start, end)
|
||||
starttoend = "$" + self.addrFmt + " to $" + self.addrFmt
|
||||
self._output(("Wrote +%d bytes from " + starttoend) % fmt)
|
||||
|
||||
def help_mem(self):
|
||||
self._output("mem <address_range>")
|
||||
self._output("Display the contents of memory.")
|
||||
self._output('Range is specified like "<start:end>".')
|
||||
|
||||
def do_mem(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 1:
|
||||
return self.help_mem()
|
||||
|
||||
start, end = self._address_parser.range(split[0])
|
||||
|
||||
line = self.addrFmt % start + ":"
|
||||
for address in range(start, end + 1):
|
||||
byte = self._mpu.memory[address]
|
||||
more = " " + self.byteFmt % byte
|
||||
|
||||
exceeded = len(line) + len(more) > self._width
|
||||
if exceeded:
|
||||
self._output(line)
|
||||
line = self.addrFmt % address + ":"
|
||||
line += more
|
||||
self._output(line)
|
||||
|
||||
def help_add_label(self):
|
||||
self._output("add_label <address> <label>")
|
||||
self._output("Map a given address to a label.")
|
||||
|
||||
def do_add_label(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 2:
|
||||
self._output("Syntax error: %s" % args)
|
||||
return self.help_add_label()
|
||||
|
||||
try:
|
||||
address = self._address_parser.number(split[0])
|
||||
except KeyError as exc:
|
||||
self._output(exc.args[0]) # "Label not found: foo"
|
||||
except OverflowError:
|
||||
self._output("Overflow error: %s" % args)
|
||||
else:
|
||||
label = split[1]
|
||||
self._address_parser.labels[label] = address
|
||||
|
||||
def help_show_labels(self):
|
||||
self._output("show_labels")
|
||||
self._output("Display current label mappings.")
|
||||
|
||||
def do_show_labels(self, args):
|
||||
values = list(self._address_parser.labels.values())
|
||||
keys = list(self._address_parser.labels.keys())
|
||||
|
||||
byaddress = list(zip(values, keys))
|
||||
byaddress.sort()
|
||||
for address, label in byaddress:
|
||||
self._output(self.addrFmt % address + ": " + label)
|
||||
|
||||
def help_delete_label(self):
|
||||
self._output("delete_label <label>")
|
||||
self._output("Remove the specified label from the label tables.")
|
||||
|
||||
def do_delete_label(self, args):
|
||||
if args == '':
|
||||
return self.help_delete_label()
|
||||
|
||||
if args in self._address_parser.labels:
|
||||
del self._address_parser.labels[args]
|
||||
|
||||
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.")
|
||||
|
||||
def do_add_breakpoint(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 1:
|
||||
self._output("Syntax error: %s" % args)
|
||||
return self.help_add_breakpoint()
|
||||
|
||||
address = self._address_parser.number(split[0])
|
||||
|
||||
if address in self._breakpoints:
|
||||
self._output("Breakpoint already present at $%04X" % address)
|
||||
else:
|
||||
self._breakpoints.append(address)
|
||||
msg = "Breakpoint %d added at $%04X"
|
||||
self._output(msg % (len(self._breakpoints) - 1, address))
|
||||
|
||||
def help_add_breakpoint(self):
|
||||
self._output("add_breakpoint <address|label>")
|
||||
self._output("Add a breakpoint on execution at the given address or label")
|
||||
|
||||
def do_delete_breakpoint(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 1:
|
||||
self._output("Syntax error: %s" % args)
|
||||
return self.help_delete_breakpoint()
|
||||
|
||||
number = None
|
||||
try:
|
||||
number = int(split[0])
|
||||
if number < 0 or number > len(self._breakpoints):
|
||||
self._output("Invalid breakpoint number %d", number)
|
||||
return
|
||||
except ValueError:
|
||||
self._output("Illegal number: %s" % args)
|
||||
return
|
||||
|
||||
if self._breakpoints[number] is not None:
|
||||
self._breakpoints[number] = None
|
||||
self._output("Breakpoint %d removed" % number)
|
||||
else:
|
||||
self._output("Breakpoint %d already removed" % number)
|
||||
|
||||
def help_delete_breakpoint(self):
|
||||
self._output("delete_breakpoint <number>")
|
||||
self._output("Delete the breakpoint on execution marked by the given number")
|
||||
|
||||
def do_show_breakpoints(self, args):
|
||||
for i, address in enumerate(self._breakpoints):
|
||||
if address is not None:
|
||||
bpinfo = "Breakpoint %d: $%04X" % (i, address)
|
||||
label = self._address_parser.label_for(address)
|
||||
if label is not None:
|
||||
bpinfo += " " + label
|
||||
self._output(bpinfo)
|
||||
|
||||
def help_show_breakpoints(self):
|
||||
self._output("show_breakpoints")
|
||||
self._output("Lists the currently assigned breakpoints")
|
||||
|
||||
def main(args=None):
|
||||
c = Monitor()
|
||||
|
||||
try:
|
||||
import readline
|
||||
readline = readline # pyflakes
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
c.onecmd('version')
|
||||
c.cmdloop()
|
||||
except KeyboardInterrupt:
|
||||
c._output('')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user