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,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)
@@ -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