Example #1
0
 def test_number_bad_label(self):
     parser = AddressParser()
     try:
         parser.number('bad_label')
         self.fail()
     except KeyError, why:
         self.assertEqual('Label not found: bad_label', why[0])
Example #2
0
 def test_number_bad_label_with_offset(self):
     parser = AddressParser()
     try:
         parser.number('bad_label+3')
         self.fail()
     except KeyError as exc:
         self.assertEqual('Label not found: bad_label', exc.args[0])
Example #3
0
 def test_number_bad_label_with_offset(self):
     parser = AddressParser()
     try:
         parser.number("bad_label+3")
         self.fail()
     except KeyError, why:
         self.assertEqual("Label not found: bad_label", why[0])
Example #4
0
 def test_number_label_bin_offset(self):
   parser = AddressParser()
   parser.labels = {'foo': 0xC000}
   self.assertEqual(0xC003, parser.number('foo+%00000011'))
   self.assertEqual(0xBFFD, parser.number('foo-%00000011'))
   self.assertEqual(0xC003, parser.number('foo + %00000011'))
   self.assertEqual(0xBFFD, parser.number('foo - %00000011'))
Example #5
0
 def test_number_label_dec_offset(self):
   parser = AddressParser()
   parser.labels = {'foo': 0xC000}
   self.assertEquals(0xC003, parser.number('foo++3'))
   self.assertEquals(0xBFFD, parser.number('foo-+3'))
   self.assertEquals(0xC003, parser.number('foo + +3'))
   self.assertEquals(0xBFFD, parser.number('foo - +3'))
Example #6
0
 def test_number_label_dec_offset(self):
     parser = AddressParser()
     parser.labels = {"foo": 0xC000}
     self.assertEqual(0xC003, parser.number("foo++3"))
     self.assertEqual(0xBFFD, parser.number("foo-+3"))
     self.assertEqual(0xC003, parser.number("foo + +3"))
     self.assertEqual(0xBFFD, parser.number("foo - +3"))
Example #7
0
 def test_number_bad_label_with_offset(self):
   parser = AddressParser()
   try:
     parser.number('bad_label+3')
     self.fail()
   except KeyError as why:
     self.assertEqual("'Label not found: bad_label'", str(why))    
Example #8
0
 def test_number_bad_label_syntax(self):
     parser = AddressParser()
     parser.labels = {'foo': 0xFFFF}
     try:
         parser.number('#$foo')
         self.fail()
     except KeyError as exc:
         self.assertEqual('Label not found: #$foo', exc.args[0])
Example #9
0
 def _reset(self, mpu_type, getc_addr=0xF004, putc_addr=0xF001):
     self._mpu = mpu_type(memory=self.memory)
     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
     if getc_addr and putc_addr:
         self._install_mpu_observers(getc_addr, putc_addr)
     self._address_parser = AddressParser()
     self._disassembler = Disassembler(self._mpu, self._address_parser)
     self._assembler = Assembler(self._mpu, self._address_parser)
Example #10
0
 def disassemble(self, bytes, pc=0, mpu=None):
     if mpu is None:
         mpu = MPU()
     address_parser = AddressParser()
     disasm = Disassembler(mpu, address_parser)
     mpu.memory[pc:len(bytes) - 81] = bytes
     return disasm.instruction_at(pc)
Example #11
0
 def test_number_label_offset_default_radix(self):
     parser = AddressParser()
     parser.labels = {'foo': 0xC000}
     parser.radix = 16
     self.assertEqual(0xC010, parser.number('foo+10'))
     self.assertEqual(0xBFF0, parser.number('foo-10'))
     self.assertEqual(0xC010, parser.number('foo + 10'))
     self.assertEqual(0xBFF0, parser.number('foo - 10'))
     parser.radix = 10
     self.assertEqual(0xC00A, parser.number('foo+10'))
     self.assertEqual(0xBFF6, parser.number('foo-10'))
     self.assertEqual(0xC00A, parser.number('foo + 10'))
     self.assertEqual(0xBFF6, parser.number('foo - 10'))
Example #12
0
    def __init__(self, mpu, address_parser=None):
        """ If a configured AddressParser is passed, symbolic addresses
        may be used in the assembly statements.
        """
        if address_parser is None:
            address_parser = AddressParser()

        self._mpu = mpu
        self._address_parser = address_parser
Example #13
0
 def test_number_label_offset_default_radix(self):
   parser = AddressParser()
   parser.labels = {'foo': 0xC000}
   parser.radix = 16
   self.assertEqual(0xC010, parser.number('foo+10'))
   self.assertEqual(0xBFF0, parser.number('foo-10'))
   self.assertEqual(0xC010, parser.number('foo + 10'))
   self.assertEqual(0xBFF0, parser.number('foo - 10'))    
   parser.radix = 10
   self.assertEqual(0xC00A, parser.number('foo+10'))
   self.assertEqual(0xBFF6, parser.number('foo-10'))
   self.assertEqual(0xC00A, parser.number('foo + 10'))
   self.assertEqual(0xBFF6, parser.number('foo - 10'))    
Example #14
0
 def _reset(self, mpu_type,getc_addr=0xF004,putc_addr=0xF001):
     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)
     self._address_parser = AddressParser()
     self._disassembler = Disassembler(self._mpu, self._address_parser)
     self._assembler = Assembler(self._mpu, self._address_parser)
    def __init__(self, mpu, address_parser=None):
        if address_parser is None:
            address_parser = AddressParser()

        self._mpu = mpu
        self._address_parser = address_parser

        self.addrWidth = mpu.ADDR_WIDTH
        self.byteWidth = mpu.BYTE_WIDTH
        self.addrFmt = mpu.ADDR_FORMAT
        self.byteFmt = mpu.BYTE_FORMAT
        self.addrMask = mpu.addrMask
        self.byteMask = mpu.byteMask
    def __init__(self, mpu, address_parser=None):
        """ If a configured AddressParser is passed, symbolic addresses
        may be used in the assembly statements.
        """
        self._mpu = mpu

        if address_parser is None:
            address_parser = AddressParser()
        self._address_parser = address_parser

        self._addressing = []
        numchars = mpu.BYTE_WIDTH / 4  # 1 byte = 2 chars in hex
        for mode, format in self.Addressing:
            pat = "^" + re.escape(format) + "$"
            pat = pat.replace('00', '0{%d}' % numchars)
            pat = pat.replace('FF', '([0-9A-F]{%d})' % numchars)
            self._addressing.append([mode, re.compile(pat)])
Example #17
0
    def __init__(self, mpu, address_parser=None):
        """ If a configured AddressParser is passed, symbolic addresses
        may be used in the assembly statements.
        """
        if address_parser is None:
            address_parser = AddressParser()

        self._mpu = mpu
        self._address_parser = address_parser

        self.addrWidth = mpu.ADDR_WIDTH
        self.byteWidth = mpu.BYTE_WIDTH
        self.addrFmt = mpu.ADDR_FORMAT
        self.byteFmt = mpu.BYTE_FORMAT
        self.addrMask = mpu.addrMask
        self.byteMask = mpu.byteMask

        if self.byteWidth == 8:
            self.Addressing = self.Addressing8
        else:
            self.Addressing = self.Addressing16
Example #18
0
 def test_number_bin_literal(self):
   parser = AddressParser()
   self.assertEqual(129, parser.number('%10000001'))
Example #19
0
 def test_number_hex_literal(self):
   parser = AddressParser()
   self.assertEqual(49152, parser.number('$c000'))
Example #20
0
 def test_label_for_returns_alternate_default(self):
   parser = AddressParser(labels={})
   self.assertEqual('foo', parser.label_for(0xFFD2, 'foo'))
Example #21
0
 def test_label_for_returns_label(self):
   parser = AddressParser(labels={'chrout':0xFFD2})
   self.assertEqual('chrout', parser.label_for(0xFFD2))
Example #22
0
class Monitor(cmd.Cmd):

    Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02,
                       '65Org16': V65Org16}

    def __init__(self, argv=None, stdin=None, stdout=None,
                       mpu_type=NMOS6502, memory=None,
                       putc_addr=0xF001, getc_addr=0xF004):
        self.mpu_type = mpu_type
        self.memory = memory
        self.putc_addr = putc_addr
        self.getc_addr = getc_addr
        self._breakpoints = []
        self._width = 78
        self.prompt = "."
        self._add_shortcuts()
        cmd.Cmd.__init__(self, stdin=stdin, stdout=stdout)

        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(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)

    def _parse_args(self, argv):
        try:
            shortopts = 'hi:o:m:l:r:g:'
            longopts = ['help', 'mpu=', '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, rom, goto = None, None, 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 ('-m', '--mpu'):
                mpu_type = self._get_mpu(value)
                if mpu_type is None:
                    mpus = sorted(self.Microprocessors.keys())
                    msg = "Fatal: no such MPU. Available MPUs: %s"
                    self._output(msg % ', '.join(mpus))
                    sys.exit(1)
                self.mpu_type = mpu_type

            if opt in ("-h", "--help"):
                self._usage()
                self._exit(0)

            if opt in ('-l', '--load'):
                load = value

            if opt in ('-r', '--rom'):
                rom = value

            if opt in ('-g', '--goto'):
                goto = value

        return load, rom, goto

    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):
        self._mpu = mpu_type(memory=self.memory)
        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
        if getc_addr and putc_addr:
            self._install_mpu_observers(getc_addr, putc_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):
        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(subject=self.memory, addrWidth=self.addrWidth)
        m.subscribe_to_write([self.putc_addr], putc)
        m.subscribe_to_read([self.getc_addr], getc)

        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._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")
Example #23
0
 def test_number_label(self):
     parser = AddressParser()
     parser.labels = {'foo': 0xC000}
     self.assertEqual(0xC000, parser.number('foo'))
Example #24
0
class Monitor(cmd.Cmd):

    def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None, stdout=None):
        self._reset(mpu_type)
        self._width = 78
        self._update_prompt()
        self._add_shortcuts()
        cmd.Cmd.__init__(self, completekey, stdin, stdout)

    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 as e:
            (file, fun, line), t, v, tbinfo = compact_traceback()
            error = 'Error: %s, %s: file: %s line: %s' % (t, v, file, line)
            self._output(error)

        self._update_prompt()
        return result

    def _reset(self, mpu_type):
        self._mpu = mpu_type()
        self._install_mpu_observers()
        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 = {'~':   'tilde',
                           '?':   'help',
                           'a':   'assemble',
                           'al':  'add_label',
                           'd':   'disassemble',
                           'dl':  'delete_label',
                           'f':   'fill',
                           '>':   'fill',
                           'g':   'goto',
                           'l':   'load',
                           'm':   'mem',
                           'r':   'registers',
                           'ret': 'return',
                           'rad': 'radix',
                           's':   'save',
                           '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 _install_mpu_observers(self):
        def putc(address, value):
            self.stdout.write(chr(value))
            self.stdout.flush()

        def getc(address):
            char = console.getch_noblock(self.stdin)
            if char:
                byte = ord(char)
            else:
                byte = 0
            return byte

        m = ObservableMemory()
        #m.subscribe_to_write([0xF001], putc)
        #m.subscribe_to_read([0xF004], getc)
        
        self._mpu.memory = m

    def _update_prompt(self):
        self.prompt = "\n%s\n." % repr(self._mpu)
        
    def _output(self, stuff):
        if stuff is not None:
            self.stdout.write(stuff + "\n")

    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):                                         
        mpus = {'6502': NMOS6502, '65C02': CMOS65C02}
        
        def available_mpus():
            mpu_list = ', '.join(list(mpus.keys()))
            self._output("Available MPUs: %s" % mpu_list)            
        
        if args == '':                      
            self._output("Current MPU is %s" % self._mpu.name)
            available_mpus()
        else:
            requested = args.upper()
            new_mpu = mpus.get(requested, None)
            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.")
        
    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):
        return self.do_EOF(args)
    
    def help_quit(self):
        return self.help_EOF()

    def do_assemble(self, args):
        split = args.split(None, 1)
        if len(split) != 2:
            return self._interactive_assemble(args)
        
        start, statement = split
        try:
            start = self._address_parser.number(start)
        except KeyError:
            self._output("Bad label: %s" % start)
            return

        bytes = self._assembler.assemble(statement, start)
        if bytes is None:
            self._output("Assemble failed: %s" % statement)
        else:
            end = start + len(bytes)
            self._mpu.memory[start:end] = bytes
            self.do_disassemble('%04x:%04x' % (start, end))

    def help_assemble(self):
        self._output("assemble <address> <statement>")
        self._output("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:
              self._output("Bad label: %s" % start)
              return
          
        assembling = True

        while assembling:
          prompt = "\r$%04x            " % (start)
          line = console.line_input(prompt, 
                    stdin=self.stdin, stdout=self.stdout)

          if not line:
            self.stdout.write("\n")
            return

          # assemble into memory
          bytes = self._assembler.assemble(line)
          if bytes is None:
              self.stdout.write("\r$%04x  ???\n" % start)
              continue
          end = start + len(bytes)
          self._mpu.memory[start:end] = bytes  

          # print disassembly
          bytes, disasm = self._disassembler.instruction_at(start)
          disassembly = self._format_disassembly(start, bytes, disasm)
          self.stdout.write("\r" + (' ' * (len(prompt+line) + 5) ) + "\r")
          self.stdout.write(disassembly + "\n")

          start += bytes

    def do_disassemble(self, args):
        start, end = self._address_parser.range(args)
        if start == end:
            end += 1
        
        address = start
        while address < end:
            bytes, disasm = self._disassembler.instruction_at(address)
            self._output(self._format_disassembly(address, bytes, disasm))                
            address += bytes
    
    def _format_disassembly(self, address, bytes, disasm):
        mem = ''
        for byte in self._mpu.memory[address:address+bytes]:
            mem += '%02x ' % byte
        
        return "$%04x  %-10s%s" % (address, mem, disasm)

    def help_disassemble(self):
        self._output("disassemble <address_range>")
        self._output("Disassemble instructions in the address range.")

    def help_step(self):
        self._output("step")
        self._output("Single-step through instructions.")

    def do_step(self, args):
        self._mpu.step()
        self.do_disassemble('%04x' % 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):
        self._mpu.pc = self._address_parser.number(args)
        brks = [0x00] # BRK
        self._run(stopcodes=brks)
    
    def _run(self, stopcodes=[]):
        last_instruct = None
        while last_instruct not in stopcodes:
            self._mpu.step()
            last_instruct = self._mpu.memory[self._mpu.pc]
    
    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.")
    
    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 the specified number in decimal, hex, octal, and binary.")
    
    def do_tilde(self, args):   
        try:
            num = self._address_parser.number(args)
        except ValueError:
            self._output("Syntax error: %s" % args)
            return

        self._output("+%u" % num)
        self._output("$%02x" % num)
        self._output("%04o" % num)
        self._output(itoa(num, 2).zfill(8))
    
    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.")
    
    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) & 0xFFFF
                    if len(register) == 1:
                        intval &= 0xFF
                    setattr(self._mpu, register, intval)
                except KeyError as why:
                    self._output(str(why))
    
    def help_cd(self, args):
        self._output("cd <directory>")
        self._output("Change the working directory.")
    
    def do_cd(self, args):
        try:
            os.chdir(args)
        except OSError as why:
            msg = "Cannot change directory: [%d] %s" % (why.errno, why.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\" <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:
            start = self._address_parser.number(split[1])
        else:
            start = self._mpu.pc

        try:
            f = open(filename, 'rb')
            bytes = f.read()
            f.close()
        except (OSError, IOError) as why:
            msg = "Cannot load file: [%d] %s" % (why[0], why[1])
            self._output(msg)
            return


#        self._fill(start, start, list(map(ord, bytes)))
        self._fill(start, start, list(bytes))

    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])
        
        bytes = bytearray(self._mpu.memory[start:end+1])
        try:
            f = open(filename, 'wb')
            f.write(bytes)
            f.close()
        except (OSError, IOError) as why:
            msg = "Cannot save file: [%d] %s" % (why.errno, why.strerror)
            self._output(msg)
            return
        
        self._output("Saved +%d bytes to %s" % (len(bytes), filename))    

    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.")
    
    def help_fill(self):
        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.")
    
    def do_fill(self, args):
        split = shlex.split(args)
        if len(split) < 2:
            self._output("Syntax error: %s" % args)
            return

        start, end = self._address_parser.range(split[0])
        filler = list(map(self._address_parser.number, split[1:]))
        
        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 > 0xFFFF):
                end = 0xFFFF

        while address <= end:
            address &= 0xFFFF
            self._mpu.memory[address] = (filler[index] & 0xFF)
            index += 1
            if index == length:
                index = 0
            address += 1

        fmt = (end - start + 1, start, end)
        self._output("Wrote +%d bytes from $%04x to $%04x" % fmt)
    
    def help_mem(self):
        self._output("mem <address_range>")
        self._output("Display the contents of memory.")
    
    def do_mem(self, args):
        start, end = self._address_parser.range(args)

        line = "%04x:" % start
        for address in range(start, end+1):
            byte = self._mpu.memory[address]
            more = "  %02x" % byte                       
            
            exceeded = len(line) + len(more) > self._width
            if exceeded:
                self._output(line)
                line = "%04x:" % 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
        
        address = self._address_parser.number(split[0])    
        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("%04x: %s" % (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()
        
        try:
            del self._address_parser.labels[args]
        except KeyError:
            pass

    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.")
Example #25
0
 def test_label_for_returns_none_by_default(self):
     parser = AddressParser(labels={})
     self.assertEqual(None, parser.label_for(0xFFD2))
Example #26
0
 def test_label_for_returns_label(self):
     parser = AddressParser(labels={'chrout': 0xFFD2})
     self.assertEqual('chrout', parser.label_for(0xFFD2))
Example #27
0
 def test_number_constrains_address_at_maxwidth_24(self):
     parser = AddressParser()
     parser.maxwidth = 24
     parser.labels = {'foo': 0xFFFFFF}
     self.assertRaises(OverflowError, parser.number, 'foo+5')
Example #28
0
 def test_number_constrains_address_at_zero_or_above(self):
     parser = AddressParser()
     self.assertRaises(OverflowError, parser.number, '-1')
Example #29
0
 def test_maxwidth_defaults_to_16_bits(self):
     parser = AddressParser()
     self.assertEqual(16, parser.maxwidth)
     self.assertEqual(0xFFFF, parser._maxaddr)
Example #30
0
 def test_ctor_uses_provided_mpu_and_address_parser(self):
     mpu = MPU()
     address_parser = AddressParser()
     asm = Assembler(mpu, address_parser)
     self.assertTrue(asm._mpu is mpu)
     self.assertTrue(asm._address_parser is address_parser)
Example #31
0
 def _reset(self, mpu_type):
     self._mpu = mpu_type()
     self._install_mpu_observers()
     self._address_parser = AddressParser()
     self._disassembler = Disassembler(self._mpu, self._address_parser)
     self._assembler = Assembler(self._mpu, self._address_parser)
Example #32
0
 def test_number_label(self):
   parser = AddressParser()
   parser.labels = {'foo': 0xC000}
   self.assertEqual(0xC000, parser.number('foo'))
Example #33
0
 def test_adderss_for_returns_alternate_default(self):
     parser = AddressParser(labels={})
     self.assertEqual('foo', parser.address_for('chrout', 'foo'))
Example #34
0
 def test_label_for_returns_alternate_default(self):
     parser = AddressParser(labels={})
     self.assertEqual('foo', parser.label_for(0xFFD2, 'foo'))
Example #35
0
 def test_number_default_radix(self):
     parser = AddressParser()
     parser.radix = 10
     self.assertEqual(10, parser.number('10'))
     parser.radix = 16
     self.assertEqual(16, parser.number('10'))
Example #36
0
 def test_number_truncates_address_at_maxwidth_16(self):
   parser = AddressParser()
   parser.labels = {'foo': 0xFFFF}
   self.assertEqual(0xFFFF, parser.number('foo+5'))
Example #37
0
 def test_maxwidth_can_be_set_in_constructor(self):
     parser = AddressParser(maxwidth=24)
     self.assertEqual(24, parser.maxwidth)
     self.assertEqual(0xFFFFFF, parser._maxaddr)
Example #38
0
 def assemble(self, statement, pc=0000):
     mpu = MPU()
     address_parser = AddressParser()
     assembler = Assembler(mpu, address_parser)
     return assembler.assemble(statement, pc)
Example #39
0
 def test_number_truncates_address_at_maxwidth_24(self):
     parser = AddressParser()
     parser.maxwidth = 24
     parser.labels = {'foo': 0xFFFFFF}
     self.assertEqual(0xFFFFFF, parser.number('foo+5'))
Example #40
0
 def test_range_two_numbers(self):
     parser = AddressParser(labels={})
     self.assertEqual((0xFFD2, 0xFFD4), parser.range('ffd2:ffd4'))
Example #41
0
 def test_label_for_returns_none_by_default(self):
   parser = AddressParser(labels={})
   self.assertEqual(None, parser.label_for(0xFFD2))
Example #42
0
 def test_range_mixed(self):
     parser = AddressParser(labels={'chrout':0xFFD2})
     self.assertEqual((0xFFD2, 0xFFD4), parser.range('chrout:ffd4'))
Example #43
0
 def test_maxwidth_setter(self):
   parser = AddressParser()
   parser.maxwidth = 24
   self.assertEqual(24, parser.maxwidth)
   self.assertEqual(0xFFFFFF, parser._maxaddr)       
Example #44
0
 def test_range_start_exceeds_end(self):
     parser = AddressParser(labels={})
     self.assertEqual((0xFFD2, 0xFFD4), parser.range('ffd4:ffd2'))
Example #45
0
 def test_number_dec_literal(self):
   parser = AddressParser()
   self.assertEqual(49152, parser.number('+49152'))
Example #46
0
 def test_maxwidth_setter(self):
     parser = AddressParser()
     parser.maxwidth = 24
     self.assertEqual(24, parser.maxwidth)
     self.assertEqual(0xFFFFFF, parser._maxaddr)
Example #47
0
 def test_number_default_radix(self):
   parser = AddressParser()
   parser.radix = 10
   self.assertEqual(10, parser.number('10'))
   parser.radix = 16
   self.assertEqual(16, parser.number('10'))
Example #48
0
 def test_number_hex_literal(self):
     parser = AddressParser()
     self.assertEqual(49152, parser.number('$c000'))
Example #49
0
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
        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)

    def _parse_args(self, argv):
        try:
            shortopts = 'hi:o:m:l:r:g:'
            longopts = ['help', 'mpu=', '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 ('-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):
        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)
        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):
        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)

        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._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")
Example #50
0
 def test_number_dec_literal(self):
     parser = AddressParser()
     self.assertEqual(49152, parser.number('+49152'))
Example #51
0
 def test_range_one_number(self):
     parser = AddressParser(labels={})
     self.assertEqual((0xFFD2, 0xFFD2), parser.range('ffd2'))
Example #52
0
 def test_number_bin_literal(self):
     parser = AddressParser()
     self.assertEqual(129, parser.number('%10000001'))
Example #53
0
 def assemble(self, statement, pc=0000, mpu=None):
     if mpu is None:
         mpu = MPU()
     address_parser = AddressParser()
     assembler = Assembler(mpu, address_parser)
     return assembler.assemble(statement, pc)
Example #54
0
 def test_range_one_label(self):
     parser = AddressParser(labels={'chrout':0xFFD2})
     self.assertEqual((0xFFD2, 0xFFD2), parser.range('chrout'))