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,243 @@
|
|
|
1
|
+
"""The opcode map: which handler each of the 65,536 first words runs.
|
|
2
|
+
|
|
3
|
+
``RULES`` below is the 68000 instruction set as PRM Section 8 prints it, one
|
|
4
|
+
line per encoding: a 16-character bit pattern, the handler, and the operand
|
|
5
|
+
constraint. In a pattern ``0`` and ``1`` are fixed bits and letters are
|
|
6
|
+
fields: ``ss`` a size (00 byte, 01 word, 10 long; 11 is never that
|
|
7
|
+
instruction), ``eeeeee`` an effective address, anything else a field the
|
|
8
|
+
handler decodes. The constraint names the addressing category of PRM Table
|
|
9
|
+
2-4 the EA field must fall in; an EA outside it makes the word something
|
|
10
|
+
else or illegal (UM 6.3.6, docs/undocumented-behavior.md).
|
|
11
|
+
|
|
12
|
+
:func:`build_table` walks all 65,536 words once per class, at first use, and
|
|
13
|
+
checks that no word matches two rules. A word that matches none runs the
|
|
14
|
+
illegal-instruction exception, or the line 1010 / line 1111 emulator
|
|
15
|
+
exception when its top four bits say so (UM 6.3.6). Dispatch is then one
|
|
16
|
+
list index and one call per instruction; handlers receive the opcode word
|
|
17
|
+
and never see the table.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
|
|
22
|
+
from m68000_python._ea import (
|
|
23
|
+
ALL,
|
|
24
|
+
ALTERABLE,
|
|
25
|
+
AN,
|
|
26
|
+
CONTROL,
|
|
27
|
+
DATA,
|
|
28
|
+
DATA_ALTERABLE,
|
|
29
|
+
EA_KIND,
|
|
30
|
+
MEMORY_ALTERABLE,
|
|
31
|
+
POSTINC,
|
|
32
|
+
PREDEC,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
Check = Callable[[int], bool]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _ea_field(opcode: int) -> int | None:
|
|
39
|
+
return EA_KIND[opcode & 0x3F]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def ea(category: frozenset) -> Check:
|
|
43
|
+
"""The EA in bits 5-0 must be in ``category``."""
|
|
44
|
+
return lambda opcode: _ea_field(opcode) in category
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def sized(category: frozenset) -> Check:
|
|
48
|
+
"""Size in bits 7-6 is not 11, the EA is in ``category``, and An is never a byte."""
|
|
49
|
+
|
|
50
|
+
def check(opcode: int) -> bool:
|
|
51
|
+
size_bits = (opcode >> 6) & 3
|
|
52
|
+
kind = _ea_field(opcode)
|
|
53
|
+
if size_bits == 3 or kind not in category:
|
|
54
|
+
return False
|
|
55
|
+
return not (size_bits == 0 and kind == AN)
|
|
56
|
+
|
|
57
|
+
return check
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def any_size_register(opcode: int) -> bool:
|
|
61
|
+
"""Register forms with a size field (ADDX, SUBX, CMPM, shifts): not 11."""
|
|
62
|
+
return (opcode >> 6) & 3 != 3
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def move(opcode: int) -> bool:
|
|
66
|
+
"""MOVE: source any mode (no An for a byte), destination data alterable (PRM 4, MOVE)."""
|
|
67
|
+
source = _ea_field(opcode)
|
|
68
|
+
destination = EA_KIND[((opcode >> 3) & 0x38) | ((opcode >> 9) & 7)]
|
|
69
|
+
if source is None or destination not in DATA_ALTERABLE:
|
|
70
|
+
return False
|
|
71
|
+
return not ((opcode >> 12) == 1 and source == AN)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def movea(opcode: int) -> bool:
|
|
75
|
+
"""MOVEA: any source mode, word or long only (PRM 4, MOVEA)."""
|
|
76
|
+
return _ea_field(opcode) is not None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def always(opcode: int) -> bool:
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
CONTROL_ALTERABLE = CONTROL & ALTERABLE
|
|
84
|
+
|
|
85
|
+
#: (bit pattern, handler, constraint) -- PRM Section 8, in opcode-map order.
|
|
86
|
+
RULES: tuple[tuple[str, str, Check], ...] = (
|
|
87
|
+
# 0000: bit manipulation, MOVEP, immediate
|
|
88
|
+
("0000 0000 0011 1100", "ori_to_ccr", always),
|
|
89
|
+
("0000 0000 0111 1100", "ori_to_sr", always),
|
|
90
|
+
("0000 0000 ssee eeee", "ori", sized(DATA_ALTERABLE)),
|
|
91
|
+
("0000 0010 0011 1100", "andi_to_ccr", always),
|
|
92
|
+
("0000 0010 0111 1100", "andi_to_sr", always),
|
|
93
|
+
("0000 0010 ssee eeee", "andi", sized(DATA_ALTERABLE)),
|
|
94
|
+
("0000 0100 ssee eeee", "subi", sized(DATA_ALTERABLE)),
|
|
95
|
+
("0000 0110 ssee eeee", "addi", sized(DATA_ALTERABLE)),
|
|
96
|
+
("0000 1010 0011 1100", "eori_to_ccr", always),
|
|
97
|
+
("0000 1010 0111 1100", "eori_to_sr", always),
|
|
98
|
+
("0000 1010 ssee eeee", "eori", sized(DATA_ALTERABLE)),
|
|
99
|
+
("0000 1100 ssee eeee", "cmpi", sized(DATA_ALTERABLE)),
|
|
100
|
+
("0000 1000 00ee eeee", "btst", ea(DATA - {11})),
|
|
101
|
+
("0000 1000 01ee eeee", "bchg", ea(DATA_ALTERABLE)),
|
|
102
|
+
("0000 1000 10ee eeee", "bclr", ea(DATA_ALTERABLE)),
|
|
103
|
+
("0000 1000 11ee eeee", "bset", ea(DATA_ALTERABLE)),
|
|
104
|
+
("0000 rrr1 00ee eeee", "btst", ea(DATA)),
|
|
105
|
+
("0000 rrr1 01ee eeee", "bchg", ea(DATA_ALTERABLE)),
|
|
106
|
+
("0000 rrr1 10ee eeee", "bclr", ea(DATA_ALTERABLE)),
|
|
107
|
+
("0000 rrr1 11ee eeee", "bset", ea(DATA_ALTERABLE)),
|
|
108
|
+
("0000 rrr1 oo00 1aaa", "movep", always),
|
|
109
|
+
# 0001, 0010, 0011: MOVE and MOVEA
|
|
110
|
+
("0001 RRRM MMee eeee", "move", move),
|
|
111
|
+
("0011 RRRM MMee eeee", "move", move),
|
|
112
|
+
("0010 RRRM MMee eeee", "move", move),
|
|
113
|
+
("0011 aaa0 01ee eeee", "movea", movea),
|
|
114
|
+
("0010 aaa0 01ee eeee", "movea", movea),
|
|
115
|
+
# 0100: miscellaneous
|
|
116
|
+
("0100 0000 11ee eeee", "move_from_sr", ea(DATA_ALTERABLE)),
|
|
117
|
+
("0100 0000 ssee eeee", "negx", sized(DATA_ALTERABLE)),
|
|
118
|
+
("0100 0010 ssee eeee", "clr", sized(DATA_ALTERABLE)),
|
|
119
|
+
("0100 0100 11ee eeee", "move_to_ccr", ea(DATA)),
|
|
120
|
+
("0100 0100 ssee eeee", "neg", sized(DATA_ALTERABLE)),
|
|
121
|
+
("0100 0110 11ee eeee", "move_to_sr", ea(DATA)),
|
|
122
|
+
("0100 0110 ssee eeee", "not", sized(DATA_ALTERABLE)),
|
|
123
|
+
("0100 1000 00ee eeee", "nbcd", ea(DATA_ALTERABLE)),
|
|
124
|
+
("0100 1000 0100 0rrr", "swap", always),
|
|
125
|
+
("0100 1000 01ee eeee", "pea", ea(CONTROL)),
|
|
126
|
+
("0100 1000 1000 0rrr", "ext", always),
|
|
127
|
+
("0100 1000 1100 0rrr", "ext", always),
|
|
128
|
+
("0100 1000 1see eeee", "movem", ea(CONTROL_ALTERABLE | {PREDEC})),
|
|
129
|
+
("0100 1010 1111 1100", "illegal", always),
|
|
130
|
+
("0100 1010 11ee eeee", "tas", ea(DATA_ALTERABLE)),
|
|
131
|
+
("0100 1010 ssee eeee", "tst", sized(DATA_ALTERABLE)),
|
|
132
|
+
("0100 1100 1see eeee", "movem", ea(CONTROL | {POSTINC})),
|
|
133
|
+
("0100 1110 0100 vvvv", "trap", always),
|
|
134
|
+
("0100 1110 0101 0rrr", "link", always),
|
|
135
|
+
("0100 1110 0101 1rrr", "unlk", always),
|
|
136
|
+
("0100 1110 0110 drrr", "move_usp", always),
|
|
137
|
+
("0100 1110 0111 0000", "reset", always),
|
|
138
|
+
("0100 1110 0111 0001", "nop", always),
|
|
139
|
+
("0100 1110 0111 0010", "stop", always),
|
|
140
|
+
("0100 1110 0111 0011", "rte", always),
|
|
141
|
+
("0100 1110 0111 0101", "rts", always),
|
|
142
|
+
("0100 1110 0111 0110", "trapv", always),
|
|
143
|
+
("0100 1110 0111 0111", "rtr", always),
|
|
144
|
+
("0100 1110 10ee eeee", "jsr", ea(CONTROL)),
|
|
145
|
+
("0100 1110 11ee eeee", "jmp", ea(CONTROL)),
|
|
146
|
+
("0100 rrr1 10ee eeee", "chk", ea(DATA)),
|
|
147
|
+
("0100 rrr1 11ee eeee", "lea", ea(CONTROL)),
|
|
148
|
+
# 0101: ADDQ, SUBQ, Scc, DBcc
|
|
149
|
+
("0101 qqq0 ssee eeee", "addq", sized(ALTERABLE)),
|
|
150
|
+
("0101 qqq1 ssee eeee", "subq", sized(ALTERABLE)),
|
|
151
|
+
("0101 cccc 1100 1rrr", "dbcc", always),
|
|
152
|
+
("0101 cccc 11ee eeee", "scc", ea(DATA_ALTERABLE)),
|
|
153
|
+
# 0110: Bcc, BRA, BSR; 0111: MOVEQ
|
|
154
|
+
("0110 0000 dddd dddd", "bra", always),
|
|
155
|
+
("0110 0001 dddd dddd", "bsr", always),
|
|
156
|
+
("0110 cccc dddd dddd", "bcc", lambda opcode: ((opcode >> 8) & 0xF) >= 2),
|
|
157
|
+
("0111 rrr0 dddd dddd", "moveq", always),
|
|
158
|
+
# 1000: OR, DIVU, DIVS, SBCD
|
|
159
|
+
("1000 rrr0 11ee eeee", "divu", ea(DATA)),
|
|
160
|
+
("1000 rrr1 11ee eeee", "divs", ea(DATA)),
|
|
161
|
+
("1000 yyy1 0000 mxxx", "sbcd", always),
|
|
162
|
+
("1000 rrr0 ssee eeee", "or", sized(DATA)),
|
|
163
|
+
("1000 rrr1 ssee eeee", "or", sized(MEMORY_ALTERABLE)),
|
|
164
|
+
# 1001: SUB, SUBA, SUBX
|
|
165
|
+
("1001 aaas 11ee eeee", "suba", ea(ALL)),
|
|
166
|
+
("1001 yyy1 ss00 mxxx", "subx", any_size_register),
|
|
167
|
+
("1001 rrr0 ssee eeee", "sub", sized(ALL)),
|
|
168
|
+
("1001 rrr1 ssee eeee", "sub", sized(MEMORY_ALTERABLE)),
|
|
169
|
+
# 1011: CMP, CMPA, CMPM, EOR
|
|
170
|
+
("1011 aaas 11ee eeee", "cmpa", ea(ALL)),
|
|
171
|
+
("1011 yyy1 ss00 1xxx", "cmpm", any_size_register),
|
|
172
|
+
("1011 rrr0 ssee eeee", "cmp", sized(ALL)),
|
|
173
|
+
("1011 rrr1 ssee eeee", "eor", sized(DATA_ALTERABLE)),
|
|
174
|
+
# 1100: AND, MULU, MULS, ABCD, EXG
|
|
175
|
+
("1100 rrr0 11ee eeee", "mulu", ea(DATA)),
|
|
176
|
+
("1100 rrr1 11ee eeee", "muls", ea(DATA)),
|
|
177
|
+
("1100 yyy1 0000 mxxx", "abcd", always),
|
|
178
|
+
("1100 xxx1 0100 0yyy", "exg", always),
|
|
179
|
+
("1100 xxx1 0100 1yyy", "exg", always),
|
|
180
|
+
("1100 xxx1 1000 1yyy", "exg", always),
|
|
181
|
+
("1100 rrr0 ssee eeee", "and", sized(DATA)),
|
|
182
|
+
("1100 rrr1 ssee eeee", "and", sized(MEMORY_ALTERABLE)),
|
|
183
|
+
# 1101: ADD, ADDA, ADDX
|
|
184
|
+
("1101 aaas 11ee eeee", "adda", ea(ALL)),
|
|
185
|
+
("1101 yyy1 ss00 mxxx", "addx", any_size_register),
|
|
186
|
+
("1101 rrr0 ssee eeee", "add", sized(ALL)),
|
|
187
|
+
("1101 rrr1 ssee eeee", "add", sized(MEMORY_ALTERABLE)),
|
|
188
|
+
# 1110: shifts and rotates, memory (word, by one) then register forms
|
|
189
|
+
("1110 000d 11ee eeee", "asd_memory", ea(MEMORY_ALTERABLE)),
|
|
190
|
+
("1110 001d 11ee eeee", "lsd_memory", ea(MEMORY_ALTERABLE)),
|
|
191
|
+
("1110 010d 11ee eeee", "roxd_memory", ea(MEMORY_ALTERABLE)),
|
|
192
|
+
("1110 011d 11ee eeee", "rod_memory", ea(MEMORY_ALTERABLE)),
|
|
193
|
+
("1110 cccd ssi0 0rrr", "asd", any_size_register),
|
|
194
|
+
("1110 cccd ssi0 1rrr", "lsd", any_size_register),
|
|
195
|
+
("1110 cccd ssi1 0rrr", "roxd", any_size_register),
|
|
196
|
+
("1110 cccd ssi1 1rrr", "rod", any_size_register),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _mask_and_value(pattern: str) -> tuple[int, int]:
|
|
201
|
+
bits = pattern.replace(" ", "")
|
|
202
|
+
assert len(bits) == 16, pattern
|
|
203
|
+
mask = value = 0
|
|
204
|
+
for bit in bits:
|
|
205
|
+
mask <<= 1
|
|
206
|
+
value <<= 1
|
|
207
|
+
if bit in "01":
|
|
208
|
+
mask |= 1
|
|
209
|
+
value |= bit == "1"
|
|
210
|
+
return mask, value
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
#: RULES with each pattern turned into (mask, value), in the same order.
|
|
214
|
+
COMPILED = tuple((*_mask_and_value(pattern), name, check) for pattern, name, check in RULES)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def decode(opcode: int) -> str:
|
|
218
|
+
"""The handler name for ``opcode``: a rule's, or illegal / line_a / line_f."""
|
|
219
|
+
found = [
|
|
220
|
+
name for mask, value, name, check in COMPILED if opcode & mask == value and check(opcode)
|
|
221
|
+
]
|
|
222
|
+
if len(found) > 1:
|
|
223
|
+
raise AssertionError(f"{opcode:04X} matches {found}")
|
|
224
|
+
if found:
|
|
225
|
+
return found[0]
|
|
226
|
+
family = opcode >> 12
|
|
227
|
+
if family == 0xA:
|
|
228
|
+
return "line_a"
|
|
229
|
+
if family == 0xF:
|
|
230
|
+
return "line_f"
|
|
231
|
+
return "illegal"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
#: The handler name of every first word, computed once at import.
|
|
235
|
+
NAMES: tuple[str, ...] = tuple(decode(opcode) for opcode in range(0x10000))
|
|
236
|
+
|
|
237
|
+
Handler = Callable[..., None]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def build_table(cls: type) -> list[Handler]:
|
|
241
|
+
"""Return the 65,536 dispatch entries of ``cls``: ``table[opcode](cpu, opcode)``."""
|
|
242
|
+
handlers = {name: getattr(cls, "_op_" + name) for name in set(NAMES)}
|
|
243
|
+
return [handlers[name] for name in NAMES]
|
m68000_python/_ea.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Effective addresses: the twelve modes of the 68000 (PRM 2.2).
|
|
2
|
+
|
|
3
|
+
The 6-bit EA field is ``mode:register``; mode 7 uses the register field to
|
|
4
|
+
select five more forms. :data:`EA_KIND` maps the field to one of the twelve
|
|
5
|
+
kinds below once, at import, and handlers carry the kind, not the field.
|
|
6
|
+
|
|
7
|
+
Timing is Table 8-1 of the UM, spent in the order the microcode spends it:
|
|
8
|
+
``-(An)`` and ``(d8,An,Xn)`` start with two internal clocks, extension words
|
|
9
|
+
are taken from the prefetch queue (each one refills it with a program read),
|
|
10
|
+
and the operand access comes last. The steps that bring the microcode's PC
|
|
11
|
+
register up to the fetch address (what an address error stacks) are the
|
|
12
|
+
corpus's, T3.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from m68000_python._core import MASK
|
|
16
|
+
|
|
17
|
+
# The twelve kinds, in PRM 2.2 order.
|
|
18
|
+
DN = 0 # Dn data register direct
|
|
19
|
+
AN = 1 # An address register direct
|
|
20
|
+
IND = 2 # (An) address register indirect
|
|
21
|
+
POSTINC = 3 # (An)+ indirect with postincrement
|
|
22
|
+
PREDEC = 4 # -(An) indirect with predecrement
|
|
23
|
+
DISP = 5 # (d16,An) indirect with displacement
|
|
24
|
+
INDEX = 6 # (d8,An,Xn) indirect with index
|
|
25
|
+
ABSW = 7 # (xxx).W absolute short
|
|
26
|
+
ABSL = 8 # (xxx).L absolute long
|
|
27
|
+
PCDISP = 9 # (d16,PC) program counter with displacement
|
|
28
|
+
PCINDEX = 10 # (d8,PC,Xn) program counter with index
|
|
29
|
+
IMM = 11 # #<data> immediate
|
|
30
|
+
|
|
31
|
+
KIND_NAMES = (
|
|
32
|
+
"Dn", "An", "(An)", "(An)+", "-(An)", "(d16,An)", "(d8,An,Xn)",
|
|
33
|
+
"(xxx).W", "(xxx).L", "(d16,PC)", "(d8,PC,Xn)", "#<data>",
|
|
34
|
+
) # fmt: skip
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _kind(field: int) -> int | None:
|
|
38
|
+
mode, register = field >> 3, field & 7
|
|
39
|
+
if mode < 7:
|
|
40
|
+
return mode
|
|
41
|
+
return (ABSW, ABSL, PCDISP, PCINDEX, IMM, None, None, None)[register]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
#: EA field (bits 5-0) -> kind, or None for mode 7 registers 5-7 (illegal).
|
|
45
|
+
EA_KIND: tuple[int | None, ...] = tuple(_kind(field) for field in range(64))
|
|
46
|
+
|
|
47
|
+
# The addressing categories of PRM Table 2-4, as sets of kinds.
|
|
48
|
+
ALL = frozenset(range(12))
|
|
49
|
+
DATA = ALL - {AN}
|
|
50
|
+
MEMORY = ALL - {DN, AN}
|
|
51
|
+
CONTROL = frozenset({IND, DISP, INDEX, ABSW, ABSL, PCDISP, PCINDEX})
|
|
52
|
+
ALTERABLE = frozenset({DN, AN, IND, POSTINC, PREDEC, DISP, INDEX, ABSW, ABSL})
|
|
53
|
+
DATA_ALTERABLE = DATA & ALTERABLE
|
|
54
|
+
MEMORY_ALTERABLE = MEMORY & ALTERABLE
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def sign_extend_8(value: int) -> int:
|
|
58
|
+
return (value & 0xFF) - 0x100 if value & 0x80 else value & 0xFF
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def sign_extend_16(value: int) -> int:
|
|
62
|
+
return (value & 0xFFFF) - 0x10000 if value & 0x8000 else value & 0xFFFF
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def word_to_long(value: int) -> int:
|
|
66
|
+
"""A word sign-extended to 32 bits, as An, MOVEM.W and EXT.L take it (PRM 2.3)."""
|
|
67
|
+
return sign_extend_16(value) & 0xFFFFFFFF
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class EAMixin:
|
|
71
|
+
"""Private effective-address implementation shared by every family."""
|
|
72
|
+
|
|
73
|
+
def _step_size(self, register: int, size: int) -> int:
|
|
74
|
+
# (A7)+ and -(A7) move by 2 for a byte, keeping SP even (PRM 2.2.4-2.2.5).
|
|
75
|
+
return 2 if size == 1 and register == 7 else size
|
|
76
|
+
|
|
77
|
+
def _index(self, base: int) -> int:
|
|
78
|
+
"""Take the brief extension word from the queue and apply it to ``base``."""
|
|
79
|
+
return self._indexed(base, self._extension())
|
|
80
|
+
|
|
81
|
+
def _indexed(self, base: int, extension: int) -> int:
|
|
82
|
+
"""Apply a brief extension word (PRM 2.4) to ``base``.
|
|
83
|
+
|
|
84
|
+
Bits 15-12 name Xn (D0-D7, A0-A7), bit 11 chooses the sign-extended
|
|
85
|
+
low word (0) or the whole register (1), bits 7-0 are the signed
|
|
86
|
+
displacement. Bits 10-8, the 68020's scale, are ignored (PRM 2.4).
|
|
87
|
+
JMP, JSR, LEA and PEA apply the word still waiting in IRC.
|
|
88
|
+
"""
|
|
89
|
+
index = self.R[extension >> 12]
|
|
90
|
+
if not extension & 0x0800:
|
|
91
|
+
index = sign_extend_16(index)
|
|
92
|
+
return (base + index + sign_extend_8(extension)) & 0xFFFFFFFF
|
|
93
|
+
|
|
94
|
+
def _ea_address(self, kind: int, register: int, size: int) -> int:
|
|
95
|
+
"""Compute a memory operand's address, with its extension words and clocks.
|
|
96
|
+
|
|
97
|
+
The standard source-operand sequence: the one TST, CMP, ADD <ea>,Dn
|
|
98
|
+
and the read half of a read-modify-write use.
|
|
99
|
+
"""
|
|
100
|
+
R = self.R
|
|
101
|
+
if kind == IND:
|
|
102
|
+
return R[8 + register]
|
|
103
|
+
if kind == POSTINC:
|
|
104
|
+
address = R[8 + register]
|
|
105
|
+
R[8 + register] = (address + self._step_size(register, size)) & 0xFFFFFFFF
|
|
106
|
+
return address
|
|
107
|
+
if kind == PREDEC:
|
|
108
|
+
self._cycles += 2
|
|
109
|
+
self._commit_pc()
|
|
110
|
+
address = (R[8 + register] - self._step_size(register, size)) & 0xFFFFFFFF
|
|
111
|
+
R[8 + register] = address
|
|
112
|
+
return address
|
|
113
|
+
if kind == DISP:
|
|
114
|
+
return (R[8 + register] + sign_extend_16(self._extension())) & 0xFFFFFFFF
|
|
115
|
+
if kind == INDEX:
|
|
116
|
+
self._cycles += 2
|
|
117
|
+
return self._index(R[8 + register])
|
|
118
|
+
if kind == ABSW:
|
|
119
|
+
self._commit_pc()
|
|
120
|
+
return word_to_long(self._extension())
|
|
121
|
+
if kind == ABSL:
|
|
122
|
+
high = self._extension()
|
|
123
|
+
self._commit_pc()
|
|
124
|
+
return ((high << 16) | self._extension()) & 0xFFFFFFFF
|
|
125
|
+
if kind == PCDISP:
|
|
126
|
+
base = self._pc - 2 # the address of the extension word itself
|
|
127
|
+
return (base + sign_extend_16(self._extension())) & 0xFFFFFFFF
|
|
128
|
+
if kind == PCINDEX:
|
|
129
|
+
self._cycles += 2
|
|
130
|
+
return self._index(self._pc - 2)
|
|
131
|
+
raise AssertionError(f"not a memory kind: {kind}")
|
|
132
|
+
|
|
133
|
+
def _ea_read(self, kind: int, register: int, size: int) -> int:
|
|
134
|
+
"""Fetch a source operand of any kind (UM Table 8-1)."""
|
|
135
|
+
if kind == DN:
|
|
136
|
+
return self.R[register] & MASK[size]
|
|
137
|
+
if kind == AN:
|
|
138
|
+
return self.R[8 + register] & MASK[size]
|
|
139
|
+
if kind == IMM:
|
|
140
|
+
if size == 4:
|
|
141
|
+
return self._extension_long()
|
|
142
|
+
return self._extension() & MASK[size]
|
|
143
|
+
return self._ea_fetch(kind, register, size)[1]
|
|
144
|
+
|
|
145
|
+
def _ea_fetch(self, kind: int, register: int, size: int) -> tuple[int, int]:
|
|
146
|
+
"""Address and value of a memory operand: the read half of every access.
|
|
147
|
+
|
|
148
|
+
PC-relative operands are read in program space (FC 2/6, UM Table 3-2).
|
|
149
|
+
"""
|
|
150
|
+
if size == 4 and kind == POSTINC:
|
|
151
|
+
# A long (An)+ steps An between its two reads (SST ADD.l, MOVE.l and
|
|
152
|
+
# every long (An)+ read; T3): a fault on
|
|
153
|
+
# the first leaves An alone.
|
|
154
|
+
R = self.R
|
|
155
|
+
address = R[8 + register]
|
|
156
|
+
high = self._read_word(address)
|
|
157
|
+
R[8 + register] = (address + 4) & 0xFFFFFFFF
|
|
158
|
+
return address, (high << 16) | self._read_word(address + 2)
|
|
159
|
+
if size == 4 and kind == PREDEC:
|
|
160
|
+
# A long -(An) does not bring PC up to date, unlike a word (SST
|
|
161
|
+
# ADD.l, MOVE.l and every long -(An) read; T3).
|
|
162
|
+
self._cycles += 2
|
|
163
|
+
address = (self.R[8 + register] - 4) & 0xFFFFFFFF
|
|
164
|
+
self.R[8 + register] = address
|
|
165
|
+
return address, self._read_long(address)
|
|
166
|
+
address = self._ea_address(kind, register, size)
|
|
167
|
+
if kind >= PCDISP:
|
|
168
|
+
return address, self._read_program_operand(size, address)
|
|
169
|
+
return address, self._read(size, address)
|
|
170
|
+
|
|
171
|
+
def _write_register(self, register: int, size: int, value: int) -> None:
|
|
172
|
+
"""Write the low byte, word or all of a data register (PRM 1.1)."""
|
|
173
|
+
if size == 4:
|
|
174
|
+
self.R[register] = value & 0xFFFFFFFF
|
|
175
|
+
else:
|
|
176
|
+
mask = MASK[size]
|
|
177
|
+
self.R[register] = (self.R[register] & ~mask & 0xFFFFFFFF) | (value & mask)
|
m68000_python/_flags.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Condition codes: the flag rules of PRM Table 3-18 and the tests of Table 3-19.
|
|
2
|
+
|
|
3
|
+
Every rule is written once here and used by every family that shares it:
|
|
4
|
+
``_flags_logic`` for the moves, logic and tests (N, Z from the result, V and C
|
|
5
|
+
cleared, X untouched), ``_flags_add``/``_flags_sub`` for binary arithmetic,
|
|
6
|
+
``_flags_cmp`` for the compares (X untouched), and the ``x`` variants for
|
|
7
|
+
ADDX/SUBX/NEGX, whose Z is sticky: cleared by a nonzero result, otherwise
|
|
8
|
+
unchanged (PRM Table 3-18).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from m68000_python._core import MASK, MSB, C, N, V, X, Z
|
|
12
|
+
|
|
13
|
+
CONDITION_NAMES = (
|
|
14
|
+
"T", "F", "HI", "LS", "CC", "CS", "NE", "EQ",
|
|
15
|
+
"VC", "VS", "PL", "MI", "GE", "LT", "GT", "LE",
|
|
16
|
+
) # fmt: skip
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _test(cc: int, ccr: int) -> bool:
|
|
20
|
+
"""PRM Table 3-19, evaluated for one condition and one N/Z/V/C value."""
|
|
21
|
+
n, z, v, c = bool(ccr & N), bool(ccr & Z), bool(ccr & V), bool(ccr & C)
|
|
22
|
+
return (
|
|
23
|
+
True, # T
|
|
24
|
+
False, # F
|
|
25
|
+
not c and not z, # HI
|
|
26
|
+
c or z, # LS
|
|
27
|
+
not c, # CC (HS)
|
|
28
|
+
c, # CS (LO)
|
|
29
|
+
not z, # NE
|
|
30
|
+
z, # EQ
|
|
31
|
+
not v, # VC
|
|
32
|
+
v, # VS
|
|
33
|
+
not n, # PL
|
|
34
|
+
n, # MI
|
|
35
|
+
n == v, # GE
|
|
36
|
+
n != v, # LT
|
|
37
|
+
not z and n == v, # GT
|
|
38
|
+
z or n != v, # LE
|
|
39
|
+
)[cc]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
#: CONDITION[cc][SR & 0xF] is the truth of condition ``cc``; built at import.
|
|
43
|
+
CONDITION: tuple[tuple[bool, ...], ...] = tuple(
|
|
44
|
+
tuple(_test(cc, ccr) for ccr in range(16)) for cc in range(16)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FlagsMixin:
|
|
49
|
+
"""Private condition-code helpers."""
|
|
50
|
+
|
|
51
|
+
def _flags_logic(self, result: int, size: int) -> None:
|
|
52
|
+
"""N and Z from ``result``; V and C cleared; X unchanged."""
|
|
53
|
+
ccr = self.SR & (0xFF00 | X)
|
|
54
|
+
if result & MSB[size]:
|
|
55
|
+
ccr |= N
|
|
56
|
+
if not result & MASK[size]:
|
|
57
|
+
ccr |= Z
|
|
58
|
+
self.SR = ccr
|
|
59
|
+
|
|
60
|
+
# A long result leaves the ALU as two words, and the microcode updates the
|
|
61
|
+
# flags once per word: N Z V C from the low word, then N from the high
|
|
62
|
+
# word with Z kept only if the high word is zero too. Only an address
|
|
63
|
+
# error between the two can see the halfway state; the stacked SR does
|
|
64
|
+
# (SST MOVE.l, T3). _flags_logic is the two applied in order.
|
|
65
|
+
|
|
66
|
+
def _flags_low_word(self, value: int) -> None:
|
|
67
|
+
"""N Z from the low word; V C cleared (the first half of a long's flags)."""
|
|
68
|
+
ccr = self.SR & (0xFF00 | X)
|
|
69
|
+
if value & 0x8000:
|
|
70
|
+
ccr |= N
|
|
71
|
+
if not value & 0xFFFF:
|
|
72
|
+
ccr |= Z
|
|
73
|
+
self.SR = ccr
|
|
74
|
+
|
|
75
|
+
def _flags_high_word(self, value: int) -> None:
|
|
76
|
+
"""N from bit 31; Z kept only if the high word is also zero (the second half)."""
|
|
77
|
+
ccr = self.SR & ~N
|
|
78
|
+
if value & 0x80000000:
|
|
79
|
+
ccr |= N
|
|
80
|
+
if value & 0xFFFF0000:
|
|
81
|
+
ccr &= ~Z
|
|
82
|
+
self.SR = ccr
|
|
83
|
+
|
|
84
|
+
def _flags_add(self, destination: int, source: int, result: int, size: int) -> None:
|
|
85
|
+
"""X N Z V C of ``destination + source`` (``result`` unmasked)."""
|
|
86
|
+
msb = MSB[size]
|
|
87
|
+
ccr = self.SR & 0xFF00
|
|
88
|
+
if result & msb:
|
|
89
|
+
ccr |= N
|
|
90
|
+
if not result & MASK[size]:
|
|
91
|
+
ccr |= Z
|
|
92
|
+
if (source ^ result) & (destination ^ result) & msb:
|
|
93
|
+
ccr |= V
|
|
94
|
+
if result > MASK[size]:
|
|
95
|
+
ccr |= X | C
|
|
96
|
+
self.SR = ccr
|
|
97
|
+
|
|
98
|
+
def _flags_sub(self, destination: int, source: int, result: int, size: int) -> None:
|
|
99
|
+
"""X N Z V C of ``destination - source`` (``result`` unmasked, may be negative)."""
|
|
100
|
+
msb = MSB[size]
|
|
101
|
+
ccr = self.SR & 0xFF00
|
|
102
|
+
if result & msb:
|
|
103
|
+
ccr |= N
|
|
104
|
+
if not result & MASK[size]:
|
|
105
|
+
ccr |= Z
|
|
106
|
+
if (source ^ destination) & (result ^ destination) & msb:
|
|
107
|
+
ccr |= V
|
|
108
|
+
if result < 0:
|
|
109
|
+
ccr |= X | C
|
|
110
|
+
self.SR = ccr
|
|
111
|
+
|
|
112
|
+
def _flags_cmp(self, destination: int, source: int, result: int, size: int) -> None:
|
|
113
|
+
"""N Z V C of ``destination - source``; X unchanged (PRM Table 3-18, CMP)."""
|
|
114
|
+
msb = MSB[size]
|
|
115
|
+
ccr = self.SR & (0xFF00 | X)
|
|
116
|
+
if result & msb:
|
|
117
|
+
ccr |= N
|
|
118
|
+
if not result & MASK[size]:
|
|
119
|
+
ccr |= Z
|
|
120
|
+
if (source ^ destination) & (result ^ destination) & msb:
|
|
121
|
+
ccr |= V
|
|
122
|
+
if result < 0:
|
|
123
|
+
ccr |= C
|
|
124
|
+
self.SR = ccr
|
|
125
|
+
|
|
126
|
+
def _flags_addx(self, destination: int, source: int, result: int, size: int) -> None:
|
|
127
|
+
"""ADDX: as ADD, but Z is only ever cleared (PRM Table 3-18)."""
|
|
128
|
+
msb = MSB[size]
|
|
129
|
+
ccr = self.SR & (0xFF00 | Z)
|
|
130
|
+
if result & msb:
|
|
131
|
+
ccr |= N
|
|
132
|
+
if result & MASK[size]:
|
|
133
|
+
ccr &= ~Z
|
|
134
|
+
if (source ^ result) & (destination ^ result) & msb:
|
|
135
|
+
ccr |= V
|
|
136
|
+
if result > MASK[size]:
|
|
137
|
+
ccr |= X | C
|
|
138
|
+
self.SR = ccr
|
|
139
|
+
|
|
140
|
+
def _flags_subx(self, destination: int, source: int, result: int, size: int) -> None:
|
|
141
|
+
"""SUBX and NEGX: as SUB, but Z is only ever cleared (PRM Table 3-18)."""
|
|
142
|
+
msb = MSB[size]
|
|
143
|
+
ccr = self.SR & (0xFF00 | Z)
|
|
144
|
+
if result & msb:
|
|
145
|
+
ccr |= N
|
|
146
|
+
if result & MASK[size]:
|
|
147
|
+
ccr &= ~Z
|
|
148
|
+
if (source ^ destination) & (result ^ destination) & msb:
|
|
149
|
+
ccr |= V
|
|
150
|
+
if result < 0:
|
|
151
|
+
ccr |= X | C
|
|
152
|
+
self.SR = ccr
|