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/_system.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""System control: traps, the status register, the stack pointers, STOP, RESET.
|
|
2
|
+
|
|
3
|
+
PRM Table 3-10 and Section 6; the exception processing is UM 6.2-6.3. The
|
|
4
|
+
privileged instructions check S first and take the privilege-violation
|
|
5
|
+
exception (vector 8) in user mode, stacking the address of the instruction
|
|
6
|
+
itself (UM 6.3.7). An instruction that writes SR refills the prefetch queue
|
|
7
|
+
afterwards, re-reading the next instruction in the (possibly new) program
|
|
8
|
+
space (SST MOVEtoSR, ORItoSR, ANDItoSR, EORItoSR; T3).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from m68000_python._core import (
|
|
12
|
+
CCR_BITS,
|
|
13
|
+
VECTOR_CHK,
|
|
14
|
+
VECTOR_ILLEGAL,
|
|
15
|
+
VECTOR_LINE_A,
|
|
16
|
+
VECTOR_LINE_F,
|
|
17
|
+
VECTOR_PRIVILEGE,
|
|
18
|
+
VECTOR_TRAP_BASE,
|
|
19
|
+
VECTOR_TRAPV,
|
|
20
|
+
VECTOR_ZERO_DIVIDE,
|
|
21
|
+
N,
|
|
22
|
+
S,
|
|
23
|
+
V,
|
|
24
|
+
X,
|
|
25
|
+
Z,
|
|
26
|
+
)
|
|
27
|
+
from m68000_python._ea import DN, EA_KIND, sign_extend_16
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SystemMixin:
|
|
31
|
+
"""Private system-control implementation."""
|
|
32
|
+
|
|
33
|
+
# -- exceptions an instruction raises -----------------------------------
|
|
34
|
+
|
|
35
|
+
def _not_executed(self, vector: int) -> None:
|
|
36
|
+
"""The exception for an instruction that is never executed, stacking its address.
|
|
37
|
+
|
|
38
|
+
Illegal words, the line 1010 and 1111 words, and privileged
|
|
39
|
+
instructions in user mode are not executed, so no trace exception
|
|
40
|
+
follows them even when T was set (UM 6.3.8; MAME 0.285's microcode
|
|
41
|
+
clears the pending trace in these four states, T3; WinUAE keeps it
|
|
42
|
+
only after vectors 5-7 and 32-47, T2). step() reads ``_untraced``.
|
|
43
|
+
"""
|
|
44
|
+
self._exception(vector, self._pc - 4)
|
|
45
|
+
self._untraced = True
|
|
46
|
+
|
|
47
|
+
def _privilege_violation(self) -> None:
|
|
48
|
+
"""Vector 8, stacking the address of the offending instruction (UM 6.3.7)."""
|
|
49
|
+
self._not_executed(VECTOR_PRIVILEGE)
|
|
50
|
+
|
|
51
|
+
def _op_illegal(self, opcode: int) -> None:
|
|
52
|
+
"""ILLEGAL -- take the illegal-instruction exception, vector 4 (PRM 4-107; UM 6.3.6).
|
|
53
|
+
|
|
54
|
+
Also every first word the 68000 does not define. The stacked PC is
|
|
55
|
+
the address of the word itself (SST ILLEGAL_LINEA, ILLEGAL_LINEF, and
|
|
56
|
+
WinUAE run for $4AFC itself; UM 6.3.6 says only
|
|
57
|
+
"similar to that for traps").
|
|
58
|
+
"""
|
|
59
|
+
self._not_executed(VECTOR_ILLEGAL)
|
|
60
|
+
|
|
61
|
+
def _op_line_a(self, opcode: int) -> None:
|
|
62
|
+
"""LINE A -- words $Axxx: the line 1010 emulator exception, vector 10 (UM 6.3.6)."""
|
|
63
|
+
self._not_executed(VECTOR_LINE_A)
|
|
64
|
+
|
|
65
|
+
def _op_line_f(self, opcode: int) -> None:
|
|
66
|
+
"""LINE F -- words $Fxxx: the line 1111 emulator exception, vector 11 (UM 6.3.6)."""
|
|
67
|
+
self._not_executed(VECTOR_LINE_F)
|
|
68
|
+
|
|
69
|
+
def _op_trap(self, opcode: int) -> None:
|
|
70
|
+
"""TRAP -- exception through vector 32 + n, stacking the next PC (PRM 4-188; UM 6.3.5)."""
|
|
71
|
+
self._exception(VECTOR_TRAP_BASE + (opcode & 0xF), self._pc - 2, by_instruction=True)
|
|
72
|
+
|
|
73
|
+
def _op_trapv(self, opcode: int) -> None:
|
|
74
|
+
"""TRAPV -- if V, exception through vector 7 (PRM 4-189; UM Table 8-14: 34 clocks)."""
|
|
75
|
+
if not self.SR & V:
|
|
76
|
+
self._prefetch()
|
|
77
|
+
return
|
|
78
|
+
# Exception processing starts before the queue refill, which is
|
|
79
|
+
# therefore read in supervisor program space (SST TRAPV).
|
|
80
|
+
saved = self._enter_supervisor()
|
|
81
|
+
self._prefetch()
|
|
82
|
+
# IR takes IRC, but the decoder keeps TRAPV: a fault while the trap
|
|
83
|
+
# is processed stacks TRAPV as its IR (WinUAE's CPU tester, run, T2;
|
|
84
|
+
# MAME 0.285's trpv3 never loads IRD, T3; docs/referees.md).
|
|
85
|
+
self._opcode = opcode
|
|
86
|
+
self._exception(VECTOR_TRAPV, self._pc - 4, idle=0, saved=saved, by_instruction=True)
|
|
87
|
+
|
|
88
|
+
def _divide_by_zero(self, signed: bool, dividend: int) -> None:
|
|
89
|
+
"""DIVU/DIVS by zero: vector 5, stacking the next PC (UM 6.3.5; Table 8-14: 38+).
|
|
90
|
+
|
|
91
|
+
The flags are undefined in PRM. The pinned corpus has no case (its
|
|
92
|
+
issue #3); WinUAE's 68000 rule (``divbyzero_special``, T2) is used:
|
|
93
|
+
DIVS clears N V C and sets Z; DIVU clears V C and sets N and Z from
|
|
94
|
+
the high word of the dividend (docs/undocumented-behavior.md).
|
|
95
|
+
"""
|
|
96
|
+
ccr = self.SR & (0xFF00 | X)
|
|
97
|
+
if signed:
|
|
98
|
+
ccr |= Z
|
|
99
|
+
elif dividend & 0x80000000:
|
|
100
|
+
ccr |= N
|
|
101
|
+
elif not dividend & 0xFFFF0000:
|
|
102
|
+
ccr |= Z
|
|
103
|
+
self.SR = ccr
|
|
104
|
+
self._exception(VECTOR_ZERO_DIVIDE, self._pc - 2, idle=8, by_instruction=True)
|
|
105
|
+
|
|
106
|
+
def _op_chk(self, opcode: int) -> None:
|
|
107
|
+
"""CHK -- trap through vector 6 if Dn.w < 0 or Dn.w > source (PRM 4-69; UM Table 8-12).
|
|
108
|
+
|
|
109
|
+
N, Z, V and C are undefined in PRM; here they are what the microcode
|
|
110
|
+
leaves (SST CHK), which is also WinUAE run's rule on every case and on
|
|
111
|
+
Dn = 0, which no corpus has: Z from Dn, V and C clear, N set for Dn < 0
|
|
112
|
+
and clear for Dn > source (docs/undocumented-behavior.md, "Flags after
|
|
113
|
+
CHK"). The trap for Dn > bound takes 38 clocks, the manual's 40 less 2,
|
|
114
|
+
and the negative-Dn trap 8 or 10 internal clocks (below): SST CHK; the
|
|
115
|
+
2-clock questions are inside WinUAE's tolerance (docs/claims.md).
|
|
116
|
+
"""
|
|
117
|
+
bound = sign_extend_16(self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2))
|
|
118
|
+
value = sign_extend_16(self.R[(opcode >> 9) & 7])
|
|
119
|
+
ccr = self.SR & (0xFF00 | X)
|
|
120
|
+
if value == 0:
|
|
121
|
+
ccr |= Z
|
|
122
|
+
if value < 0:
|
|
123
|
+
ccr |= N
|
|
124
|
+
self.SR = ccr
|
|
125
|
+
# The microcode tests the upper bound first. A negative Dn within it
|
|
126
|
+
# costs one more internal step, unless bound - Dn, taken as a 16-bit
|
|
127
|
+
# difference, came out negative (SST CHK: every case agrees).
|
|
128
|
+
if value > bound:
|
|
129
|
+
self._exception(VECTOR_CHK, self._pc - 2, idle=8, by_instruction=True)
|
|
130
|
+
elif value < 0:
|
|
131
|
+
idle = 8 if (bound - value) & 0x8000 else 10
|
|
132
|
+
self._exception(VECTOR_CHK, self._pc - 2, idle=idle, by_instruction=True)
|
|
133
|
+
else:
|
|
134
|
+
self._cycles += 6
|
|
135
|
+
self._prefetch()
|
|
136
|
+
|
|
137
|
+
# -- the status register and CCR (PRM 4-20, 4-104, 4-155, 4-123, 6-*) --------
|
|
138
|
+
|
|
139
|
+
def _refill_after_status(self) -> None:
|
|
140
|
+
"""Re-read the queue from the next instruction after SR or CCR is written."""
|
|
141
|
+
self._jump(self._pc - 2)
|
|
142
|
+
|
|
143
|
+
def _write_status(self, value: int, idle: int = 4) -> None:
|
|
144
|
+
self._cycles += idle
|
|
145
|
+
self._set_sr(value)
|
|
146
|
+
self._refill_after_status()
|
|
147
|
+
|
|
148
|
+
def _write_ccr(self, value: int, idle: int = 4) -> None:
|
|
149
|
+
self._cycles += idle
|
|
150
|
+
self._set_ccr(value)
|
|
151
|
+
self._refill_after_status()
|
|
152
|
+
|
|
153
|
+
def _op_ori_to_ccr(self, opcode: int) -> None:
|
|
154
|
+
"""ORI to CCR -- CCR <- CCR OR #data (PRM 4-155; UM Table 8-12: 20 clocks).
|
|
155
|
+
|
|
156
|
+
8 idle clocks, then the queue re-read from the next instruction:
|
|
157
|
+
SST ORItoCCR.
|
|
158
|
+
"""
|
|
159
|
+
data = self._immediate(1)
|
|
160
|
+
self._write_ccr((self.SR | data) & CCR_BITS, idle=8)
|
|
161
|
+
|
|
162
|
+
def _op_andi_to_ccr(self, opcode: int) -> None:
|
|
163
|
+
"""ANDI to CCR -- CCR <- CCR AND #data (PRM 4-20; UM Table 8-12: 20 clocks).
|
|
164
|
+
|
|
165
|
+
8 idle clocks, then the queue re-read from the next instruction:
|
|
166
|
+
SST ANDItoCCR.
|
|
167
|
+
"""
|
|
168
|
+
data = self._immediate(1)
|
|
169
|
+
self._write_ccr(self.SR & data & CCR_BITS, idle=8)
|
|
170
|
+
|
|
171
|
+
def _op_eori_to_ccr(self, opcode: int) -> None:
|
|
172
|
+
"""EORI to CCR -- CCR <- CCR XOR #data (PRM 4-104; UM Table 8-12: 20 clocks).
|
|
173
|
+
|
|
174
|
+
8 idle clocks, then the queue re-read from the next instruction:
|
|
175
|
+
SST EORItoCCR.
|
|
176
|
+
"""
|
|
177
|
+
data = self._immediate(1)
|
|
178
|
+
self._write_ccr((self.SR ^ data) & CCR_BITS, idle=8)
|
|
179
|
+
|
|
180
|
+
def _op_ori_to_sr(self, opcode: int) -> None:
|
|
181
|
+
"""ORI to SR -- SR <- SR OR #data, privileged (PRM 6-27; UM Table 8-12: 20 clocks).
|
|
182
|
+
|
|
183
|
+
8 idle clocks, then the queue re-read from the next instruction in the
|
|
184
|
+
new program space: SST ORItoSR.
|
|
185
|
+
"""
|
|
186
|
+
if not self.SR & S:
|
|
187
|
+
self._privilege_violation()
|
|
188
|
+
return
|
|
189
|
+
data = self._immediate(2)
|
|
190
|
+
self._write_status(self.SR | data, idle=8)
|
|
191
|
+
|
|
192
|
+
def _op_andi_to_sr(self, opcode: int) -> None:
|
|
193
|
+
"""ANDI to SR -- SR <- SR AND #data, privileged (PRM 6-2; UM Table 8-12: 20 clocks).
|
|
194
|
+
|
|
195
|
+
8 idle clocks, then the queue re-read from the next instruction in the
|
|
196
|
+
new program space: SST ANDItoSR.
|
|
197
|
+
"""
|
|
198
|
+
if not self.SR & S:
|
|
199
|
+
self._privilege_violation()
|
|
200
|
+
return
|
|
201
|
+
data = self._immediate(2)
|
|
202
|
+
self._write_status(self.SR & data, idle=8)
|
|
203
|
+
|
|
204
|
+
def _op_eori_to_sr(self, opcode: int) -> None:
|
|
205
|
+
"""EORI to SR -- SR <- SR XOR #data, privileged (PRM 6-6; UM Table 8-12: 20 clocks).
|
|
206
|
+
|
|
207
|
+
8 idle clocks, then the queue re-read from the next instruction in the
|
|
208
|
+
new program space: SST EORItoSR.
|
|
209
|
+
"""
|
|
210
|
+
if not self.SR & S:
|
|
211
|
+
self._privilege_violation()
|
|
212
|
+
return
|
|
213
|
+
data = self._immediate(2)
|
|
214
|
+
self._write_status(self.SR ^ data, idle=8)
|
|
215
|
+
|
|
216
|
+
def _op_move_to_ccr(self, opcode: int) -> None:
|
|
217
|
+
"""MOVE to CCR -- CCR <- low byte of the source word (PRM 4-123; UM Table 8-12).
|
|
218
|
+
|
|
219
|
+
4 idle clocks, then the queue re-read from the next instruction; the PC
|
|
220
|
+
an address error stacks: SST MOVEtoCCR.
|
|
221
|
+
"""
|
|
222
|
+
value = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
223
|
+
self._write_ccr(value)
|
|
224
|
+
|
|
225
|
+
def _op_move_to_sr(self, opcode: int) -> None:
|
|
226
|
+
"""MOVE to SR -- SR <- source word, privileged (PRM 6-19; UM Table 8-12).
|
|
227
|
+
|
|
228
|
+
4 idle clocks, then the queue re-read from the next instruction in the
|
|
229
|
+
new program space; the PC an address error stacks: SST MOVEtoSR.
|
|
230
|
+
"""
|
|
231
|
+
if not self.SR & S:
|
|
232
|
+
self._privilege_violation()
|
|
233
|
+
return
|
|
234
|
+
value = self._ea_read(EA_KIND[opcode & 0x3F], opcode & 7, 2)
|
|
235
|
+
self._write_status(value)
|
|
236
|
+
|
|
237
|
+
def _op_move_from_sr(self, opcode: int) -> None:
|
|
238
|
+
"""MOVE from SR -- destination <- SR; not privileged on the 68000 (PRM 6-17; UM 6.3.7).
|
|
239
|
+
|
|
240
|
+
The memory operand is read before it is written, as the chip does; the
|
|
241
|
+
2 idle clocks of the register form and the PC an address error stacks:
|
|
242
|
+
SST MOVEfromSR.
|
|
243
|
+
"""
|
|
244
|
+
kind = EA_KIND[opcode & 0x3F]
|
|
245
|
+
register = opcode & 7
|
|
246
|
+
if kind == DN:
|
|
247
|
+
self._prefetch()
|
|
248
|
+
self._cycles += 2
|
|
249
|
+
self._write_register(register, 2, self.SR)
|
|
250
|
+
return
|
|
251
|
+
address, _ = self._ea_fetch(kind, register, 2) # read first, as the chip does
|
|
252
|
+
self._prefetch()
|
|
253
|
+
self._write_word(address, self.SR)
|
|
254
|
+
|
|
255
|
+
def _op_move_usp(self, opcode: int) -> None:
|
|
256
|
+
"""MOVE USP -- An <-> USP, privileged (PRM 6-21; UM Table 8-12: 4 clocks)."""
|
|
257
|
+
if not self.SR & S:
|
|
258
|
+
self._privilege_violation()
|
|
259
|
+
return
|
|
260
|
+
an = 8 + (opcode & 7)
|
|
261
|
+
if opcode & 8:
|
|
262
|
+
self.R[an] = self._other_sp
|
|
263
|
+
else:
|
|
264
|
+
self._other_sp = self.R[an]
|
|
265
|
+
self._prefetch()
|
|
266
|
+
|
|
267
|
+
# -- returns that restore state --------------------------------------------
|
|
268
|
+
|
|
269
|
+
def _op_rte(self, opcode: int) -> None:
|
|
270
|
+
"""RTE -- SR <- (SP)+, PC <- (SP)+, privileged (PRM 6-84; UM Table 8-12: 20 clocks)."""
|
|
271
|
+
if not self.SR & S:
|
|
272
|
+
self._privilege_violation()
|
|
273
|
+
return
|
|
274
|
+
sp = self.R[15]
|
|
275
|
+
status = self._read_word(sp)
|
|
276
|
+
high = self._read_word(sp + 2)
|
|
277
|
+
self.R[15] = (sp + 6) & 0xFFFFFFFF
|
|
278
|
+
pc = (high << 16) | self._read_word(sp + 4)
|
|
279
|
+
self._set_sr(status)
|
|
280
|
+
self._jump(pc)
|
|
281
|
+
|
|
282
|
+
# -- STOP and RESET ------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
def _op_stop(self, opcode: int) -> None:
|
|
285
|
+
"""STOP -- SR <- #data, then stop until an interrupt or reset (PRM 6-85; UM Table 8-12).
|
|
286
|
+
|
|
287
|
+
The immediate is taken from IRC without a refill and the queue is left
|
|
288
|
+
as it was, so a stopped CPU resumes at the instruction after STOP:
|
|
289
|
+
SST STOP.
|
|
290
|
+
"""
|
|
291
|
+
if not self.SR & S:
|
|
292
|
+
self._privilege_violation()
|
|
293
|
+
return
|
|
294
|
+
data = self.irc
|
|
295
|
+
self._cycles += 4
|
|
296
|
+
self._set_sr(data)
|
|
297
|
+
self.stopped = True
|
|
298
|
+
|
|
299
|
+
def _op_reset(self, opcode: int) -> None:
|
|
300
|
+
"""RESET -- pulse the RESET line, privileged (PRM 6-83; UM Table 8-12: 132 clocks).
|
|
301
|
+
|
|
302
|
+
The pulse is 124 clocks; the instruction takes 132 in all (UM Table
|
|
303
|
+
8-12): 128 before the closing prefetch, as SST RESET records. The
|
|
304
|
+
processor itself is not reset. A host that wants to see the pulse
|
|
305
|
+
passes ``reset_devices`` to the constructor; it is called with no
|
|
306
|
+
arguments.
|
|
307
|
+
"""
|
|
308
|
+
if not self.SR & S:
|
|
309
|
+
self._privilege_violation()
|
|
310
|
+
return
|
|
311
|
+
self._cycles += 128
|
|
312
|
+
if self._reset_devices is not None:
|
|
313
|
+
self._reset_devices()
|
|
314
|
+
self._prefetch()
|