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
m68000_python/_alu.py
ADDED
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
"""Integer arithmetic and logic (PRM Tables 3-3 and 3-4).
|
|
2
|
+
|
|
3
|
+
ADD, SUB, AND, OR, EOR, CMP and their A/I/Q/X/M forms; NEG, NEGX, NOT, CLR,
|
|
4
|
+
TST, TAS; MULU, MULS, DIVU, DIVS. Flag rules are _flags.py's. The bus
|
|
5
|
+
sequences are three shapes, all the microcode's (corpus, T3):
|
|
6
|
+
|
|
7
|
+
* **to a data register**: the operand is read (EA timing, UM Table 8-1),
|
|
8
|
+
the result written, the queue refilled; a long result then spends 2
|
|
9
|
+
internal clocks after a memory operand and 4 after a register or
|
|
10
|
+
immediate one (UM Table 8-4 note **);
|
|
11
|
+
* **to memory** (read-modify-write): the operand is read, the queue
|
|
12
|
+
refilled, the result written -- a long one low word first;
|
|
13
|
+
* **compares** read and refill only.
|
|
14
|
+
|
|
15
|
+
Only the operand read can fault in a read-modify-write: the write goes to
|
|
16
|
+
the address the read already proved even.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from m68000_python._core import MASK, VECTOR_BUS_ERROR, BusError, N, V, X, Z
|
|
20
|
+
from m68000_python._ea import AN, DN, EA_KIND, IMM, sign_extend_16, word_to_long
|
|
21
|
+
|
|
22
|
+
#: Bits 7-6 of the common size field (PRM Section 8): 00 byte, 01 word, 10 long.
|
|
23
|
+
SIZE = (1, 2, 4, 0)
|
|
24
|
+
#: ADDQ/SUBQ data: 1-8, with 0 meaning 8 (PRM 4, ADDQ).
|
|
25
|
+
QUICK = (8, 1, 2, 3, 4, 5, 6, 7)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def multiply_unsigned_clocks(source: int) -> int:
|
|
29
|
+
"""MULU: 38 + 2n, n the number of 1 bits in the source word (UM Table 8-4, note)."""
|
|
30
|
+
return 38 + 2 * bin(source & 0xFFFF).count("1")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def multiply_signed_clocks(source: int) -> int:
|
|
34
|
+
"""MULS: 38 + 2n, n the 01/10 transitions of the source with a 0 appended (UM Table 8-4)."""
|
|
35
|
+
pattern = (source & 0xFFFF) << 1
|
|
36
|
+
return 38 + 2 * bin((pattern ^ (pattern >> 1)) & 0xFFFF).count("1")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def divide_unsigned_clocks(dividend: int, divisor: int) -> int:
|
|
40
|
+
"""DIVU's internal clocks, from the microcode's shift-and-subtract loop.
|
|
41
|
+
|
|
42
|
+
The manual gives only a maximum (UM Table 8-4). The loop: overflow is
|
|
43
|
+
detected first; otherwise 15 steps each shift the dividend left, and a
|
|
44
|
+
step whose shifted-out bit was 0 costs 2 more clocks, less 1 if the
|
|
45
|
+
trial subtraction succeeded. Jorge Cwik's analysis (2005, carried in
|
|
46
|
+
WinUAE, T2) states the same count; SST DIVU checks it on every case (T3).
|
|
47
|
+
"""
|
|
48
|
+
if dividend >> 16 >= divisor:
|
|
49
|
+
return 10
|
|
50
|
+
half_clocks = 38
|
|
51
|
+
shifted_divisor = divisor << 16
|
|
52
|
+
for _ in range(15):
|
|
53
|
+
carry = dividend & 0x80000000
|
|
54
|
+
dividend = (dividend << 1) & 0xFFFFFFFF
|
|
55
|
+
if carry:
|
|
56
|
+
dividend = (dividend - shifted_divisor) & 0xFFFFFFFF
|
|
57
|
+
else:
|
|
58
|
+
half_clocks += 2
|
|
59
|
+
if dividend >= shifted_divisor:
|
|
60
|
+
dividend -= shifted_divisor
|
|
61
|
+
half_clocks -= 1
|
|
62
|
+
return half_clocks * 2
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def divide_signed_clocks(dividend: int, divisor: int) -> int:
|
|
66
|
+
"""DIVS's internal clocks: sign handling, then one step per quotient bit.
|
|
67
|
+
|
|
68
|
+
``dividend`` and ``divisor`` are signed. Absolute overflow (|dividend|
|
|
69
|
+
>> 16 >= |divisor|) is found early; otherwise the unsigned loop runs on
|
|
70
|
+
the magnitudes and each of the 15 high quotient bits that is 0 costs 2
|
|
71
|
+
clocks (Cwik 2005, T2; checked against the corpus, T3).
|
|
72
|
+
"""
|
|
73
|
+
half_clocks = 6
|
|
74
|
+
if dividend < 0:
|
|
75
|
+
half_clocks += 1
|
|
76
|
+
if abs(dividend) >> 16 >= abs(divisor):
|
|
77
|
+
return (half_clocks + 2) * 2
|
|
78
|
+
quotient = abs(dividend) // abs(divisor)
|
|
79
|
+
half_clocks += 55
|
|
80
|
+
if divisor >= 0:
|
|
81
|
+
half_clocks += -1 if dividend >= 0 else 1
|
|
82
|
+
for bit in range(15):
|
|
83
|
+
if not quotient & (0x8000 >> bit):
|
|
84
|
+
half_clocks += 1
|
|
85
|
+
return half_clocks * 2
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ALUMixin:
|
|
89
|
+
"""Private arithmetic and logic implementation."""
|
|
90
|
+
|
|
91
|
+
# -- the operations: result from destination and source, flags set --------
|
|
92
|
+
|
|
93
|
+
def _add(self, destination: int, source: int, size: int) -> int:
|
|
94
|
+
result = destination + source
|
|
95
|
+
self._flags_add(destination, source, result, size)
|
|
96
|
+
return result & MASK[size]
|
|
97
|
+
|
|
98
|
+
def _sub(self, destination: int, source: int, size: int) -> int:
|
|
99
|
+
result = destination - source
|
|
100
|
+
self._flags_sub(destination, source, result, size)
|
|
101
|
+
return result & MASK[size]
|
|
102
|
+
|
|
103
|
+
def _and(self, destination: int, source: int, size: int) -> int:
|
|
104
|
+
result = destination & source
|
|
105
|
+
self._flags_logic(result, size)
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
def _or(self, destination: int, source: int, size: int) -> int:
|
|
109
|
+
result = destination | source
|
|
110
|
+
self._flags_logic(result, size)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
def _eor(self, destination: int, source: int, size: int) -> int:
|
|
114
|
+
result = destination ^ source
|
|
115
|
+
self._flags_logic(result, size)
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
def _addx(self, destination: int, source: int, size: int) -> int:
|
|
119
|
+
result = destination + source + ((self.SR >> 4) & 1)
|
|
120
|
+
self._flags_addx(destination, source, result, size)
|
|
121
|
+
return result & MASK[size]
|
|
122
|
+
|
|
123
|
+
def _subx(self, destination: int, source: int, size: int) -> int:
|
|
124
|
+
result = destination - source - ((self.SR >> 4) & 1)
|
|
125
|
+
self._flags_subx(destination, source, result, size)
|
|
126
|
+
return result & MASK[size]
|
|
127
|
+
|
|
128
|
+
def _cmp(self, destination: int, source: int, size: int) -> None:
|
|
129
|
+
self._flags_cmp(destination, source, destination - source, size)
|
|
130
|
+
|
|
131
|
+
# -- the shapes ------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
def _to_data_register(self, register: int, size: int, result: int, kind: int) -> None:
|
|
134
|
+
"""Refill the queue and write Dn; a long spends 2 or 4 more clocks (Table 8-4)."""
|
|
135
|
+
self._prefetch()
|
|
136
|
+
if size == 4:
|
|
137
|
+
self._cycles += 4 if kind in (DN, AN, IMM) else 2
|
|
138
|
+
self.R[register] = result
|
|
139
|
+
else:
|
|
140
|
+
self._write_register(register, size, result)
|
|
141
|
+
|
|
142
|
+
def _modify(self, kind: int, register: int, size: int, operation, source: int) -> None:
|
|
143
|
+
"""Read-modify-write ``operation(destination, source)`` on an EA operand."""
|
|
144
|
+
if kind == DN:
|
|
145
|
+
result = operation(self.R[register] & MASK[size], source, size)
|
|
146
|
+
self._prefetch()
|
|
147
|
+
if size == 4:
|
|
148
|
+
self._cycles += 4
|
|
149
|
+
self._write_register(register, size, result)
|
|
150
|
+
return
|
|
151
|
+
address, value = self._ea_fetch(kind, register, size)
|
|
152
|
+
result = operation(value, source, size)
|
|
153
|
+
self._prefetch()
|
|
154
|
+
self._write_result(address, size, result)
|
|
155
|
+
|
|
156
|
+
def _write_result(self, address: int, size: int, result: int) -> None:
|
|
157
|
+
if size == 4:
|
|
158
|
+
self._write_long_low_first(address, result)
|
|
159
|
+
elif size == 2:
|
|
160
|
+
self._write_word(address, result)
|
|
161
|
+
else:
|
|
162
|
+
self._write_byte(address, result)
|
|
163
|
+
|
|
164
|
+
def _binary(self, opcode: int, operation) -> None:
|
|
165
|
+
"""ADD, SUB, AND, OR: ``<ea>,Dn`` when bit 8 is 0, ``Dn,<ea>`` when it is 1."""
|
|
166
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
167
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
168
|
+
register = opcode & 7
|
|
169
|
+
dn = (opcode >> 9) & 7
|
|
170
|
+
if opcode & 0x0100:
|
|
171
|
+
self._modify(kind, register, size, operation, self.R[dn] & MASK[size])
|
|
172
|
+
return
|
|
173
|
+
source = self._ea_read(kind, register, size)
|
|
174
|
+
result = operation(self.R[dn] & MASK[size], source, size)
|
|
175
|
+
self._to_data_register(dn, size, result, kind)
|
|
176
|
+
|
|
177
|
+
def _op_add(self, opcode: int) -> None:
|
|
178
|
+
"""ADD -- destination <- destination + source; X N Z V C (PRM 4-4; UM Table 8-4).
|
|
179
|
+
|
|
180
|
+
SST ADD.b/.w/.l pin the bus order, the idle clocks and the PC an
|
|
181
|
+
address error stacks (their cases also cover ADDI and ADDQ).
|
|
182
|
+
"""
|
|
183
|
+
self._binary(opcode, self._add)
|
|
184
|
+
|
|
185
|
+
def _op_sub(self, opcode: int) -> None:
|
|
186
|
+
"""SUB -- destination <- destination - source; X N Z V C (PRM 4-174; UM Table 8-4).
|
|
187
|
+
|
|
188
|
+
SST SUB.b/.w/.l pin the bus order, the idle clocks and the PC an
|
|
189
|
+
address error stacks (their cases also cover SUBI and SUBQ).
|
|
190
|
+
"""
|
|
191
|
+
self._binary(opcode, self._sub)
|
|
192
|
+
|
|
193
|
+
def _op_and(self, opcode: int) -> None:
|
|
194
|
+
"""AND -- destination <- destination AND source; N Z, V C cleared (PRM 4-15; UM Table 8-4).
|
|
195
|
+
|
|
196
|
+
SST AND.b/.w/.l pin the bus order, the idle clocks and the PC an
|
|
197
|
+
address error stacks (their cases also cover ANDI).
|
|
198
|
+
"""
|
|
199
|
+
self._binary(opcode, self._and)
|
|
200
|
+
|
|
201
|
+
def _op_or(self, opcode: int) -> None:
|
|
202
|
+
"""OR -- destination <- destination OR source; N Z, V C cleared (PRM 4-150; UM Table 8-4).
|
|
203
|
+
|
|
204
|
+
SST OR.b/.w/.l pin the bus order, the idle clocks and the PC an
|
|
205
|
+
address error stacks (their cases also cover ORI).
|
|
206
|
+
"""
|
|
207
|
+
self._binary(opcode, self._or)
|
|
208
|
+
|
|
209
|
+
def _op_eor(self, opcode: int) -> None:
|
|
210
|
+
"""EOR -- <ea> <- <ea> XOR Dn; N Z, V C cleared (PRM 4-100; UM Table 8-4).
|
|
211
|
+
|
|
212
|
+
SST EOR.b/.w/.l pin the bus order and the PC an address error stacks
|
|
213
|
+
(their cases also cover EORI).
|
|
214
|
+
"""
|
|
215
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
216
|
+
source = self.R[(opcode >> 9) & 7] & MASK[size]
|
|
217
|
+
self._modify(EA_KIND[opcode & 0x3F], opcode & 7, size, self._eor, source)
|
|
218
|
+
|
|
219
|
+
def _op_cmp(self, opcode: int) -> None:
|
|
220
|
+
"""CMP -- set N Z V C from Dn - source; X unchanged (PRM 4-75; UM Table 8-4).
|
|
221
|
+
|
|
222
|
+
SST CMP.b/.w/.l pin the bus order, the 2 idle clocks of a long and the PC
|
|
223
|
+
an address error stacks (their cases also cover CMPI and CMPM).
|
|
224
|
+
"""
|
|
225
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
226
|
+
source = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, size)
|
|
227
|
+
self._cmp(self.R[(opcode >> 9) & 7] & MASK[size], source, size)
|
|
228
|
+
self._prefetch()
|
|
229
|
+
if size == 4:
|
|
230
|
+
self._cycles += 2
|
|
231
|
+
|
|
232
|
+
# -- address-register forms (PRM 4-7, 4-77, 4-178) ------------------------
|
|
233
|
+
|
|
234
|
+
def _address_source(self, opcode: int) -> tuple[int, int]:
|
|
235
|
+
"""The source of ADDA/SUBA/CMPA, sign-extended to 32 bits, and its kind."""
|
|
236
|
+
size = 4 if opcode & 0x0100 else 2
|
|
237
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
238
|
+
source = self._ea_read(kind, opcode & 7, size)
|
|
239
|
+
if size == 2:
|
|
240
|
+
source = word_to_long(source)
|
|
241
|
+
return source, kind
|
|
242
|
+
|
|
243
|
+
def _address_idle(self, opcode: int, kind: int) -> None:
|
|
244
|
+
# Word forms and register/immediate sources: 4 clocks; long from memory: 2.
|
|
245
|
+
if not opcode & 0x0100 or kind in (DN, AN, IMM):
|
|
246
|
+
self._cycles += 4
|
|
247
|
+
else:
|
|
248
|
+
self._cycles += 2
|
|
249
|
+
|
|
250
|
+
def _op_adda(self, opcode: int) -> None:
|
|
251
|
+
"""ADDA -- An <- An + source, a word sign-extended; no flags (PRM 4-7; UM Table 8-4).
|
|
252
|
+
|
|
253
|
+
SST ADDA.w/.l pin the idle clocks (4 for a word or a register or immediate
|
|
254
|
+
long source, else 2) and the PC an address error stacks.
|
|
255
|
+
"""
|
|
256
|
+
source, kind = self._address_source(opcode)
|
|
257
|
+
an = 8 + ((opcode >> 9) & 7)
|
|
258
|
+
self._prefetch()
|
|
259
|
+
self._address_idle(opcode, kind)
|
|
260
|
+
self.R[an] = (self.R[an] + source) & 0xFFFFFFFF
|
|
261
|
+
|
|
262
|
+
def _op_suba(self, opcode: int) -> None:
|
|
263
|
+
"""SUBA -- An <- An - source, a word sign-extended; no flags (PRM 4-178; UM Table 8-4).
|
|
264
|
+
|
|
265
|
+
SST SUBA.w/.l pin the idle clocks (4 for a word or a register or immediate
|
|
266
|
+
long source, else 2) and the PC an address error stacks.
|
|
267
|
+
"""
|
|
268
|
+
source, kind = self._address_source(opcode)
|
|
269
|
+
an = 8 + ((opcode >> 9) & 7)
|
|
270
|
+
self._prefetch()
|
|
271
|
+
self._address_idle(opcode, kind)
|
|
272
|
+
self.R[an] = (self.R[an] - source) & 0xFFFFFFFF
|
|
273
|
+
|
|
274
|
+
def _op_cmpa(self, opcode: int) -> None:
|
|
275
|
+
"""CMPA -- set N Z V C from An - source, a 32-bit compare (PRM 4-77; UM Table 8-4).
|
|
276
|
+
|
|
277
|
+
SST CMPA.w/.l pin the 2 idle clocks and the PC an address error stacks.
|
|
278
|
+
"""
|
|
279
|
+
source, _ = self._address_source(opcode)
|
|
280
|
+
self._cmp(self.R[8 + ((opcode >> 9) & 7)], source, 4)
|
|
281
|
+
self._prefetch()
|
|
282
|
+
self._cycles += 2
|
|
283
|
+
|
|
284
|
+
# -- immediate forms (PRM 4-9, 4-18, 4-79, 4-102, 4-153, 4-180) ----------
|
|
285
|
+
|
|
286
|
+
def _immediate(self, size: int) -> int:
|
|
287
|
+
"""The immediate operand that follows the opcode word (byte in the low half)."""
|
|
288
|
+
if size == 4:
|
|
289
|
+
high = self._extension()
|
|
290
|
+
self._commit_pc()
|
|
291
|
+
return (high << 16) | self._extension()
|
|
292
|
+
self._commit_pc()
|
|
293
|
+
return self._extension() & MASK[size]
|
|
294
|
+
|
|
295
|
+
def _immediate_to_ea(self, opcode: int, operation) -> None:
|
|
296
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
297
|
+
source = self._immediate(size)
|
|
298
|
+
self._modify(EA_KIND[opcode & 0x3F], opcode & 7, size, operation, source)
|
|
299
|
+
|
|
300
|
+
def _op_addi(self, opcode: int) -> None:
|
|
301
|
+
"""ADDI -- destination <- destination + #data (PRM 4-9; UM Table 8-5).
|
|
302
|
+
|
|
303
|
+
The immediate's refills come before the operand and the PC is committed
|
|
304
|
+
at the last of them: SST ADD.b/.w/.l (the ADDI cases).
|
|
305
|
+
"""
|
|
306
|
+
self._immediate_to_ea(opcode, self._add)
|
|
307
|
+
|
|
308
|
+
def _op_subi(self, opcode: int) -> None:
|
|
309
|
+
"""SUBI -- destination <- destination - #data (PRM 4-180; UM Table 8-5).
|
|
310
|
+
|
|
311
|
+
Refills, commit point and bus order: SST SUB.b/.w/.l (the SUBI cases).
|
|
312
|
+
"""
|
|
313
|
+
self._immediate_to_ea(opcode, self._sub)
|
|
314
|
+
|
|
315
|
+
def _op_andi(self, opcode: int) -> None:
|
|
316
|
+
"""ANDI -- destination <- destination AND #data (PRM 4-18; UM Table 8-5).
|
|
317
|
+
|
|
318
|
+
Refills, commit point and bus order: SST AND.b/.w/.l (the ANDI cases).
|
|
319
|
+
"""
|
|
320
|
+
self._immediate_to_ea(opcode, self._and)
|
|
321
|
+
|
|
322
|
+
def _op_ori(self, opcode: int) -> None:
|
|
323
|
+
"""ORI -- destination <- destination OR #data (PRM 4-153; UM Table 8-5).
|
|
324
|
+
|
|
325
|
+
Refills, commit point and bus order: SST OR.b/.w/.l (the ORI cases).
|
|
326
|
+
"""
|
|
327
|
+
self._immediate_to_ea(opcode, self._or)
|
|
328
|
+
|
|
329
|
+
def _op_eori(self, opcode: int) -> None:
|
|
330
|
+
"""EORI -- destination <- destination XOR #data (PRM 4-102; UM Table 8-5).
|
|
331
|
+
|
|
332
|
+
Refills, commit point and bus order: SST EOR.b/.w/.l (the EORI cases).
|
|
333
|
+
"""
|
|
334
|
+
self._immediate_to_ea(opcode, self._eor)
|
|
335
|
+
|
|
336
|
+
def _op_cmpi(self, opcode: int) -> None:
|
|
337
|
+
"""CMPI -- set N Z V C from destination - #data (PRM 4-79; UM Table 8-5).
|
|
338
|
+
|
|
339
|
+
Refills, commit point, and the 2 idle clocks of a long compare with Dn:
|
|
340
|
+
SST CMP.b/.w/.l (the CMPI cases).
|
|
341
|
+
"""
|
|
342
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
343
|
+
source = self._immediate(size)
|
|
344
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
345
|
+
destination = self._ea_read(kind, opcode & 7, size)
|
|
346
|
+
self._cmp(destination, source, size)
|
|
347
|
+
self._prefetch()
|
|
348
|
+
if size == 4 and kind == DN:
|
|
349
|
+
self._cycles += 2
|
|
350
|
+
|
|
351
|
+
# -- quick forms (PRM 4-11, 4-182) -------------------------------------------
|
|
352
|
+
|
|
353
|
+
def _quick(self, opcode: int, operation, sign: int) -> None:
|
|
354
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
355
|
+
data = QUICK[(opcode >> 9) & 7]
|
|
356
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
357
|
+
register = opcode & 7
|
|
358
|
+
if kind == AN:
|
|
359
|
+
# An: the whole register, no flags, any size (PRM 4-11).
|
|
360
|
+
self._prefetch()
|
|
361
|
+
self._cycles += 4
|
|
362
|
+
an = 8 + register
|
|
363
|
+
self.R[an] = (self.R[an] + sign * data) & 0xFFFFFFFF
|
|
364
|
+
return
|
|
365
|
+
self._modify(kind, register, size, operation, data)
|
|
366
|
+
|
|
367
|
+
def _op_addq(self, opcode: int) -> None:
|
|
368
|
+
"""ADDQ -- destination <- destination + 1..8 (PRM 4-11; UM Table 8-5).
|
|
369
|
+
|
|
370
|
+
ADDQ.W #,An takes 8 clocks, not the 4 UM Table 8-5 prints: SST ADD.b/.w/.l
|
|
371
|
+
(the ADDQ cases, 118 of them to An) and WinUAE run agree, and 4 clocks is
|
|
372
|
+
outside the tester's tolerance (docs/claims.md, "Strong").
|
|
373
|
+
"""
|
|
374
|
+
self._quick(opcode, self._add, 1)
|
|
375
|
+
|
|
376
|
+
def _op_subq(self, opcode: int) -> None:
|
|
377
|
+
"""SUBQ -- destination <- destination - 1..8 (PRM 4-182; UM Table 8-5).
|
|
378
|
+
|
|
379
|
+
Bus order, the 4 idle clocks of the An form and the PC an address error
|
|
380
|
+
stacks: SST SUB.b/.w/.l (the SUBQ cases).
|
|
381
|
+
"""
|
|
382
|
+
self._quick(opcode, self._sub, -1)
|
|
383
|
+
|
|
384
|
+
# -- multiprecision (PRM 4-14, 4-184, 4-81) ------------------------------------
|
|
385
|
+
|
|
386
|
+
def _extended(self, opcode: int, operation) -> None:
|
|
387
|
+
"""ADDX and SUBX: Dy,Dx or -(Ay),-(Ax) (UM Table 8-11)."""
|
|
388
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
389
|
+
rx = (opcode >> 9) & 7
|
|
390
|
+
ry = opcode & 7
|
|
391
|
+
R = self.R
|
|
392
|
+
if not opcode & 8:
|
|
393
|
+
result = operation(R[rx] & MASK[size], R[ry] & MASK[size], size)
|
|
394
|
+
self._prefetch()
|
|
395
|
+
if size == 4:
|
|
396
|
+
self._cycles += 4
|
|
397
|
+
R[rx] = result
|
|
398
|
+
else:
|
|
399
|
+
self._write_register(rx, size, result)
|
|
400
|
+
return
|
|
401
|
+
self._cycles += 2
|
|
402
|
+
self._commit_pc()
|
|
403
|
+
if size == 4:
|
|
404
|
+
# Low words first, each register stepping once its high word is next.
|
|
405
|
+
source_address = (R[8 + ry] - 4) & 0xFFFFFFFF
|
|
406
|
+
low = self._read_word(source_address + 2)
|
|
407
|
+
R[8 + ry] = source_address
|
|
408
|
+
source = (self._read_word(source_address) << 16) | low
|
|
409
|
+
destination_address = (R[8 + rx] - 4) & 0xFFFFFFFF
|
|
410
|
+
low = self._read_word(destination_address + 2)
|
|
411
|
+
R[8 + rx] = destination_address
|
|
412
|
+
destination = (self._read_word(destination_address) << 16) | low
|
|
413
|
+
result = operation(destination, source, 4)
|
|
414
|
+
self._write_word(destination_address + 2, result)
|
|
415
|
+
self._prefetch()
|
|
416
|
+
self._write_word(destination_address, result >> 16)
|
|
417
|
+
return
|
|
418
|
+
step = self._step_size
|
|
419
|
+
source_address = (R[8 + ry] - step(ry, size)) & 0xFFFFFFFF
|
|
420
|
+
R[8 + ry] = source_address
|
|
421
|
+
source = self._read(size, source_address)
|
|
422
|
+
destination_address = (R[8 + rx] - step(rx, size)) & 0xFFFFFFFF
|
|
423
|
+
R[8 + rx] = destination_address
|
|
424
|
+
destination = self._read(size, destination_address)
|
|
425
|
+
result = operation(destination, source, size)
|
|
426
|
+
self._prefetch()
|
|
427
|
+
self._write(size, destination_address, result)
|
|
428
|
+
|
|
429
|
+
def _op_addx(self, opcode: int) -> None:
|
|
430
|
+
"""ADDX -- destination <- destination + source + X; Z sticky (PRM 4-14; UM Table 8-11).
|
|
431
|
+
|
|
432
|
+
The -(Ay),-(Ax) form's read order (low words first, each register stepping
|
|
433
|
+
once its high word is next), the write between the closing prefetch and
|
|
434
|
+
its high word, and the PC an address error stacks: SST ADDX.b/.w/.l.
|
|
435
|
+
"""
|
|
436
|
+
self._extended(opcode, self._addx)
|
|
437
|
+
|
|
438
|
+
def _op_subx(self, opcode: int) -> None:
|
|
439
|
+
"""SUBX -- destination <- destination - source - X; Z sticky (PRM 4-184; UM Table 8-11).
|
|
440
|
+
|
|
441
|
+
The memory form's read and write order and the PC an address error
|
|
442
|
+
stacks: SST SUBX.b/.w/.l.
|
|
443
|
+
"""
|
|
444
|
+
self._extended(opcode, self._subx)
|
|
445
|
+
|
|
446
|
+
def _op_cmpm(self, opcode: int) -> None:
|
|
447
|
+
"""CMPM -- set N Z V C from (Ax)+ - (Ay)+ (PRM 4-81; UM Table 8-11).
|
|
448
|
+
|
|
449
|
+
Ay steps before each word it reads; Ax only after its operand is in, so
|
|
450
|
+
an address error leaves Ay moved by 2 and Ax alone: SST CMP.b/.w/.l (the
|
|
451
|
+
CMPM cases). For CMPM.L, WinUAE run leaves the faulting Ay 2 less: the
|
|
452
|
+
rule is contested and the core follows the gate (docs/claims.md).
|
|
453
|
+
"""
|
|
454
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
455
|
+
ay = 8 + (opcode & 7)
|
|
456
|
+
ax = 8 + ((opcode >> 9) & 7)
|
|
457
|
+
R = self.R
|
|
458
|
+
self._commit_pc()
|
|
459
|
+
address = R[ay]
|
|
460
|
+
if size == 4:
|
|
461
|
+
R[ay] = (address + 2) & 0xFFFFFFFF
|
|
462
|
+
high = self._read_word(address)
|
|
463
|
+
R[ay] = (address + 4) & 0xFFFFFFFF
|
|
464
|
+
source = (high << 16) | self._read_word(address + 2)
|
|
465
|
+
else:
|
|
466
|
+
R[ay] = (address + self._step_size(opcode & 7, size)) & 0xFFFFFFFF
|
|
467
|
+
source = self._read(size, address)
|
|
468
|
+
address = R[ax]
|
|
469
|
+
destination = self._read(size, address)
|
|
470
|
+
R[ax] = (address + (self._step_size(ax - 8, size) if size != 4 else 4)) & 0xFFFFFFFF
|
|
471
|
+
self._cmp(destination, source, size)
|
|
472
|
+
self._prefetch()
|
|
473
|
+
|
|
474
|
+
# -- single operand (PRM 4-144, 4-146, 4-148, 4-73, 4-192, 4-190) ------------
|
|
475
|
+
|
|
476
|
+
def _unary(self, opcode: int, operation) -> None:
|
|
477
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
478
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
479
|
+
register = opcode & 7
|
|
480
|
+
if kind == DN:
|
|
481
|
+
result = operation(self.R[register] & MASK[size], size)
|
|
482
|
+
self._prefetch()
|
|
483
|
+
if size == 4:
|
|
484
|
+
self._cycles += 2
|
|
485
|
+
self._write_register(register, size, result)
|
|
486
|
+
return
|
|
487
|
+
address, value = self._ea_fetch(kind, register, size)
|
|
488
|
+
result = operation(value, size)
|
|
489
|
+
self._prefetch()
|
|
490
|
+
self._write_result(address, size, result)
|
|
491
|
+
|
|
492
|
+
def _negate(self, value: int, size: int) -> int:
|
|
493
|
+
return self._sub(0, value, size)
|
|
494
|
+
|
|
495
|
+
def _negate_extended(self, value: int, size: int) -> int:
|
|
496
|
+
return self._subx(0, value, size)
|
|
497
|
+
|
|
498
|
+
def _complement(self, value: int, size: int) -> int:
|
|
499
|
+
result = ~value & MASK[size]
|
|
500
|
+
self._flags_logic(result, size)
|
|
501
|
+
return result
|
|
502
|
+
|
|
503
|
+
def _clear(self, value: int, size: int) -> int:
|
|
504
|
+
self.SR = (self.SR & (0xFF00 | X)) | Z
|
|
505
|
+
return 0
|
|
506
|
+
|
|
507
|
+
def _op_neg(self, opcode: int) -> None:
|
|
508
|
+
"""NEG -- destination <- 0 - destination; X N Z V C (PRM 4-144; UM Table 8-6).
|
|
509
|
+
|
|
510
|
+
Bus order (read, refill, write; a long low word first) and the PC an
|
|
511
|
+
address error stacks: SST NEG.b/.w/.l.
|
|
512
|
+
"""
|
|
513
|
+
self._unary(opcode, self._negate)
|
|
514
|
+
|
|
515
|
+
def _op_negx(self, opcode: int) -> None:
|
|
516
|
+
"""NEGX -- destination <- 0 - destination - X; Z sticky (PRM 4-146; UM Table 8-6).
|
|
517
|
+
|
|
518
|
+
Bus order and the PC an address error stacks: SST NEGX.b/.w/.l.
|
|
519
|
+
"""
|
|
520
|
+
self._unary(opcode, self._negate_extended)
|
|
521
|
+
|
|
522
|
+
def _op_not(self, opcode: int) -> None:
|
|
523
|
+
"""NOT -- destination <- ones' complement; N Z, V C cleared (PRM 4-148; UM Table 8-6).
|
|
524
|
+
|
|
525
|
+
Bus order and the PC an address error stacks: SST NOT.b/.w/.l.
|
|
526
|
+
"""
|
|
527
|
+
self._unary(opcode, self._complement)
|
|
528
|
+
|
|
529
|
+
def _op_clr(self, opcode: int) -> None:
|
|
530
|
+
"""CLR -- destination <- 0, the operand read first on the 68000 (PRM 4-73; UM Table 8-6).
|
|
531
|
+
|
|
532
|
+
The read before the write, the bus order and the PC an address error
|
|
533
|
+
stacks: SST CLR.b/.w/.l.
|
|
534
|
+
"""
|
|
535
|
+
self._unary(opcode, self._clear)
|
|
536
|
+
|
|
537
|
+
def _op_tst(self, opcode: int) -> None:
|
|
538
|
+
"""TST -- set N Z from the operand; V C cleared (PRM 4-192; UM Table 8-6).
|
|
539
|
+
|
|
540
|
+
Bus order and the PC an address error stacks: SST TST.b/.w/.l.
|
|
541
|
+
"""
|
|
542
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
543
|
+
value = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, size)
|
|
544
|
+
self._flags_logic(value, size)
|
|
545
|
+
self._prefetch()
|
|
546
|
+
|
|
547
|
+
def _op_tas(self, opcode: int) -> None:
|
|
548
|
+
"""TAS -- test a byte, then set its bit 7, indivisibly (PRM 4-190; UM Table 8-6).
|
|
549
|
+
|
|
550
|
+
The write half goes through the host's ``tas_write`` when it gave
|
|
551
|
+
one: the Genesis bus drops it (docs/undocumented-behavior.md).
|
|
552
|
+
|
|
553
|
+
Read, 2 idle clocks, write, then the prefetch (14 clocks for (An)):
|
|
554
|
+
SST TAS, whose totals WinUAE run confirms on every case it judges, against
|
|
555
|
+
the corpus README's own doubt (docs/validation.md, "TAS").
|
|
556
|
+
"""
|
|
557
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
558
|
+
register = opcode & 7
|
|
559
|
+
if kind == DN:
|
|
560
|
+
value = self.R[register] & 0xFF
|
|
561
|
+
self._flags_logic(value, 1)
|
|
562
|
+
self._prefetch()
|
|
563
|
+
self.R[register] |= 0x80
|
|
564
|
+
return
|
|
565
|
+
address, value = self._ea_fetch(kind, register, 1)
|
|
566
|
+
self._cycles += 2
|
|
567
|
+
self._flags_logic(value, 1)
|
|
568
|
+
self._cycles += 4
|
|
569
|
+
try:
|
|
570
|
+
self._tas_write(address & 0xFFFFFF, value | 0x80)
|
|
571
|
+
except BusError:
|
|
572
|
+
# BERR on the write half is a bus error like any other (UM 6.3.9.1).
|
|
573
|
+
raise self._fault(VECTOR_BUS_ERROR, address, True, False) from None
|
|
574
|
+
self._prefetch()
|
|
575
|
+
|
|
576
|
+
# -- multiply and divide (PRM 4-139, 4-141, 4-93, 4-96; UM Table 8-4) ------
|
|
577
|
+
|
|
578
|
+
def _op_mulu(self, opcode: int) -> None:
|
|
579
|
+
"""MULU -- Dn <- Dn.w * source.w, unsigned (PRM 4-141; UM Table 8-4).
|
|
580
|
+
|
|
581
|
+
The count of 1 bits sets the clocks (38 + 2n, UM Table 8-4's note),
|
|
582
|
+
spent after the closing prefetch: SST MULU.
|
|
583
|
+
"""
|
|
584
|
+
source = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
585
|
+
dn = (opcode >> 9) & 7
|
|
586
|
+
product = (self.R[dn] & 0xFFFF) * source
|
|
587
|
+
self._flags_logic(product, 4)
|
|
588
|
+
self._prefetch()
|
|
589
|
+
self._cycles += multiply_unsigned_clocks(source) - 4
|
|
590
|
+
self.R[dn] = product
|
|
591
|
+
|
|
592
|
+
def _op_muls(self, opcode: int) -> None:
|
|
593
|
+
"""MULS -- Dn <- Dn.w * source.w, signed (PRM 4-139; UM Table 8-4).
|
|
594
|
+
|
|
595
|
+
The count of 01/10 transitions sets the clocks (UM Table 8-4's note),
|
|
596
|
+
spent after the closing prefetch: SST MULS.
|
|
597
|
+
"""
|
|
598
|
+
source = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
599
|
+
dn = (opcode >> 9) & 7
|
|
600
|
+
product = (sign_extend_16(self.R[dn]) * sign_extend_16(source)) & 0xFFFFFFFF
|
|
601
|
+
self._flags_logic(product, 4)
|
|
602
|
+
self._prefetch()
|
|
603
|
+
self._cycles += multiply_signed_clocks(source) - 4
|
|
604
|
+
self.R[dn] = product
|
|
605
|
+
|
|
606
|
+
def _op_divu(self, opcode: int) -> None:
|
|
607
|
+
"""DIVU -- Dn <- Dn / source.w, unsigned; remainder:quotient (PRM 4-96; UM Table 8-4).
|
|
608
|
+
|
|
609
|
+
The data-dependent clocks (``divide_unsigned_clocks``) and the overflow
|
|
610
|
+
flags N=1 V=1 Z=0 C=0, undefined in PRM: SST DIVU on every case, and
|
|
611
|
+
WinUAE run (docs/undocumented-behavior.md). Division by zero is
|
|
612
|
+
``_divide_by_zero``, whose flags no corpus has: WinUAE run pins them.
|
|
613
|
+
"""
|
|
614
|
+
source = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
615
|
+
dn = (opcode >> 9) & 7
|
|
616
|
+
dividend = self.R[dn]
|
|
617
|
+
if source == 0:
|
|
618
|
+
self._divide_by_zero(False, dividend)
|
|
619
|
+
return
|
|
620
|
+
quotient, remainder = divmod(dividend, source)
|
|
621
|
+
self._cycles += divide_unsigned_clocks(dividend, source) - 4
|
|
622
|
+
if quotient > 0xFFFF:
|
|
623
|
+
# Overflow: V set, Dn unchanged; N Z C as the microcode leaves them
|
|
624
|
+
# (docs/undocumented-behavior.md, "Flags after DIVU overflow").
|
|
625
|
+
self.SR = (self.SR & (0xFF00 | X)) | V | N
|
|
626
|
+
else:
|
|
627
|
+
self.R[dn] = (remainder << 16) | quotient
|
|
628
|
+
self._flags_logic(quotient, 2)
|
|
629
|
+
self._prefetch()
|
|
630
|
+
|
|
631
|
+
def _op_divs(self, opcode: int) -> None:
|
|
632
|
+
"""DIVS -- Dn <- Dn / source.w, signed; remainder:quotient (PRM 4-93; UM Table 8-4).
|
|
633
|
+
|
|
634
|
+
The data-dependent clocks (``divide_signed_clocks``, Cwik's analysis) and
|
|
635
|
+
the overflow flags N=1 V=1 Z=0 C=0, undefined in PRM: SST DIVS on every
|
|
636
|
+
case, and WinUAE run. Division by zero is ``_divide_by_zero``, whose
|
|
637
|
+
flags no corpus has: WinUAE run pins them.
|
|
638
|
+
"""
|
|
639
|
+
source = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
640
|
+
dn = (opcode >> 9) & 7
|
|
641
|
+
dividend = self.R[dn] - 0x100000000 if self.R[dn] & 0x80000000 else self.R[dn]
|
|
642
|
+
divisor = sign_extend_16(source)
|
|
643
|
+
if divisor == 0:
|
|
644
|
+
self._divide_by_zero(True, self.R[dn])
|
|
645
|
+
return
|
|
646
|
+
quotient = abs(dividend) // abs(divisor)
|
|
647
|
+
if (dividend < 0) != (divisor < 0):
|
|
648
|
+
quotient = -quotient
|
|
649
|
+
remainder = dividend - quotient * divisor # takes the dividend's sign
|
|
650
|
+
self._cycles += divide_signed_clocks(dividend, divisor) - 4
|
|
651
|
+
if not -0x8000 <= quotient <= 0x7FFF:
|
|
652
|
+
self.SR = (self.SR & (0xFF00 | X)) | V | N
|
|
653
|
+
else:
|
|
654
|
+
self.R[dn] = ((remainder & 0xFFFF) << 16) | (quotient & 0xFFFF)
|
|
655
|
+
self._flags_logic(quotient & 0xFFFF, 2)
|
|
656
|
+
self._prefetch()
|