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