m68000-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ """A readable, pure-Python Motorola MC68000 instruction core."""
2
+
3
+ from m68000_python._core import AUTOVECTOR, SPURIOUS, BusError
4
+ from m68000_python.console import CommandDebugger, CommandError, CommandResult
5
+ from m68000_python.cpu import M68000CPU
6
+ from m68000_python.debug import (
7
+ Access,
8
+ BoundaryKind,
9
+ DebugSession,
10
+ DebugTarget,
11
+ RunResult,
12
+ StepRecord,
13
+ StopReason,
14
+ next_boundary,
15
+ )
16
+ from m68000_python.disasm import (
17
+ Instruction,
18
+ WordReader,
19
+ disassemble,
20
+ disassemble_bytes,
21
+ disassemble_range,
22
+ )
23
+ from m68000_python.state import CPUState
24
+ from m68000_python.trace import (
25
+ TRACE_SCHEMA_VERSION,
26
+ TraceDifference,
27
+ TraceDivergence,
28
+ compare_step_records,
29
+ first_session_divergence,
30
+ first_trace_divergence,
31
+ iter_session_steps,
32
+ iter_trace_divergences,
33
+ read_trace,
34
+ step_record_from_dict,
35
+ step_record_to_dict,
36
+ write_trace,
37
+ )
38
+
39
+ __all__ = [
40
+ "AUTOVECTOR",
41
+ "M68000CPU",
42
+ "SPURIOUS",
43
+ "TRACE_SCHEMA_VERSION",
44
+ "Access",
45
+ "BoundaryKind",
46
+ "BusError",
47
+ "CPUState",
48
+ "CommandDebugger",
49
+ "CommandError",
50
+ "CommandResult",
51
+ "DebugSession",
52
+ "DebugTarget",
53
+ "Instruction",
54
+ "RunResult",
55
+ "StepRecord",
56
+ "StopReason",
57
+ "TraceDifference",
58
+ "TraceDivergence",
59
+ "WordReader",
60
+ "compare_step_records",
61
+ "disassemble",
62
+ "disassemble_bytes",
63
+ "disassemble_range",
64
+ "first_session_divergence",
65
+ "first_trace_divergence",
66
+ "iter_session_steps",
67
+ "iter_trace_divergences",
68
+ "next_boundary",
69
+ "read_trace",
70
+ "step_record_from_dict",
71
+ "step_record_to_dict",
72
+ "write_trace",
73
+ ]
@@ -0,0 +1,112 @@
1
+ """Load a program and step through it: ``python -m m68000_python``.
2
+
3
+ python -m m68000_python --load program.bin@0x1000 --pc 0x1000
4
+ python -m m68000_python --zip ROMPATH/altbeast.zip:epr-11907.a7,epr-11906.a5@0 --reset
5
+ python -m m68000_python --load game.md@0 --reset -c "break 0x206" -c "run 100000"
6
+
7
+ The host is a flat 16 MB RAM holding every image loaded, so ROM is writable
8
+ and there are no devices: good for reading and stepping through code, not for
9
+ running a board (for that, write a host -- validation/lockstep.py has two).
10
+ ``--zip ZIP:EVEN,ODD@ADDRESS`` interleaves a pair of byte-wide ROMs, as 68000
11
+ boards wire them. Commands given with ``-c`` run first; then the prompt reads
12
+ stdin, unless ``--batch`` is given. ``help`` lists the commands. Numbers,
13
+ here and in the commands, are decimal unless written with ``$`` or ``0x``.
14
+ """
15
+
16
+ import argparse
17
+ import sys
18
+ import zipfile
19
+ from pathlib import Path
20
+
21
+ from m68000_python.console import CommandDebugger, CommandError, parse_number
22
+ from m68000_python.cpu import M68000CPU
23
+ from m68000_python.debug import DebugSession
24
+
25
+ SIZE = 1 << 24
26
+
27
+
28
+ def _image(spec: str, zipped: bool) -> tuple[bytes, int]:
29
+ source, _, where = spec.rpartition("@")
30
+ if not source:
31
+ raise SystemExit(f"{spec!r}: give the load address as FILE@ADDRESS")
32
+ try:
33
+ address = parse_number(where, "load address", maximum=SIZE - 1)
34
+ except CommandError as exc:
35
+ raise SystemExit(str(exc)) from None
36
+ if zipped:
37
+ archive, _, members = source.rpartition(":")
38
+ if not archive:
39
+ raise SystemExit(f"{spec!r}: give --zip as ZIPFILE:MEMBER[,ODD]@ADDRESS")
40
+ with zipfile.ZipFile(archive) as z:
41
+ parts = [z.read(member) for member in members.split(",")]
42
+ if len(parts) == 2:
43
+ data = bytearray(2 * len(parts[0]))
44
+ data[0::2], data[1::2] = parts
45
+ data = bytes(data)
46
+ else:
47
+ data = parts[0]
48
+ else:
49
+ data = Path(source).read_bytes()
50
+ if address + len(data) > SIZE:
51
+ raise SystemExit(f"{spec!r}: {len(data)} bytes do not fit at ${address:06X}")
52
+ return data, address
53
+
54
+
55
+ def main(argv: list[str] | None = None) -> None:
56
+ parser = argparse.ArgumentParser(
57
+ prog="python -m m68000_python",
58
+ description=__doc__,
59
+ formatter_class=argparse.RawDescriptionHelpFormatter,
60
+ )
61
+ parser.add_argument("--load", action="append", default=[], metavar="FILE@ADDRESS")
62
+ parser.add_argument("--zip", action="append", default=[], metavar="ZIP:MEMBER[,ODD]@ADDRESS")
63
+ start = parser.add_mutually_exclusive_group()
64
+ start.add_argument("--pc", help="start at this address ($ or 0x for hexadecimal)")
65
+ start.add_argument("--reset", action="store_true", help="start from the vectors at 0 and 4")
66
+ parser.add_argument(
67
+ "-c", "--command", action="append", default=[], help="run first (repeatable)"
68
+ )
69
+ parser.add_argument("--batch", action="store_true", help="exit after the -c commands")
70
+ args = parser.parse_args(argv)
71
+
72
+ memory = bytearray(SIZE)
73
+ for spec in args.load:
74
+ data, address = _image(spec, zipped=False)
75
+ memory[address : address + len(data)] = data
76
+ for spec in args.zip:
77
+ data, address = _image(spec, zipped=True)
78
+ memory[address : address + len(data)] = data
79
+
80
+ def read_word(address: int) -> int:
81
+ return (memory[address] << 8) | memory[(address + 1) & 0xFFFFFF]
82
+
83
+ def write_word(address: int, value: int) -> None:
84
+ memory[address] = value >> 8
85
+ memory[(address + 1) & 0xFFFFFF] = value & 0xFF
86
+
87
+ cpu = M68000CPU(memory.__getitem__, read_word, memory.__setitem__, write_word)
88
+ if args.reset:
89
+ cpu.reset()
90
+ elif args.pc is not None:
91
+ cpu.set_pc(parse_number(args.pc, "pc", maximum=SIZE - 1))
92
+ else:
93
+ cpu.set_pc(0)
94
+ session = DebugSession(cpu, peek_word=read_word, track_accesses=True)
95
+ debugger = CommandDebugger(session)
96
+ for command in ["registers", "disassemble", *args.command]:
97
+ print(f"m68000> {command}")
98
+ try:
99
+ result = debugger.execute(command)
100
+ except CommandError as exc:
101
+ print(f"error: {exc}")
102
+ continue
103
+ for line in result.lines:
104
+ print(line)
105
+ if result.quit:
106
+ return
107
+ if not args.batch:
108
+ debugger.interact(sys.stdin, sys.stdout)
109
+
110
+
111
+ if __name__ == "__main__":
112
+ main()