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.
- m68000_python/__init__.py +73 -0
- m68000_python/__main__.py +112 -0
- m68000_python/_alu.py +656 -0
- m68000_python/_bcd.py +137 -0
- m68000_python/_bits.py +100 -0
- m68000_python/_control.py +208 -0
- m68000_python/_core.py +545 -0
- m68000_python/_dispatch.py +243 -0
- m68000_python/_ea.py +177 -0
- m68000_python/_flags.py +152 -0
- m68000_python/_loads.py +405 -0
- m68000_python/_shifts.py +168 -0
- m68000_python/_system.py +314 -0
- m68000_python/conformance.py +796 -0
- m68000_python/console.py +254 -0
- m68000_python/cpu.py +373 -0
- m68000_python/debug.py +361 -0
- m68000_python/disasm.py +354 -0
- m68000_python/py.typed +0 -0
- m68000_python/state.py +76 -0
- m68000_python/trace.py +296 -0
- m68000_python-0.1.0.dist-info/METADATA +379 -0
- m68000_python-0.1.0.dist-info/RECORD +25 -0
- m68000_python-0.1.0.dist-info/WHEEL +4 -0
- m68000_python-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -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()
|