diff --git a/py65/monitor.py b/py65/monitor.py index 7f46857..b63db62 100644 --- a/py65/monitor.py +++ b/py65/monitor.py @@ -42,21 +42,39 @@ 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, memory=None, putc_addr=0xF001, getc_addr=0xF004): + 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 - 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) + 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: @@ -68,10 +86,7 @@ class Monitor(cmd.Cmd): self._usage() self._exit(1) - load = None - rom = None - goto = None - mpu = None + load, rom, goto = None, None, None for opt, value in options: if opt in ('-i', '--input'): @@ -80,6 +95,19 @@ class Monitor(cmd.Cmd): 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 @@ -89,42 +117,7 @@ class Monitor(cmd.Cmd): 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) - self.mpu_type = self._get_mpu(mpu) - - 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) + return load, rom, goto def _usage(self): usage = __doc__ % sys.argv[0] @@ -148,7 +141,7 @@ class Monitor(cmd.Cmd): return result - def _reset(self, mpu_type,getc_addr=0xF004,putc_addr=0xF001): + 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 @@ -157,7 +150,7 @@ class Monitor(cmd.Cmd): 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._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) @@ -230,7 +223,7 @@ class Monitor(cmd.Cmd): break return mpu - def _install_mpu_observers(self,getc_addr,putc_addr): + def _install_mpu_observers(self, getc_addr, putc_addr): def putc(address, value): try: self.stdout.write(chr(value)) diff --git a/py65/tests/test_monitor.py b/py65/tests/test_monitor.py index 3477668..c54579f 100644 --- a/py65/tests/test_monitor.py +++ b/py65/tests/test_monitor.py @@ -1156,6 +1156,95 @@ class MonitorTests(unittest.TestCase): out = stdout.getvalue() self.assertTrue(out.startswith('0008: 00 00 ff 00 00'), "monitor must see pre-initialized memory") + # command line options + + def test_argv_mpu(self): + argv = ['py65mon', '--mpu', '65c02'] + stdout = StringIO() + mon = Monitor(argv=argv, stdout=stdout) + self.assertEqual('65C02', mon._mpu.name) + + def test_argv_mpu_invalid(self): + argv = ['py65mon', '--mpu', 'bad'] + stdout = StringIO() + try: + Monitor(argv=argv, stdout=stdout) + except SystemExit as exc: + self.assertEqual(1, exc.code) + self.assertTrue("Fatal: no such MPU." in stdout.getvalue()) + + def test_argv_goto(self): + argv = ['py65mon', '--goto', 'c000'] + stdout = StringIO() + memory = bytearray(0x10000) + memory[0xc000] = 0xea # c000 nop + memory[0xc001] = 0xea # c001 nop + memory[0xc002] = 0x00 # c002 brk + mon = Monitor(argv=argv, stdout=stdout, memory=memory) + self.assertEqual(0xc002, mon._mpu.pc) + + def test_argv_load(self): + with tempfile.NamedTemporaryFile('wb+') as f: + data = bytearray([0xab, 0xcd]) + f.write(data) + f.flush() + + argv = ['py65mon', '--load', f.name] + stdout = StringIO() + mon = Monitor(argv=argv, stdout=stdout) + self.assertEqual(list(data), mon._mpu.memory[:len(data)]) + + def test_argv_rom(self): + with tempfile.NamedTemporaryFile('wb+') as f: + rom = bytearray(4096) + rom[0] = 0xea # f000 nop + rom[1] = 0xea # f001 nop + rom[2] = 0x00 # f002 brk + rom[-2] = 0xf000 & 0xff # fffc reset vector low + rom[-3] = 0xf000 >> 8 # fffd reset vector high + f.write(rom) + f.flush() + + argv = ['py65mon', '--rom', f.name] + stdout = StringIO() + mon = Monitor(argv=argv, stdout=stdout) + self.assertEqual(list(rom), mon._mpu.memory[-len(rom):]) + self.assertEqual(0xf002, mon._mpu.pc) + + def test_argv_input(self): + argv = ['py65mon', '--input', 'abcd'] + stdout = StringIO() + mon = Monitor(argv=argv, stdout=stdout) + read_subscribers = mon._mpu.memory._read_subscribers + self.assertEqual(1, len(read_subscribers)) + self.assertTrue('getc' in repr(read_subscribers[0xabcd])) + + def test_argv_output(self): + argv = ['py65mon', '--output', 'dcba'] + stdout = StringIO() + mon = Monitor(argv=argv, stdout=stdout) + write_subscribers = mon._mpu.memory._write_subscribers + self.assertEqual(1, len(write_subscribers)) + self.assertTrue('putc' in repr(write_subscribers[0xdcba])) + + def test_argv_combination_rom_mpu(self): + with tempfile.NamedTemporaryFile('wb+') as f: + rom = bytearray(4096) + rom[0] = 0xea # f000 nop + rom[1] = 0xea # f001 nop + rom[2] = 0x00 # f002 brk + rom[-2] = 0xf000 & 0xff # fffc reset vector low + rom[-3] = 0xf000 >> 8 # fffd reset vector high + f.write(rom) + f.flush() + + argv = ['py65mon', '--rom', f.name, '--mpu', '65c02',] + stdout = StringIO() + mon = Monitor(argv=argv, stdout=stdout) + self.assertEqual('65C02', mon._mpu.name) + self.assertEqual(list(rom), mon._mpu.memory[-len(rom):]) + self.assertEqual(0xf002, mon._mpu.pc) + def test_suite(): return unittest.findTestCases(sys.modules[__name__])