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/_core.py ADDED
@@ -0,0 +1,545 @@
1
+ """Processor state, the bus, the prefetch queue, and exception entry.
2
+
3
+ Everything a handler touches goes through the helpers here: sized register
4
+ access, the host bus with its odd-address check, the two-word prefetch queue,
5
+ and the group 0/1/2 exception sequences. Sources are cited as in
6
+ docs/start-here.md: PRM is the M68000 Family Programmer's Reference Manual,
7
+ UM the M68000 User's Manual. Where the manuals are silent, the order of bus
8
+ accesses, the internal ("idle") clocks, and the value an address error
9
+ stacks as PC follow the pinned SingleStepTests/m68000 corpus, which records
10
+ them as MAME 0.285's microcode-transcribed core produces them (tier T3; see
11
+ docs/undocumented-behavior.md).
12
+
13
+ Time is counted in clocks as the core goes: every bus access adds 4 and every
14
+ internal step adds the clocks the microcode spends there, so an instruction
15
+ that faults halfway has spent exactly the clocks up to the fault.
16
+ """
17
+
18
+ from collections.abc import Callable
19
+
20
+ # -- status register (PRM 1.3.1) ----------------------------------------------
21
+ T = 0x8000 # trace
22
+ S = 0x2000 # supervisor
23
+ IPL_MASK = 0x0700 # interrupt priority mask, I2-I0
24
+ X = 0x10 # extend
25
+ N = 0x08 # negative
26
+ Z = 0x04 # zero
27
+ V = 0x02 # overflow
28
+ C = 0x01 # carry
29
+ CCR_BITS = 0x1F
30
+ #: Bits 14, 12, 11, 7, 6 and 5 read as zero on the 68000 (PRM 1.3.1).
31
+ SR_BITS = 0xA71F
32
+
33
+ MASK24 = 0xFFFFFF # the 68000 drives A23-A1 (UM 3.1)
34
+
35
+ # Function codes FC2-FC0 (UM Table 3-2).
36
+ FC_USER_DATA = 1
37
+ FC_USER_PROGRAM = 2
38
+ FC_SUPERVISOR_DATA = 5
39
+ FC_SUPERVISOR_PROGRAM = 6
40
+
41
+ # Exception vectors (UM Table 6-2).
42
+ VECTOR_BUS_ERROR = 2
43
+ VECTOR_ADDRESS_ERROR = 3
44
+ VECTOR_ILLEGAL = 4
45
+ VECTOR_ZERO_DIVIDE = 5
46
+ VECTOR_CHK = 6
47
+ VECTOR_TRAPV = 7
48
+ VECTOR_PRIVILEGE = 8
49
+ VECTOR_TRACE = 9
50
+ VECTOR_LINE_A = 10
51
+ VECTOR_LINE_F = 11
52
+ VECTOR_UNINITIALIZED = 15
53
+ VECTOR_SPURIOUS = 24
54
+ VECTOR_AUTOVECTOR_BASE = 24 # level n autovectors through vector 24 + n
55
+ VECTOR_TRAP_BASE = 32
56
+
57
+ #: ``acknowledge(level)`` answers: a vector number 0-255, or one of these.
58
+ AUTOVECTOR = -1
59
+ SPURIOUS = -2
60
+
61
+ #: Operand sizes in bytes (1, 2, 4) index their masks and sign bits: tuples,
62
+ #: because a tuple index is cheaper than a dict lookup on every instruction
63
+ #: (rung A of the speed ladder, docs/validation.md "Speed").
64
+ MASK = (0, 0xFF, 0xFFFF, 0, 0xFFFFFFFF)
65
+ MSB = (0, 0x80, 0x8000, 0, 0x80000000)
66
+
67
+ ReadFunction = Callable[..., int]
68
+ WriteFunction = Callable[..., None]
69
+ Acknowledge = Callable[[int], int]
70
+
71
+
72
+ class BusError(Exception):
73
+ """Raise from a host read or write callable to assert BERR on that access.
74
+
75
+ The core catches it at the access, aborts the instruction, and takes the
76
+ bus-error exception (vector 2) with the seven-word group 0 frame
77
+ (UM 6.3.9.1). Most hosts never raise it.
78
+ """
79
+
80
+
81
+ class GroupZero(Exception):
82
+ """Internal: an address or bus error aborting the current instruction."""
83
+
84
+ def __init__(self, vector: int, address: int, write: bool, program: bool) -> None:
85
+ super().__init__(vector, address, write, program)
86
+ self.vector = vector
87
+ self.address = address # the full 32-bit value the microcode computed
88
+ self.write = write
89
+ self.program = program
90
+
91
+
92
+ class CoreMixin:
93
+ """Private implementation of the state and helpers every handler uses."""
94
+
95
+ # -- construction -------------------------------------------------------
96
+
97
+ def _init_core(
98
+ self,
99
+ read_byte: ReadFunction,
100
+ read_word: ReadFunction,
101
+ write_byte: WriteFunction,
102
+ write_word: WriteFunction,
103
+ acknowledge: Acknowledge | None,
104
+ function_codes: bool,
105
+ tas_write: WriteFunction | None,
106
+ address_error: Callable[[int, bool, int], None] | None,
107
+ reset_devices: Callable[[], None] | None,
108
+ ) -> None:
109
+ # R[0..7] are D0-D7, R[8..15] are A0-A7; A7 is the active stack
110
+ # pointer and the inactive one waits in _other_sp (PRM 1.3).
111
+ self.R = [0] * 16
112
+ self._other_sp = 0
113
+ # Undefined at reset on silicon apart from S, T and the mask (UM 6.3.1).
114
+ self.SR = S | IPL_MASK
115
+ # The prefetch queue (UM "Prefetch", docs/start-here.md): ir is the
116
+ # opcode word of the next instruction, irc the word after it, and
117
+ # _pc the address the next queue refill reads, which is the
118
+ # instruction's address + 4 at a boundary (the corpus's ``pc``).
119
+ self.ir = 0
120
+ self.irc = 0
121
+ self._pc = 0
122
+ # The microcode's PC register: an address error stacks it. It trails
123
+ # _pc and is brought up to it only at the steps where the microcode
124
+ # copies the address unit into PC (see _commit_pc).
125
+ self._fault_pc = 0
126
+ self._opcode = 0 # the instruction being executed (IRD)
127
+ self._cycles = 0 # clocks of the step in progress
128
+ #: Clocks run since construction: the sum of every step() and reset().
129
+ #: The host may set it; only the E-clock phase of an autovectored
130
+ #: interrupt acknowledge depends on it.
131
+ self.clock = 0
132
+ # Interrupts, trace and the stopped/halted states (UM 6.3).
133
+ self.ipl = 0 # the level on IPL2-IPL0 as the host last set it
134
+ self._nmi_edge = False # a 0-to-7 transition not yet taken
135
+ self._trace_pending = False
136
+ # Set by an instruction that is never executed (illegal, line A/F,
137
+ # privilege violation): no trace follows it (UM 6.3.8).
138
+ self._untraced = False
139
+ self._processing_exception = False
140
+ self.stopped = False
141
+ self.halted = False
142
+ #: E-clock phase (clock mod 10) at the last autovectored acknowledge.
143
+ self.last_acknowledge_phase = 0
144
+
145
+ self._acknowledge = acknowledge
146
+ self._address_error_hook = address_error
147
+ self._reset_devices = reset_devices
148
+ self.function_codes = function_codes
149
+ self.tas_write = tas_write
150
+ self.attach_bus(read_byte, read_word, write_byte, write_word)
151
+
152
+ def attach_bus(
153
+ self,
154
+ read_byte: ReadFunction,
155
+ read_word: ReadFunction,
156
+ write_byte: WriteFunction,
157
+ write_word: WriteFunction,
158
+ ) -> None:
159
+ """Give the core its four bus callables (the constructor's; a debugger's wrappers).
160
+
161
+ The originals stay readable as ``read_byte``, ``read_word``,
162
+ ``write_byte`` and ``write_word``.
163
+ """
164
+ for name, value in (("read_byte", read_byte), ("read_word", read_word),
165
+ ("write_byte", write_byte), ("write_word", write_word)): # fmt: skip
166
+ if not callable(value):
167
+ raise TypeError(f"{name} must be callable")
168
+ self.read_byte, self.read_word = read_byte, read_word
169
+ self.write_byte, self.write_word = write_byte, write_word
170
+ tas_write = self.tas_write
171
+ if self.function_codes:
172
+ # Only a host that asks pays for function codes (README, "The embedding contract").
173
+ self._read_program = lambda address: read_word(address, fc=self._fc(True))
174
+ self._read_data_word = lambda address: read_word(address, fc=self._fc(False))
175
+ self._read_data_byte = lambda address: read_byte(address, fc=self._fc(False))
176
+ self._read_program_byte = lambda address: read_byte(address, fc=self._fc(True))
177
+ self._write_data_word = lambda address, value: write_word(
178
+ address, value, fc=self._fc(False)
179
+ )
180
+ self._write_data_byte = lambda address, value: write_byte(
181
+ address, value, fc=self._fc(False)
182
+ )
183
+ else:
184
+ self._read_program = read_word
185
+ self._read_data_word = read_word
186
+ self._read_data_byte = read_byte
187
+ self._read_program_byte = read_byte
188
+ self._write_data_word = write_word
189
+ self._write_data_byte = write_byte
190
+ if tas_write is None:
191
+ self._tas_write = self._write_data_byte
192
+ elif self.function_codes:
193
+ self._tas_write = lambda address, value: tas_write(address, value, fc=self._fc(False))
194
+ else:
195
+ self._tas_write = tas_write
196
+
197
+ # -- the stack pointers and the status register --------------------------
198
+
199
+ def _fc(self, program: bool) -> int:
200
+ if self.SR & S:
201
+ return FC_SUPERVISOR_PROGRAM if program else FC_SUPERVISOR_DATA
202
+ return FC_USER_PROGRAM if program else FC_USER_DATA
203
+
204
+ def _set_sr(self, value: int) -> None:
205
+ """Write the whole SR, swapping A7 with the other stack pointer if S changes."""
206
+ value &= SR_BITS
207
+ if (value ^ self.SR) & S:
208
+ self.R[15], self._other_sp = self._other_sp, self.R[15]
209
+ self.SR = value
210
+
211
+ def _set_ccr(self, value: int) -> None:
212
+ self.SR = (self.SR & 0xFF00) | (value & CCR_BITS)
213
+
214
+ @property
215
+ def usp(self) -> int:
216
+ """The user stack pointer, whichever mode the CPU is in."""
217
+ return self._other_sp if self.SR & S else self.R[15]
218
+
219
+ @usp.setter
220
+ def usp(self, value: int) -> None:
221
+ if self.SR & S:
222
+ self._other_sp = value & 0xFFFFFFFF
223
+ else:
224
+ self.R[15] = value & 0xFFFFFFFF
225
+
226
+ @property
227
+ def ssp(self) -> int:
228
+ """The supervisor stack pointer, whichever mode the CPU is in."""
229
+ return self.R[15] if self.SR & S else self._other_sp
230
+
231
+ @ssp.setter
232
+ def ssp(self, value: int) -> None:
233
+ if self.SR & S:
234
+ self.R[15] = value & 0xFFFFFFFF
235
+ else:
236
+ self._other_sp = value & 0xFFFFFFFF
237
+
238
+ # -- the bus ------------------------------------------------------------
239
+ # Every access checks alignment first: a word or long at an odd address
240
+ # is an address error, and the aborted cycle never reaches the host
241
+ # (UM 6.3.10; the corpus marks it "re"/"we", AS not asserted).
242
+
243
+ def _fault(self, vector: int, address: int, write: bool, program: bool) -> GroupZero:
244
+ return GroupZero(vector, address & 0xFFFFFFFF, write, program)
245
+
246
+ def _read_word(self, address: int) -> int:
247
+ if address & 1:
248
+ raise self._fault(VECTOR_ADDRESS_ERROR, address, False, False)
249
+ self._cycles += 4
250
+ try:
251
+ return self._read_data_word(address & MASK24)
252
+ except BusError:
253
+ raise self._fault(VECTOR_BUS_ERROR, address, False, False) from None
254
+
255
+ def _read_byte(self, address: int) -> int:
256
+ self._cycles += 4
257
+ try:
258
+ return self._read_data_byte(address & MASK24)
259
+ except BusError:
260
+ raise self._fault(VECTOR_BUS_ERROR, address, False, False) from None
261
+
262
+ def _read_long(self, address: int) -> int:
263
+ """Two word reads, high word first (UM 2.4)."""
264
+ high = self._read_word(address)
265
+ return (high << 16) | self._read_word(address + 2)
266
+
267
+ def _write_word(self, address: int, value: int) -> None:
268
+ if address & 1:
269
+ raise self._fault(VECTOR_ADDRESS_ERROR, address, True, False)
270
+ self._cycles += 4
271
+ try:
272
+ self._write_data_word(address & MASK24, value & 0xFFFF)
273
+ except BusError:
274
+ raise self._fault(VECTOR_BUS_ERROR, address, True, False) from None
275
+
276
+ def _write_byte(self, address: int, value: int) -> None:
277
+ self._cycles += 4
278
+ try:
279
+ self._write_data_byte(address & MASK24, value & 0xFF)
280
+ except BusError:
281
+ raise self._fault(VECTOR_BUS_ERROR, address, True, False) from None
282
+
283
+ def _write_long(self, address: int, value: int) -> None:
284
+ """Two word writes, high word first."""
285
+ if address & 1:
286
+ raise self._fault(VECTOR_ADDRESS_ERROR, address, True, False)
287
+ self._write_word(address, value >> 16)
288
+ self._write_word(address + 2, value)
289
+
290
+ def _write_long_low_first(self, address: int, value: int) -> None:
291
+ """Two word writes, low word (at address + 2) first.
292
+
293
+ The order of -(An) destinations and of read-modify-write results:
294
+ the microcode works toward the high word (SST MOVE.l, ADDX.l, SUBX.l,
295
+ MOVEM.l and the long read-modify-write files; T3). An odd
296
+ address faults on the first write, at ``address + 2``.
297
+ """
298
+ self._write_word(address + 2, value)
299
+ self._write_word(address, value >> 16)
300
+
301
+ def _read(self, size: int, address: int) -> int:
302
+ if size == 2:
303
+ return self._read_word(address)
304
+ if size == 1:
305
+ return self._read_byte(address)
306
+ return self._read_long(address)
307
+
308
+ def _write(self, size: int, address: int, value: int) -> None:
309
+ """A byte or word write; every long write orders its halves at its caller."""
310
+ if size == 2:
311
+ self._write_word(address, value)
312
+ else:
313
+ self._write_byte(address, value)
314
+
315
+ def _read_program_word(self, address: int) -> int:
316
+ if address & 1:
317
+ raise self._fault(VECTOR_ADDRESS_ERROR, address, False, True)
318
+ self._cycles += 4
319
+ try:
320
+ return self._read_program(address & MASK24)
321
+ except BusError:
322
+ raise self._fault(VECTOR_BUS_ERROR, address, False, True) from None
323
+
324
+ def _read_program_operand(self, size: int, address: int) -> int:
325
+ """A PC-relative operand: read in program space (UM Table 3-2, FC 2/6)."""
326
+ if size == 1:
327
+ self._cycles += 4
328
+ try:
329
+ return self._read_program_byte(address & MASK24)
330
+ except BusError:
331
+ raise self._fault(VECTOR_BUS_ERROR, address, False, True) from None
332
+ high = self._read_program_word(address)
333
+ if size == 2:
334
+ return high
335
+ return (high << 16) | self._read_program_word(address + 2)
336
+
337
+ # -- the prefetch queue ---------------------------------------------------
338
+
339
+ def _commit_pc(self) -> None:
340
+ """Copy the fetch address into the microcode's PC register.
341
+
342
+ Only an address error can see the difference: it stacks this value
343
+ (UM 6.2.5 "unpredictable"; the steps that commit it are the
344
+ corpus's, T3).
345
+ """
346
+ self._fault_pc = self._pc
347
+
348
+ # The three refills below write _read_program_word's body out instead of
349
+ # calling it: they run once or more per instruction, and a Python frame
350
+ # is the cost that dominates this core (rung C, docs/validation.md "Speed").
351
+ # They make no odd-address check because _pc is always even: every path
352
+ # that sets it either faults first on an odd target (_jump, _jump_idle,
353
+ # DBcc, JSR read the target's word through _read_program_word before
354
+ # taking it) or refuses one (set_pc, CPUState), and the refills add 2.
355
+
356
+ def _extension(self) -> int:
357
+ """Take the extension word waiting in IRC and refill IRC from _pc."""
358
+ value = self.irc
359
+ address = self._pc
360
+ self._cycles += 4
361
+ try:
362
+ self.irc = self._read_program(address & MASK24)
363
+ except BusError:
364
+ raise self._fault(VECTOR_BUS_ERROR, address, False, True) from None
365
+ self._pc = (address + 2) & 0xFFFFFFFF
366
+ return value
367
+
368
+ def _extension_long(self) -> int:
369
+ high = self._extension()
370
+ return (high << 16) | self._extension()
371
+
372
+ def _prefetch(self) -> None:
373
+ """The last step of every instruction: IRC moves to IR, IRC refills.
374
+
375
+ The opcode of the next instruction is then in IR and its first
376
+ extension word in IRC (UM "Prefetch"; corpus ``prefetch`` pair).
377
+ """
378
+ address = self._fault_pc = self._pc
379
+ # IR takes IRC, and the decoder (IRD) takes IR, before the read: a
380
+ # bus error on this read already stacks the next opcode (every
381
+ # faulting closing prefetch of the corpus, T3, where the fault is an
382
+ # odd jump target's caught in _jump before the queue moves).
383
+ self.ir = self._opcode = self.irc
384
+ self._cycles += 4
385
+ try:
386
+ self.irc = self._read_program(address & MASK24)
387
+ except BusError:
388
+ raise self._fault(VECTOR_BUS_ERROR, address, False, True) from None
389
+ self._pc = (address + 2) & 0xFFFFFFFF
390
+
391
+ def _prefetch_before_write(self) -> None:
392
+ """The closing prefetch of an instruction that still has a write to do.
393
+
394
+ IR takes IRC and IRC refills, but the decoder keeps the current
395
+ opcode until the handler hands it over with ``self._opcode =
396
+ self.ir`` at the step the microcode does (SST MOVE.l, PEA; T3): an
397
+ address error on
398
+ a write before that point stacks the old opcode.
399
+ """
400
+ address = self._fault_pc = self._pc
401
+ self.ir = self.irc
402
+ self._cycles += 4
403
+ try:
404
+ self.irc = self._read_program(address & MASK24)
405
+ except BusError:
406
+ raise self._fault(VECTOR_BUS_ERROR, address, False, True) from None
407
+ self._pc = (address + 2) & 0xFFFFFFFF
408
+
409
+ def _jump(self, target: int) -> None:
410
+ """Refill the queue from ``target``: two program reads (UM Table 8-9).
411
+
412
+ The first read lands in IRC; the closing prefetch moves it to IR and
413
+ reads the word after it.
414
+ """
415
+ target &= 0xFFFFFFFF
416
+ self.irc = self._read_program_word(target)
417
+ self._pc = (target + 2) & 0xFFFFFFFF
418
+ self._prefetch()
419
+
420
+ def _jump_idle(self, target: int) -> None:
421
+ """A jump whose refill has two internal clocks between its reads."""
422
+ target &= 0xFFFFFFFF
423
+ self.irc = self._read_program_word(target)
424
+ self._pc = (target + 2) & 0xFFFFFFFF
425
+ self._cycles += 2
426
+ self._prefetch()
427
+
428
+ # -- the stack -------------------------------------------------------------
429
+
430
+ def _push_long(self, value: int) -> None:
431
+ """Push a long, high word first (BSR, JSR, PEA; corpus, T3)."""
432
+ sp = (self.R[15] - 4) & 0xFFFFFFFF
433
+ self.R[15] = sp
434
+ self._write_long(sp, value)
435
+
436
+ def _pop_word(self) -> int:
437
+ sp = self.R[15]
438
+ value = self._read_word(sp)
439
+ self.R[15] = (sp + 2) & 0xFFFFFFFF
440
+ return value
441
+
442
+ def _pop_long(self) -> int:
443
+ sp = self.R[15]
444
+ value = self._read_long(sp)
445
+ self.R[15] = (sp + 4) & 0xFFFFFFFF
446
+ return value
447
+
448
+ # -- exceptions (UM 6.2, 6.3) ---------------------------------------------
449
+
450
+ def _enter_supervisor(self) -> int:
451
+ """Step 1 of exception processing: copy SR, set S, clear T (UM 6.2.5)."""
452
+ saved = self.SR
453
+ self._set_sr((saved | S) & ~T)
454
+ return saved
455
+
456
+ def _exception(
457
+ self,
458
+ vector: int,
459
+ pc: int,
460
+ *,
461
+ idle: int = 4,
462
+ saved: int | None = None,
463
+ by_instruction: bool = False,
464
+ ) -> None:
465
+ """Group 1 and 2 exception entry with the three-word frame (UM Figure 6-5).
466
+
467
+ ``pc`` is the value to stack; ``idle`` the internal clocks before the
468
+ first push. The pushes, the vector fetch and the refill are in the
469
+ order the corpus records: PC low, SR, PC high, vector high and low,
470
+ then the handler's first two words with two idle clocks between.
471
+ ``by_instruction`` marks group 2 (TRAP, TRAPV, CHK, divide by zero),
472
+ whose processing the instruction itself starts.
473
+ """
474
+ entered = self._enter_supervisor()
475
+ if saved is None:
476
+ saved = entered
477
+ self._trace_pending = False
478
+ self._cycles += idle
479
+ # An address or bus error from here on is one "not an instruction"
480
+ # access, I/N set in its information word (UM Figure 6-7), except in
481
+ # group 2, where the instruction started the processing: I/N clear
482
+ # (WinUAE's tester core, T2 by running; MAME 0.285's microcode, T3).
483
+ self._processing_exception = not by_instruction
484
+ sp = (self.R[15] - 6) & 0xFFFFFFFF
485
+ self.R[15] = sp
486
+ self._write_word(sp + 4, pc)
487
+ self._write_word(sp, saved)
488
+ self._write_word(sp + 2, pc >> 16)
489
+ self._fault_pc = pc
490
+ target = self._read_vector(vector)
491
+ self._jump_idle(target)
492
+ self._processing_exception = False
493
+
494
+ def _read_vector(self, vector: int) -> int:
495
+ address = vector << 2
496
+ high = self._read_word(address)
497
+ return (high << 16) | self._read_word(address + 2)
498
+
499
+ def _group_zero(self, fault: GroupZero) -> None:
500
+ """Address or bus error: abort, stack the seven-word frame (UM Figure 6-7).
501
+
502
+ The aborted access costs its four clocks and four more; two internal
503
+ steps of two clocks follow (the corpus's 55,606 address errors, T3;
504
+ WinUAE run agrees on the clocks). The frame is written in
505
+ the corpus's order: PC low, SR, PC high, IR, access address low,
506
+ access information, access address high. A second group 0 fault
507
+ while doing this halts the processor (UM 5.4.4).
508
+ """
509
+ if fault.vector == VECTOR_ADDRESS_ERROR and self._address_error_hook is not None:
510
+ self._address_error_hook(fault.address & MASK24, fault.write, self._fc(fault.program))
511
+ self._cycles += 4 + 4 + 2 + 2
512
+ # The access information word: bits 15-5 are the undefined part and
513
+ # carry IR's (the corpus's faulting cases, T3; WinUAE run agrees on
514
+ # every frame it judges; UM Figure 6-7 marks them undefined), R/W is
515
+ # bit 4, I/N bit 3 (set when the access was part of exception
516
+ # processing rather than of an instruction), and the function code.
517
+ information = (
518
+ (self._opcode & 0xFFE0)
519
+ | (0 if fault.write else 0x10)
520
+ | (0x08 if self._processing_exception else 0)
521
+ | self._fc(fault.program)
522
+ )
523
+ self._processing_exception = False
524
+ saved = self._enter_supervisor()
525
+ self._trace_pending = False
526
+ pc = self._fault_pc
527
+ sp = (self.R[15] - 14) & 0xFFFFFFFF
528
+ self.R[15] = sp
529
+ try:
530
+ self._write_word(sp + 12, pc)
531
+ self._write_word(sp + 8, saved)
532
+ self._write_word(sp + 10, pc >> 16)
533
+ self._write_word(sp + 6, self._opcode)
534
+ self._write_word(sp + 4, fault.address)
535
+ self._write_word(sp, information)
536
+ self._write_word(sp + 2, fault.address >> 16)
537
+ target = self._read_vector(fault.vector)
538
+ self._jump_idle(target)
539
+ except GroupZero:
540
+ # A group 0 fault while processing a group 0 exception: the double
541
+ # bus fault halts the processor until reset (UM 5.4.4, 6.3.9.1).
542
+ # MAME 0.285 takes another address error instead; the corpus has
543
+ # no such case, and the manual is followed (docs/claims.md,
544
+ # "Undecidable here").
545
+ self.halted = True