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