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/_loads.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"""Data movement: MOVE, MOVEA, MOVEQ, MOVEM, MOVEP, LEA, PEA, LINK, UNLK, EXG, SWAP, EXT.
|
|
2
|
+
|
|
3
|
+
PRM Table 3-2. MOVE's destination has its own sequence, not the source's:
|
|
4
|
+
no extension word is taken from the queue before the source operand is read,
|
|
5
|
+
``-(An)`` costs no internal clocks and is written after the closing prefetch,
|
|
6
|
+
and ``(xxx).L`` writes between its two extension-word refills (UM Tables 8-2
|
|
7
|
+
and 8-3; the order is the corpus's: SST MOVE.b/.w/.l, T3).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from m68000_python._ea import (
|
|
11
|
+
ABSL,
|
|
12
|
+
ABSW,
|
|
13
|
+
AN,
|
|
14
|
+
DISP,
|
|
15
|
+
DN,
|
|
16
|
+
EA_KIND,
|
|
17
|
+
IMM,
|
|
18
|
+
IND,
|
|
19
|
+
INDEX,
|
|
20
|
+
PCDISP,
|
|
21
|
+
PCINDEX,
|
|
22
|
+
POSTINC,
|
|
23
|
+
PREDEC,
|
|
24
|
+
sign_extend_8,
|
|
25
|
+
sign_extend_16,
|
|
26
|
+
word_to_long,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
#: MOVE's size field, bits 13-12 (PRM 4, MOVE): 01 byte, 11 word, 10 long.
|
|
30
|
+
MOVE_SIZE = {1: 1, 3: 2, 2: 4}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LoadsMixin:
|
|
34
|
+
"""Private data-movement implementation: MOVE and its kin, MOVEM, MOVEP, LEA,
|
|
35
|
+
PEA, LINK, UNLK, EXG, SWAP and EXT."""
|
|
36
|
+
|
|
37
|
+
def _op_move(self, opcode: int) -> None:
|
|
38
|
+
"""MOVE -- destination <- source; N Z set, V C cleared (PRM 4-116; UM Tables 8-2, 8-3).
|
|
39
|
+
|
|
40
|
+
The destination sequence (the module docstring), which flags a faulting
|
|
41
|
+
long write has set (``_move_long_to``), when (An)+ and -(An) move, and the
|
|
42
|
+
PC an address error stacks: SST MOVE.b/.w/.l, 60% of whose cases fault.
|
|
43
|
+
WinUAE run agrees except that it leaves An alone when a word (An)+ faults:
|
|
44
|
+
contested, the core follows the gate (docs/claims.md).
|
|
45
|
+
"""
|
|
46
|
+
size = MOVE_SIZE[opcode >> 12]
|
|
47
|
+
source = EA_KIND[opcode & 0x3F]
|
|
48
|
+
value = self._ea_read(source, opcode & 7, size)
|
|
49
|
+
kind = EA_KIND[(opcode >> 3) & 0x38 | (opcode >> 9) & 7]
|
|
50
|
+
register = (opcode >> 9) & 7
|
|
51
|
+
if size == 4:
|
|
52
|
+
self._move_long_to(kind, register, value, source in (DN, AN, IMM))
|
|
53
|
+
return
|
|
54
|
+
R = self.R
|
|
55
|
+
if kind == DN:
|
|
56
|
+
self._flags_logic(value, size)
|
|
57
|
+
self._write_register(register, size, value)
|
|
58
|
+
self._prefetch()
|
|
59
|
+
return
|
|
60
|
+
if kind == IND or kind == POSTINC:
|
|
61
|
+
address = R[8 + register]
|
|
62
|
+
self._flags_logic(value, size)
|
|
63
|
+
self._commit_pc()
|
|
64
|
+
self._write(size, address, value)
|
|
65
|
+
if kind == POSTINC: # after the write: a faulting write leaves An alone
|
|
66
|
+
R[8 + register] = (address + self._step_size(register, size)) & 0xFFFFFFFF
|
|
67
|
+
self._prefetch()
|
|
68
|
+
return
|
|
69
|
+
if kind == PREDEC:
|
|
70
|
+
address = (R[8 + register] - self._step_size(register, size)) & 0xFFFFFFFF
|
|
71
|
+
R[8 + register] = address
|
|
72
|
+
self._flags_logic(value, size)
|
|
73
|
+
self._prefetch()
|
|
74
|
+
self._write(size, address, value)
|
|
75
|
+
return
|
|
76
|
+
if kind == DISP:
|
|
77
|
+
self._commit_pc()
|
|
78
|
+
address = (R[8 + register] + sign_extend_16(self._extension())) & 0xFFFFFFFF
|
|
79
|
+
elif kind == INDEX:
|
|
80
|
+
self._cycles += 2
|
|
81
|
+
self._commit_pc()
|
|
82
|
+
address = self._index(R[8 + register])
|
|
83
|
+
elif kind == ABSW:
|
|
84
|
+
self._commit_pc()
|
|
85
|
+
address = word_to_long(self._extension())
|
|
86
|
+
elif source in (DN, AN, IMM):
|
|
87
|
+
# (xxx).L from a register or immediate: both refills, then the write.
|
|
88
|
+
high = self._extension()
|
|
89
|
+
self._commit_pc()
|
|
90
|
+
address = ((high << 16) | self._extension()) & 0xFFFFFFFF
|
|
91
|
+
else:
|
|
92
|
+
# (xxx).L from memory: the write comes between the two refills.
|
|
93
|
+
self._commit_pc()
|
|
94
|
+
high = self._extension()
|
|
95
|
+
address = ((high << 16) | self.irc) & 0xFFFFFFFF
|
|
96
|
+
self._flags_logic(value, size)
|
|
97
|
+
self._write(size, address, value)
|
|
98
|
+
self._extension()
|
|
99
|
+
self._prefetch()
|
|
100
|
+
return
|
|
101
|
+
self._flags_logic(value, size)
|
|
102
|
+
self._write(size, address, value)
|
|
103
|
+
self._prefetch()
|
|
104
|
+
|
|
105
|
+
def _move_long_to(self, kind: int, register: int, value: int, from_register: bool) -> None:
|
|
106
|
+
"""The destination half of MOVE.L.
|
|
107
|
+
|
|
108
|
+
The long is written as two words and its flags are set as two
|
|
109
|
+
halves, low word then high word (_flags.py), interleaved with the
|
|
110
|
+
writes in an order that depends on where the value came from: the
|
|
111
|
+
stacked SR of an address error on the first write shows it (SST MOVE.l).
|
|
112
|
+
"""
|
|
113
|
+
R = self.R
|
|
114
|
+
high, low = value >> 16, value & 0xFFFF
|
|
115
|
+
if kind == DN:
|
|
116
|
+
self._flags_logic(value, 4)
|
|
117
|
+
R[register] = value
|
|
118
|
+
self._prefetch()
|
|
119
|
+
return
|
|
120
|
+
if kind == IND or kind == POSTINC:
|
|
121
|
+
address = R[8 + register]
|
|
122
|
+
self._commit_pc()
|
|
123
|
+
if from_register:
|
|
124
|
+
self._write_word(address, high) # flags not yet touched
|
|
125
|
+
self._flags_low_word(value)
|
|
126
|
+
else:
|
|
127
|
+
self._flags_low_word(value)
|
|
128
|
+
self._write_word(address, high)
|
|
129
|
+
self._write_word(address + 2, low)
|
|
130
|
+
if kind == POSTINC:
|
|
131
|
+
R[8 + register] = (address + 4) & 0xFFFFFFFF
|
|
132
|
+
self._flags_high_word(value)
|
|
133
|
+
self._prefetch()
|
|
134
|
+
return
|
|
135
|
+
if kind == PREDEC:
|
|
136
|
+
address = (R[8 + register] - 4) & 0xFFFFFFFF
|
|
137
|
+
self._flags_logic(value, 4)
|
|
138
|
+
self._prefetch_before_write()
|
|
139
|
+
self._write_word(address + 2, low) # low word first, An not yet moved
|
|
140
|
+
self._opcode = self.ir
|
|
141
|
+
R[8 + register] = address
|
|
142
|
+
self._write_word(address, high)
|
|
143
|
+
return
|
|
144
|
+
if kind == DISP or kind == INDEX:
|
|
145
|
+
if kind == INDEX:
|
|
146
|
+
self._cycles += 2
|
|
147
|
+
self._commit_pc()
|
|
148
|
+
if kind == DISP:
|
|
149
|
+
address = (R[8 + register] + sign_extend_16(self._extension())) & 0xFFFFFFFF
|
|
150
|
+
else:
|
|
151
|
+
address = self._index(R[8 + register])
|
|
152
|
+
if from_register:
|
|
153
|
+
self._flags_high_word(value) # the high word's half first
|
|
154
|
+
self._write_word(address, high)
|
|
155
|
+
self._flags_low_word(value)
|
|
156
|
+
else:
|
|
157
|
+
self._flags_low_word(value)
|
|
158
|
+
self._flags_high_word(value)
|
|
159
|
+
self._write_word(address, high)
|
|
160
|
+
self._write_word(address + 2, low)
|
|
161
|
+
self._flags_high_word(value)
|
|
162
|
+
self._prefetch()
|
|
163
|
+
return
|
|
164
|
+
if kind == ABSW:
|
|
165
|
+
self._commit_pc()
|
|
166
|
+
address = word_to_long(self._extension())
|
|
167
|
+
elif from_register:
|
|
168
|
+
high_address = self._extension()
|
|
169
|
+
self._commit_pc()
|
|
170
|
+
address = ((high_address << 16) | self._extension()) & 0xFFFFFFFF
|
|
171
|
+
else:
|
|
172
|
+
# (xxx).L from memory: the writes come between the two refills.
|
|
173
|
+
self._commit_pc()
|
|
174
|
+
high_address = self._extension()
|
|
175
|
+
address = ((high_address << 16) | self.irc) & 0xFFFFFFFF
|
|
176
|
+
self._flags_low_word(value)
|
|
177
|
+
self._write_word(address, high)
|
|
178
|
+
self._flags_high_word(value)
|
|
179
|
+
self._write_word(address + 2, low)
|
|
180
|
+
self._extension()
|
|
181
|
+
self._prefetch()
|
|
182
|
+
return
|
|
183
|
+
self._flags_logic(value, 4)
|
|
184
|
+
self._write_word(address, high)
|
|
185
|
+
self._write_word(address + 2, low)
|
|
186
|
+
self._prefetch()
|
|
187
|
+
|
|
188
|
+
def _op_movea(self, opcode: int) -> None:
|
|
189
|
+
"""MOVEA -- An <- source, a word sign-extended; no flags (PRM 4-119; UM Tables 8-2, 8-3).
|
|
190
|
+
|
|
191
|
+
Source bus order and the PC an address error stacks: SST MOVEA.w/.l.
|
|
192
|
+
"""
|
|
193
|
+
size = 2 if opcode >> 12 == 3 else 4
|
|
194
|
+
value = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, size)
|
|
195
|
+
if size == 2:
|
|
196
|
+
value = word_to_long(value)
|
|
197
|
+
self.R[8 + ((opcode >> 9) & 7)] = value
|
|
198
|
+
self._prefetch()
|
|
199
|
+
|
|
200
|
+
def _op_moveq(self, opcode: int) -> None:
|
|
201
|
+
"""MOVEQ -- Dn <- sign-extended byte; N Z, V C cleared (PRM 4-134; UM Table 8-5)."""
|
|
202
|
+
value = sign_extend_8(opcode) & 0xFFFFFFFF
|
|
203
|
+
self._flags_logic(value, 4)
|
|
204
|
+
self.R[(opcode >> 9) & 7] = value
|
|
205
|
+
self._prefetch()
|
|
206
|
+
|
|
207
|
+
# -- MOVEM (PRM 4-128; UM Table 8-10) ---------------------------------------
|
|
208
|
+
|
|
209
|
+
def _op_movem(self, opcode: int) -> None:
|
|
210
|
+
"""MOVEM -- move a list of registers to or from memory (PRM 4-128; UM Table 8-10).
|
|
211
|
+
|
|
212
|
+
The mask word follows the opcode; bit 0 is D0 and bit 15 A7, except
|
|
213
|
+
for -(An), where the order is reversed so that the registers still
|
|
214
|
+
land in memory D0 lowest (PRM 4, MOVEM). Memory-to-register reads
|
|
215
|
+
one word past the last register (the extra read of UM Table 8-10's
|
|
216
|
+
``3+n``); word loads are sign-extended into the whole register. With
|
|
217
|
+
-(An) the 68000 stores An's initial value if An is in the list; with
|
|
218
|
+
(An)+ the loaded value is replaced by the final address (PRM 4, MOVEM).
|
|
219
|
+
|
|
220
|
+
The extra read, the -(An) form's low-word-first longs, and the PC an
|
|
221
|
+
address error stacks: SST MOVEM.w/.l. For (d8,An,Xn) and (d8,PC,Xn) at
|
|
222
|
+
an odd address WinUAE run stacks 4 less: contested, the core follows the
|
|
223
|
+
gate (docs/claims.md).
|
|
224
|
+
"""
|
|
225
|
+
size = 4 if opcode & 0x40 else 2
|
|
226
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
227
|
+
register = opcode & 7
|
|
228
|
+
mask = self._extension()
|
|
229
|
+
R = self.R
|
|
230
|
+
if opcode & 0x0400:
|
|
231
|
+
program = kind in (PCDISP, PCINDEX)
|
|
232
|
+
address = R[8 + register] if kind == POSTINC else self._ea_address(kind, register, 2)
|
|
233
|
+
self._commit_pc()
|
|
234
|
+
read = self._read_program_word if program else self._read_word
|
|
235
|
+
for index in range(16):
|
|
236
|
+
if mask & (1 << index):
|
|
237
|
+
if size == 4:
|
|
238
|
+
high = read(address)
|
|
239
|
+
R[index] = (high << 16) | read(address + 2)
|
|
240
|
+
else:
|
|
241
|
+
R[index] = word_to_long(read(address))
|
|
242
|
+
address = (address + size) & 0xFFFFFFFF
|
|
243
|
+
read(address) # one word past the last register
|
|
244
|
+
if kind == POSTINC:
|
|
245
|
+
R[8 + register] = address
|
|
246
|
+
self._prefetch()
|
|
247
|
+
return
|
|
248
|
+
if kind == PREDEC:
|
|
249
|
+
address = R[8 + register]
|
|
250
|
+
self._commit_pc()
|
|
251
|
+
for bit in range(16):
|
|
252
|
+
if mask & (1 << bit):
|
|
253
|
+
value = R[15 - bit]
|
|
254
|
+
address = (address - size) & 0xFFFFFFFF
|
|
255
|
+
if size == 4:
|
|
256
|
+
self._write_long_low_first(address, value)
|
|
257
|
+
else:
|
|
258
|
+
self._write_word(address, value)
|
|
259
|
+
R[8 + register] = address
|
|
260
|
+
self._prefetch()
|
|
261
|
+
return
|
|
262
|
+
address = self._ea_address(kind, register, 2)
|
|
263
|
+
self._commit_pc()
|
|
264
|
+
for index in range(16):
|
|
265
|
+
if mask & (1 << index):
|
|
266
|
+
if size == 4:
|
|
267
|
+
self._write_long(address, R[index])
|
|
268
|
+
else:
|
|
269
|
+
self._write_word(address, R[index])
|
|
270
|
+
address = (address + size) & 0xFFFFFFFF
|
|
271
|
+
self._prefetch()
|
|
272
|
+
|
|
273
|
+
# -- MOVEP (PRM 4-133; UM Table 8-13) --------------------------------------
|
|
274
|
+
|
|
275
|
+
def _op_movep(self, opcode: int) -> None:
|
|
276
|
+
"""MOVEP -- move Dn to or from alternate bytes at (d16,Ay) (PRM 4-133; UM Table 8-13).
|
|
277
|
+
|
|
278
|
+
Byte accesses, high-order byte first, so never an address error at an
|
|
279
|
+
odd address (docs/undocumented-behavior.md, "Odd addresses"); the order
|
|
280
|
+
is SST MOVEP.w/.l's.
|
|
281
|
+
"""
|
|
282
|
+
dn = (opcode >> 9) & 7
|
|
283
|
+
address = (self.R[8 + (opcode & 7)] + sign_extend_16(self._extension())) & 0xFFFFFFFF
|
|
284
|
+
count = 4 if opcode & 0x40 else 2
|
|
285
|
+
if opcode & 0x80:
|
|
286
|
+
value = self.R[dn]
|
|
287
|
+
for shift in range(8 * (count - 1), -1, -8):
|
|
288
|
+
self._write_byte(address, value >> shift)
|
|
289
|
+
address += 2
|
|
290
|
+
else:
|
|
291
|
+
value = 0
|
|
292
|
+
for _ in range(count):
|
|
293
|
+
value = (value << 8) | self._read_byte(address)
|
|
294
|
+
address += 2
|
|
295
|
+
self._write_register(dn, count, value)
|
|
296
|
+
self._prefetch()
|
|
297
|
+
|
|
298
|
+
# -- effective addresses as values (PRM 4-110, 4-159) ----------------------
|
|
299
|
+
|
|
300
|
+
def _op_lea(self, opcode: int) -> None:
|
|
301
|
+
"""LEA -- An <- effective address (PRM 4-110; UM Table 8-10).
|
|
302
|
+
|
|
303
|
+
The extension words' refills and the PC they commit: SST LEA.
|
|
304
|
+
"""
|
|
305
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
306
|
+
address = self._control_ea(kind, opcode & 7)
|
|
307
|
+
self.R[8 + ((opcode >> 9) & 7)] = address
|
|
308
|
+
if kind in (INDEX, PCINDEX):
|
|
309
|
+
self._cycles += 2
|
|
310
|
+
self._prefetch()
|
|
311
|
+
|
|
312
|
+
def _control_ea(self, kind: int, register: int) -> int:
|
|
313
|
+
"""A control-mode address, taking the extension words from the queue."""
|
|
314
|
+
if kind == IND:
|
|
315
|
+
return self.R[8 + register]
|
|
316
|
+
return self._ea_address(kind, register, 4)
|
|
317
|
+
|
|
318
|
+
def _op_pea(self, opcode: int) -> None:
|
|
319
|
+
"""PEA -- push the effective address (PRM 4-159; UM Table 8-10).
|
|
320
|
+
|
|
321
|
+
Absolute forms push before the closing prefetch, the others after it
|
|
322
|
+
with the decoder still holding PEA; the PC an address error stacks:
|
|
323
|
+
SST PEA.
|
|
324
|
+
"""
|
|
325
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
326
|
+
address = self._control_ea(kind, opcode & 7)
|
|
327
|
+
if kind in (INDEX, PCINDEX):
|
|
328
|
+
self._cycles += 2
|
|
329
|
+
if kind in (ABSW, ABSL):
|
|
330
|
+
self._push_long(address)
|
|
331
|
+
self._prefetch()
|
|
332
|
+
return
|
|
333
|
+
self._prefetch_before_write()
|
|
334
|
+
self._push_long(address)
|
|
335
|
+
self._opcode = self.ir
|
|
336
|
+
|
|
337
|
+
# -- stack frames (PRM 4-111, 4-194) -----------------------------------------
|
|
338
|
+
|
|
339
|
+
def _op_link(self, opcode: int) -> None:
|
|
340
|
+
"""LINK -- push An, An <- SP, SP <- SP + d16 (PRM 4-111; UM Table 8-12: 16 clocks).
|
|
341
|
+
|
|
342
|
+
LINK A7 pushes A7's value from before the push: SST LINK, and WinUAE
|
|
343
|
+
run (its 4.3.0 fix against hardware; docs/claims.md, "Strong").
|
|
344
|
+
"""
|
|
345
|
+
an = 8 + (opcode & 7)
|
|
346
|
+
displacement = sign_extend_16(self._extension())
|
|
347
|
+
value = self.R[an] # LINK A7 pushes A7 as it was before the push
|
|
348
|
+
sp = (self.R[15] - 4) & 0xFFFFFFFF
|
|
349
|
+
self.R[an] = sp
|
|
350
|
+
self._write_long(sp, value)
|
|
351
|
+
self.R[15] = (sp + displacement) & 0xFFFFFFFF
|
|
352
|
+
self._prefetch()
|
|
353
|
+
|
|
354
|
+
def _op_unlk(self, opcode: int) -> None:
|
|
355
|
+
"""UNLK -- SP <- An, An <- (SP)+ (PRM 4-194; UM Table 8-12: 12 clocks).
|
|
356
|
+
|
|
357
|
+
SP moves only once both words are in, so a fault leaves it: SST UNLINK.
|
|
358
|
+
"""
|
|
359
|
+
an = 8 + (opcode & 7)
|
|
360
|
+
address = self.R[an]
|
|
361
|
+
self._commit_pc()
|
|
362
|
+
value = self._read_long(address) # SP moves only once both words are in
|
|
363
|
+
self.R[15] = (address + 4) & 0xFFFFFFFF
|
|
364
|
+
self.R[an] = value
|
|
365
|
+
self._prefetch()
|
|
366
|
+
|
|
367
|
+
# -- register shuffles (PRM 4-105, 4-187, 4-106) ------------------------------
|
|
368
|
+
|
|
369
|
+
def _op_exg(self, opcode: int) -> None:
|
|
370
|
+
"""EXG -- exchange two registers (PRM 4-105; UM Table 8-12: 6 clocks)."""
|
|
371
|
+
mode = (opcode >> 3) & 0x1F
|
|
372
|
+
rx = (opcode >> 9) & 7
|
|
373
|
+
ry = opcode & 7
|
|
374
|
+
if mode == 0x08:
|
|
375
|
+
x, y = rx, ry
|
|
376
|
+
elif mode == 0x09:
|
|
377
|
+
x, y = 8 + rx, 8 + ry
|
|
378
|
+
else:
|
|
379
|
+
x, y = rx, 8 + ry
|
|
380
|
+
R = self.R
|
|
381
|
+
R[x], R[y] = R[y], R[x]
|
|
382
|
+
self._prefetch()
|
|
383
|
+
self._cycles += 2
|
|
384
|
+
|
|
385
|
+
def _op_swap(self, opcode: int) -> None:
|
|
386
|
+
"""SWAP -- swap the halves of Dn; N Z of the long, V C clear (PRM 4-187; UM Table 8-12)."""
|
|
387
|
+
register = opcode & 7
|
|
388
|
+
value = self.R[register]
|
|
389
|
+
value = ((value << 16) | (value >> 16)) & 0xFFFFFFFF
|
|
390
|
+
self._flags_logic(value, 4)
|
|
391
|
+
self.R[register] = value
|
|
392
|
+
self._prefetch()
|
|
393
|
+
|
|
394
|
+
def _op_ext(self, opcode: int) -> None:
|
|
395
|
+
"""EXT -- sign-extend Dn's byte (EXT.W) or word (EXT.L) (PRM 4-106; UM Table 8-12)."""
|
|
396
|
+
register = opcode & 7
|
|
397
|
+
if opcode & 0x40:
|
|
398
|
+
value = word_to_long(self.R[register])
|
|
399
|
+
self._flags_logic(value, 4)
|
|
400
|
+
self.R[register] = value
|
|
401
|
+
else:
|
|
402
|
+
value = sign_extend_8(self.R[register]) & 0xFFFF
|
|
403
|
+
self._flags_logic(value, 2)
|
|
404
|
+
self._write_register(register, 2, value)
|
|
405
|
+
self._prefetch()
|
m68000_python/_shifts.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Shifts and rotates: ASL/ASR, LSL/LSR, ROXL/ROXR, ROL/ROR (PRM Table 3-5).
|
|
2
|
+
|
|
3
|
+
Register forms shift Dn by 1-8 (an immediate count, 0 meaning 8) or by Dx
|
|
4
|
+
mod 64, at any size, in 6 + 2n clocks for a byte or word and 8 + 2n for a
|
|
5
|
+
long (UM Table 8-7): the queue is refilled first, then the shifter runs.
|
|
6
|
+
Memory forms shift a word by one: read, refill, write.
|
|
7
|
+
|
|
8
|
+
Flags (PRM Table 3-18): N and Z from the result; C is the last bit shifted
|
|
9
|
+
out; X takes C for the shifts and ROXL/ROXR and is untouched by ROL/ROR. V
|
|
10
|
+
is cleared except by ASL, which sets it if the sign bit changed at any point
|
|
11
|
+
of the shift. A count of 0 shifts nothing and clears C, except ROXL/ROXR,
|
|
12
|
+
which copy X into C (docs/undocumented-behavior.md, "Shift counts").
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from m68000_python._core import MASK, MSB, C, N, V, X, Z
|
|
16
|
+
from m68000_python._ea import EA_KIND
|
|
17
|
+
|
|
18
|
+
SIZE = (1, 2, 4, 0)
|
|
19
|
+
QUICK = (8, 1, 2, 3, 4, 5, 6, 7)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _nz(result: int, size: int) -> int:
|
|
23
|
+
flags = 0
|
|
24
|
+
if result & MSB[size]:
|
|
25
|
+
flags |= N
|
|
26
|
+
if not result & MASK[size]:
|
|
27
|
+
flags |= Z
|
|
28
|
+
return flags
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def shift(kind: str, left: bool, value: int, count: int, size: int, x: int) -> tuple[int, int]:
|
|
32
|
+
"""Shift ``value`` (``size`` bytes) ``count`` places; return (result, X N Z V C bits).
|
|
33
|
+
|
|
34
|
+
The returned flags include X only where the operation defines it; the
|
|
35
|
+
caller keeps the old X for ROL/ROR and for a count of 0.
|
|
36
|
+
"""
|
|
37
|
+
bits = 8 * size
|
|
38
|
+
mask = MASK[size]
|
|
39
|
+
msb = MSB[size]
|
|
40
|
+
if count == 0:
|
|
41
|
+
carry = C if (kind == "rox" and x) else 0
|
|
42
|
+
return value, _nz(value, size) | carry | (X if kind == "rox" and x else 0)
|
|
43
|
+
if kind == "as" and left:
|
|
44
|
+
result = value
|
|
45
|
+
overflow = False
|
|
46
|
+
carry = 0
|
|
47
|
+
for _ in range(count):
|
|
48
|
+
carry = 1 if result & msb else 0
|
|
49
|
+
shifted = (result << 1) & mask
|
|
50
|
+
if (shifted ^ result) & msb:
|
|
51
|
+
overflow = True # the sign bit changed at some point (PRM 4-22)
|
|
52
|
+
result = shifted
|
|
53
|
+
flags = _nz(result, size) | (V if overflow else 0)
|
|
54
|
+
return result, flags | ((X | C) if carry else 0)
|
|
55
|
+
if kind == "as":
|
|
56
|
+
signed = value - (1 << bits) if value & msb else value
|
|
57
|
+
if count >= bits:
|
|
58
|
+
result = mask if signed < 0 else 0
|
|
59
|
+
carry = 1 if signed < 0 else 0
|
|
60
|
+
else:
|
|
61
|
+
carry = (signed >> (count - 1)) & 1
|
|
62
|
+
result = (signed >> count) & mask
|
|
63
|
+
return result, _nz(result, size) | ((X | C) if carry else 0)
|
|
64
|
+
if kind == "ls":
|
|
65
|
+
if left:
|
|
66
|
+
carry = (value >> (bits - count)) & 1 if count <= bits else 0
|
|
67
|
+
result = (value << count) & mask
|
|
68
|
+
else:
|
|
69
|
+
carry = (value >> (count - 1)) & 1 if count <= bits else 0
|
|
70
|
+
result = value >> count if count < bits else 0
|
|
71
|
+
return result, _nz(result, size) | ((X | C) if carry else 0)
|
|
72
|
+
if kind == "ro":
|
|
73
|
+
steps = count % bits
|
|
74
|
+
if left:
|
|
75
|
+
result = ((value << steps) | (value >> (bits - steps))) & mask
|
|
76
|
+
carry = result & 1
|
|
77
|
+
else:
|
|
78
|
+
result = ((value >> steps) | (value << (bits - steps))) & mask
|
|
79
|
+
carry = 1 if result & msb else 0
|
|
80
|
+
return result, _nz(result, size) | (C if carry else 0)
|
|
81
|
+
# rox: rotate through X, a (bits + 1)-bit rotation
|
|
82
|
+
steps = count % (bits + 1)
|
|
83
|
+
wide = (x << bits) | value
|
|
84
|
+
if left:
|
|
85
|
+
wide = ((wide << steps) | (wide >> (bits + 1 - steps))) & ((mask << 1) | 1)
|
|
86
|
+
else:
|
|
87
|
+
wide = ((wide >> steps) | (wide << (bits + 1 - steps))) & ((mask << 1) | 1)
|
|
88
|
+
extend = (wide >> bits) & 1
|
|
89
|
+
result = wide & mask
|
|
90
|
+
return result, _nz(result, size) | ((X | C) if extend else 0)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ShiftsMixin:
|
|
94
|
+
"""Private shift and rotate implementation."""
|
|
95
|
+
|
|
96
|
+
def _shift_register(self, opcode: int, kind: str) -> None:
|
|
97
|
+
size = SIZE[(opcode >> 6) & 3]
|
|
98
|
+
if opcode & 0x20:
|
|
99
|
+
count = self.R[(opcode >> 9) & 7] & 63
|
|
100
|
+
else:
|
|
101
|
+
count = QUICK[(opcode >> 9) & 7]
|
|
102
|
+
register = opcode & 7
|
|
103
|
+
x = (self.SR >> 4) & 1
|
|
104
|
+
result, flags = shift(
|
|
105
|
+
kind, bool(opcode & 0x0100), self.R[register] & MASK[size], count, size, x
|
|
106
|
+
)
|
|
107
|
+
self._set_shift_flags(kind, flags, count)
|
|
108
|
+
self._prefetch()
|
|
109
|
+
self._cycles += (4 if size == 4 else 2) + 2 * count
|
|
110
|
+
self._write_register(register, size, result)
|
|
111
|
+
|
|
112
|
+
def _set_shift_flags(self, kind: str, flags: int, count: int) -> None:
|
|
113
|
+
keep = 0xFF00
|
|
114
|
+
if kind == "ro" or (count == 0 and kind != "rox"):
|
|
115
|
+
keep |= X # ROL/ROR never touch X; a zero count leaves X alone
|
|
116
|
+
self.SR = (self.SR & keep) | (flags & ~keep & 0xFF)
|
|
117
|
+
|
|
118
|
+
def _shift_memory(self, opcode: int, kind: str) -> None:
|
|
119
|
+
address, value = self._ea_fetch(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
120
|
+
x = (self.SR >> 4) & 1
|
|
121
|
+
result, flags = shift(kind, bool(opcode & 0x0100), value, 1, 2, x)
|
|
122
|
+
self._set_shift_flags(kind, flags, 1)
|
|
123
|
+
self._prefetch()
|
|
124
|
+
self._write_word(address, result)
|
|
125
|
+
|
|
126
|
+
def _op_asd(self, opcode: int) -> None:
|
|
127
|
+
"""ASd -- arithmetic shift Dn left or right (PRM 4-22; UM Table 8-7)."""
|
|
128
|
+
self._shift_register(opcode, "as")
|
|
129
|
+
|
|
130
|
+
def _op_lsd(self, opcode: int) -> None:
|
|
131
|
+
"""LSd -- logical shift Dn left or right (PRM 4-113; UM Table 8-7)."""
|
|
132
|
+
self._shift_register(opcode, "ls")
|
|
133
|
+
|
|
134
|
+
def _op_roxd(self, opcode: int) -> None:
|
|
135
|
+
"""ROXd -- rotate Dn through X, left or right (PRM 4-163; UM Table 8-7)."""
|
|
136
|
+
self._shift_register(opcode, "rox")
|
|
137
|
+
|
|
138
|
+
def _op_rod(self, opcode: int) -> None:
|
|
139
|
+
"""ROd -- rotate Dn left or right, X untouched (PRM 4-160; UM Table 8-7)."""
|
|
140
|
+
self._shift_register(opcode, "ro")
|
|
141
|
+
|
|
142
|
+
def _op_asd_memory(self, opcode: int) -> None:
|
|
143
|
+
"""ASd -- arithmetic shift a memory word by one (PRM 4-22; UM Table 8-7).
|
|
144
|
+
|
|
145
|
+
Read, refill, write, and the PC an address error stacks: SST ASL.w, ASR.w.
|
|
146
|
+
"""
|
|
147
|
+
self._shift_memory(opcode, "as")
|
|
148
|
+
|
|
149
|
+
def _op_lsd_memory(self, opcode: int) -> None:
|
|
150
|
+
"""LSd -- logical shift a memory word by one (PRM 4-113; UM Table 8-7).
|
|
151
|
+
|
|
152
|
+
Read, refill, write, and the PC an address error stacks: SST LSL.w, LSR.w.
|
|
153
|
+
"""
|
|
154
|
+
self._shift_memory(opcode, "ls")
|
|
155
|
+
|
|
156
|
+
def _op_roxd_memory(self, opcode: int) -> None:
|
|
157
|
+
"""ROXd -- rotate a memory word through X by one (PRM 4-163; UM Table 8-7).
|
|
158
|
+
|
|
159
|
+
Read, refill, write, and the PC an address error stacks: SST ROXL.w, ROXR.w.
|
|
160
|
+
"""
|
|
161
|
+
self._shift_memory(opcode, "rox")
|
|
162
|
+
|
|
163
|
+
def _op_rod_memory(self, opcode: int) -> None:
|
|
164
|
+
"""ROd -- rotate a memory word by one (PRM 4-160; UM Table 8-7).
|
|
165
|
+
|
|
166
|
+
Read, refill, write, and the PC an address error stacks: SST ROL.w, ROR.w.
|
|
167
|
+
"""
|
|
168
|
+
self._shift_memory(opcode, "ro")
|