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/_bcd.py ADDED
@@ -0,0 +1,137 @@
1
+ """Binary-coded decimal: ABCD, SBCD, NBCD (PRM Table 3-8).
2
+
3
+ PRM Table 3-18 gives X and C (the decimal carry or borrow) and a sticky Z,
4
+ and calls N and V undefined. They are not undefined on silicon, and the
5
+ rule below is the one flamewing's 68k-bcd-verifier checks exhaustively on a
6
+ Model 1 and a Model 3 Sega Genesis (T1; docs/undocumented-behavior.md): the
7
+ byte is first added or subtracted in binary, then corrected by 6 in each
8
+ nibble that produced a binary carry (borrow) or, for an add, a digit above 9;
9
+ N is bit 7 of the corrected byte, and V is set when the correction flipped
10
+ bit 7 -- from 0 to 1 for an add, from 1 to 0 for a subtract. C and X are set
11
+ by a carry (borrow) out of either the binary step or the correction.
12
+
13
+ The three instructions' bus sequences are the microcode's (SST ABCD, SBCD, NBCD; T3): the
14
+ register forms prefetch and then spend two internal clocks; the memory forms
15
+ read, prefetch, then write.
16
+ """
17
+
18
+ from m68000_python._core import C, N, V, X, Z
19
+ from m68000_python._ea import DN, EA_KIND
20
+
21
+
22
+ def decimal_add(destination: int, source: int, extend: int) -> tuple[int, int]:
23
+ """ABCD's result byte and X N Z V C bits (Z to be ANDed in: sticky)."""
24
+ binary = destination + source + extend
25
+ low_carry = (destination & 0xF) + (source & 0xF) + extend > 0xF
26
+ uncorrected = binary & 0xFF
27
+ correction = 0
28
+ if low_carry or (uncorrected & 0xF) > 9:
29
+ correction = 0x06
30
+ if binary > 0xFF or uncorrected > 0x99:
31
+ correction |= 0x60
32
+ corrected = uncorrected + correction
33
+ result = corrected & 0xFF
34
+ flags = Z if result == 0 else 0
35
+ if binary > 0xFF or corrected > 0xFF:
36
+ flags |= X | C
37
+ if result & 0x80:
38
+ flags |= N
39
+ if result & 0x80 and not uncorrected & 0x80:
40
+ flags |= V # the correction carried into bit 7 (T1, flamewing)
41
+ return result, flags
42
+
43
+
44
+ def decimal_subtract(destination: int, source: int, extend: int) -> tuple[int, int]:
45
+ """SBCD's (and NBCD's, with destination 0) result byte and X N Z V C bits."""
46
+ binary = destination - source - extend
47
+ low_borrow = (destination & 0xF) - (source & 0xF) - extend < 0
48
+ uncorrected = binary & 0xFF
49
+ correction = 0
50
+ if low_borrow:
51
+ correction = 0x06
52
+ if binary < 0:
53
+ correction |= 0x60
54
+ corrected = uncorrected - correction
55
+ result = corrected & 0xFF
56
+ flags = Z if result == 0 else 0
57
+ if binary < 0 or corrected < 0:
58
+ flags |= X | C
59
+ if result & 0x80:
60
+ flags |= N
61
+ if uncorrected & 0x80 and not result & 0x80:
62
+ flags |= V # the correction borrowed out of bit 7 (T1, flamewing)
63
+ return result, flags
64
+
65
+
66
+ class BCDMixin:
67
+ """Private BCD implementation."""
68
+
69
+ def _set_bcd_flags(self, flags: int) -> None:
70
+ # X N V C replaced; Z only ever cleared (PRM Table 3-18, sticky Z).
71
+ sr = self.SR
72
+ self.SR = (sr & 0xFF00) | (flags & (X | N | V | C)) | (sr & flags & Z)
73
+
74
+ def _bcd_pair(self, opcode: int, operation) -> None:
75
+ """The two forms shared by ABCD and SBCD: Dy,Dx and -(Ay),-(Ax)."""
76
+ rx = (opcode >> 9) & 7
77
+ ry = opcode & 7
78
+ extend = (self.SR >> 4) & 1
79
+ R = self.R
80
+ if not opcode & 8:
81
+ result, flags = operation(R[rx] & 0xFF, R[ry] & 0xFF, extend)
82
+ self._set_bcd_flags(flags)
83
+ self._prefetch()
84
+ self._cycles += 2
85
+ R[rx] = (R[rx] & 0xFFFFFF00) | result
86
+ return
87
+ self._cycles += 2
88
+ self._commit_pc()
89
+ source_address = (R[8 + ry] - self._step_size(ry, 1)) & 0xFFFFFFFF
90
+ R[8 + ry] = source_address
91
+ source = self._read_byte(source_address)
92
+ destination_address = (R[8 + rx] - self._step_size(rx, 1)) & 0xFFFFFFFF
93
+ R[8 + rx] = destination_address
94
+ destination = self._read_byte(destination_address)
95
+ result, flags = operation(destination, source, extend)
96
+ self._set_bcd_flags(flags)
97
+ self._prefetch()
98
+ self._write_byte(destination_address, result)
99
+
100
+ def _op_abcd(self, opcode: int) -> None:
101
+ """ABCD -- destination <- destination + source + X, decimal (PRM 4-3; UM Table 8-11).
102
+
103
+ N and V, undefined in PRM: flamewing's hardware-captured tables (T1,
104
+ tests/test_bcd.py). Bus order, the 2 idle clocks of the register form and
105
+ the PC an address error stacks: SST ABCD.
106
+ """
107
+ self._bcd_pair(opcode, decimal_add)
108
+
109
+ def _op_sbcd(self, opcode: int) -> None:
110
+ """SBCD -- destination <- destination - source - X, decimal (PRM 4-176; UM Table 8-11).
111
+
112
+ N and V: flamewing's tables (T1). Bus order and the PC an address error
113
+ stacks: SST SBCD.
114
+ """
115
+ self._bcd_pair(opcode, decimal_subtract)
116
+
117
+ def _op_nbcd(self, opcode: int) -> None:
118
+ """NBCD -- destination <- 0 - destination - X, decimal (PRM 4-142; UM Table 8-6).
119
+
120
+ N and V: flamewing's tables (T1). Bus order and the PC an address error
121
+ stacks: SST NBCD.
122
+ """
123
+ kind = EA_KIND[opcode & 0x3F]
124
+ register = opcode & 7
125
+ extend = (self.SR >> 4) & 1
126
+ if kind == DN:
127
+ result, flags = decimal_subtract(0, self.R[register] & 0xFF, extend)
128
+ self._set_bcd_flags(flags)
129
+ self._prefetch()
130
+ self._cycles += 2
131
+ self.R[register] = (self.R[register] & 0xFFFFFF00) | result
132
+ return
133
+ address = self._ea_address(kind, register, 1)
134
+ result, flags = decimal_subtract(0, self._read_byte(address), extend)
135
+ self._set_bcd_flags(flags)
136
+ self._prefetch()
137
+ self._write_byte(address, result)
m68000_python/_bits.py ADDED
@@ -0,0 +1,100 @@
1
+ """Bit manipulation: BTST, BCHG, BCLR, BSET (PRM Table 3-6; UM Table 8-8).
2
+
3
+ The bit number comes from Dn (dynamic) or from the word after the opcode
4
+ (static). On a data register the operation is on the whole long and the
5
+ number is taken mod 32; on memory it is on a byte, mod 8 (PRM 4, BTST). Z
6
+ is set if the tested bit was 0; nothing else changes. On a register the
7
+ changing forms cost 2 clocks more for bits 16-31 (UM Table 8-8 note), and
8
+ BCLR 2 more again (SST BCLR, T3).
9
+ """
10
+
11
+ from m68000_python._core import Z
12
+ from m68000_python._ea import DN, EA_KIND, IMM
13
+
14
+ # The operations, by bits 7-6 of the opcode: test, change, clear, set.
15
+ TEST, CHANGE, CLEAR, SET = range(4)
16
+
17
+
18
+ class BitsMixin:
19
+ """Private bit-manipulation implementation."""
20
+
21
+ def _bit_operation(self, opcode: int, operation: int) -> None:
22
+ if opcode & 0x0100:
23
+ number = self.R[(opcode >> 9) & 7]
24
+ else:
25
+ self._commit_pc()
26
+ number = self._extension() & 0xFF
27
+ kind = EA_KIND[opcode & 0x3F]
28
+ register = opcode & 7
29
+ if kind == DN:
30
+ number &= 31
31
+ value = self.R[register]
32
+ self._set_z(value, number)
33
+ self._prefetch()
34
+ if operation == TEST:
35
+ self._cycles += 2
36
+ return
37
+ self._cycles += 4 if operation == CLEAR else 2
38
+ if number >= 16:
39
+ self._cycles += 2
40
+ self.R[register] = self._apply_bit(value, number, operation)
41
+ return
42
+ number &= 7
43
+ if kind == IMM: # BTST Dn,#data: the operand is the byte after the opcode
44
+ value = self._extension() & 0xFF
45
+ self._set_z(value, number)
46
+ self._prefetch()
47
+ self._cycles += 2
48
+ return
49
+ address, value = self._ea_fetch(kind, register, 1)
50
+ self._set_z(value, number)
51
+ self._prefetch()
52
+ if operation != TEST:
53
+ self._write_byte(address, self._apply_bit(value, number, operation))
54
+
55
+ def _set_z(self, value: int, number: int) -> None:
56
+ if value >> number & 1:
57
+ self.SR &= ~Z
58
+ else:
59
+ self.SR |= Z
60
+
61
+ @staticmethod
62
+ def _apply_bit(value: int, number: int, operation: int) -> int:
63
+ bit = 1 << number
64
+ if operation == CHANGE:
65
+ return value ^ bit
66
+ if operation == CLEAR:
67
+ return value & ~bit
68
+ return value | bit
69
+
70
+ def _op_btst(self, opcode: int) -> None:
71
+ """BTST -- Z <- NOT bit n of the destination (PRM 4-62; UM Table 8-8).
72
+
73
+ Bus order, the 2 idle clocks of the register form, and the PC an address
74
+ error stacks: SST BTST.
75
+ """
76
+ self._bit_operation(opcode, TEST)
77
+
78
+ def _op_bchg(self, opcode: int) -> None:
79
+ """BCHG -- Z <- NOT bit n, then invert bit n (PRM 4-28; UM Table 8-8).
80
+
81
+ On a register 6 clocks, 8 for bits 16-31 (UM Table 8-8's note); bus
82
+ order and the PC an address error stacks: SST BCHG.
83
+ """
84
+ self._bit_operation(opcode, CHANGE)
85
+
86
+ def _op_bclr(self, opcode: int) -> None:
87
+ """BCLR -- Z <- NOT bit n, then clear bit n (PRM 4-31; UM Table 8-8).
88
+
89
+ On a register 8 clocks, 10 for bits 16-31, 2 more than BCHG and BSET at
90
+ every bit; bus order and the PC an address error stacks: SST BCLR.
91
+ """
92
+ self._bit_operation(opcode, CLEAR)
93
+
94
+ def _op_bset(self, opcode: int) -> None:
95
+ """BSET -- Z <- NOT bit n, then set bit n (PRM 4-57; UM Table 8-8).
96
+
97
+ On a register 6 clocks, 8 for bits 16-31; bus order and the PC an
98
+ address error stacks: SST BSET.
99
+ """
100
+ self._bit_operation(opcode, SET)
@@ -0,0 +1,208 @@
1
+ """Program control: branches, jumps, returns, DBcc, Scc, NOP (PRM Table 3-9).
2
+
3
+ A taken branch or jump throws the prefetch queue away and refills it from
4
+ the target with two program reads (UM Table 8-9, "What prefetch does to
5
+ these numbers" in docs/timing.md). A target at an odd address faults on the
6
+ first of those reads.
7
+ """
8
+
9
+ from m68000_python._ea import (
10
+ ABSL,
11
+ ABSW,
12
+ DISP,
13
+ DN,
14
+ EA_KIND,
15
+ IND,
16
+ INDEX,
17
+ PCDISP,
18
+ PCINDEX,
19
+ sign_extend_8,
20
+ sign_extend_16,
21
+ word_to_long,
22
+ )
23
+ from m68000_python._flags import CONDITION
24
+
25
+
26
+ class ControlMixin:
27
+ """Private program-control implementation."""
28
+
29
+ # -- branches (PRM 4, Bcc/BRA/BSR; UM Table 8-9) -------------------------
30
+
31
+ def _branch_target(self, opcode: int) -> int:
32
+ """The target of a Bcc/BRA/BSR: displacement from the extension word's address.
33
+
34
+ An 8-bit displacement of 0 means a 16-bit one follows; $FF is an
35
+ ordinary -1 on the 68000 (the 68020's long form does not exist;
36
+ corpus issue #4).
37
+ """
38
+ base = self._pc - 2 # the address of the word after the opcode
39
+ displacement = opcode & 0xFF
40
+ if displacement:
41
+ return (base + sign_extend_8(displacement)) & 0xFFFFFFFF
42
+ return (base + sign_extend_16(self.irc)) & 0xFFFFFFFF
43
+
44
+ def _op_bra(self, opcode: int) -> None:
45
+ """BRA -- branch always: PC <- PC + d (PRM 4-25; UM Table 8-9: 10 clocks)."""
46
+ self._cycles += 2
47
+ self._jump(self._branch_target(opcode))
48
+
49
+ def _op_bcc(self, opcode: int) -> None:
50
+ """Bcc -- branch if the condition holds (PRM 4-25; UM Table 8-9).
51
+
52
+ Taken: 10 clocks, two internal then the refill. Not taken with an
53
+ 8-bit displacement: 8 clocks; with a 16-bit one, 12, the extra read
54
+ skipping the displacement word.
55
+ """
56
+ if CONDITION[(opcode >> 8) & 0xF][self.SR & 0xF]:
57
+ self._cycles += 2
58
+ self._jump(self._branch_target(opcode))
59
+ return
60
+ self._cycles += 4
61
+ if not opcode & 0xFF:
62
+ self._extension()
63
+ self._prefetch()
64
+
65
+ def _op_bsr(self, opcode: int) -> None:
66
+ """BSR -- push the return address, then branch (PRM 4-59; UM Table 8-9: 18 clocks).
67
+
68
+ A fault on the refill stacks the target itself: SST BSR.
69
+ """
70
+ target = self._branch_target(opcode)
71
+ # The return address is past the displacement word, when there is one.
72
+ returns = self._pc if not opcode & 0xFF else self._pc - 2
73
+ self._cycles += 2
74
+ self._push_long(returns)
75
+ self._fault_pc = target # a fault on the refill stacks the target
76
+ self._jump(target)
77
+
78
+ # -- DBcc and Scc (PRM 4-91, 4-173) ---------------------------------------
79
+
80
+ def _op_dbcc(self, opcode: int) -> None:
81
+ """DBcc -- if cc is false, decrement Dn.w; branch unless it is -1 (PRM 4-91; UM Table 8-9).
82
+
83
+ UM Table 8-9: condition true 12 clocks; false and branching 10; false
84
+ with the count expired 14.
85
+
86
+ The branch target's word is read before Dn is written, so an odd target
87
+ faults with Dn unchanged and stacks the instruction's address + 4:
88
+ SST DBcc. WinUAE run stacks the target + 2 instead: contested, the core
89
+ follows the gate (docs/claims.md).
90
+ """
91
+ register = opcode & 7
92
+ if CONDITION[(opcode >> 8) & 0xF][self.SR & 0xF]:
93
+ self._cycles += 4
94
+ self._extension()
95
+ self._prefetch()
96
+ return
97
+ # Condition false: the word at the branch target is read whether or
98
+ # not the count expires, and Dn is written only after that read, so
99
+ # an odd target faults with Dn unchanged.
100
+ self._cycles += 2
101
+ target = (self._pc - 2 + sign_extend_16(self.irc)) & 0xFFFFFFFF
102
+ self._commit_pc()
103
+ count = (self.R[register] - 1) & 0xFFFF
104
+ self.irc = self._read_program_word(target)
105
+ if count != 0xFFFF:
106
+ self._pc = (target + 2) & 0xFFFFFFFF
107
+ else:
108
+ self._extension() # expired: carry on after the displacement word
109
+ self.R[register] = (self.R[register] & 0xFFFF0000) | count
110
+ self._prefetch()
111
+
112
+ def _op_scc(self, opcode: int) -> None:
113
+ """Scc -- set a byte to $FF if the condition holds, else $00 (PRM 4-173; UM Table 8-6).
114
+
115
+ UM Table 8-6: register 4 clocks false, 6 true; memory 8 plus the EA,
116
+ the operand read before it is written. The read, the refill between it
117
+ and the write, and the PC an address error stacks: SST Scc.
118
+ """
119
+ value = 0xFF if CONDITION[(opcode >> 8) & 0xF][self.SR & 0xF] else 0
120
+ kind = EA_KIND[opcode & 0x3F]
121
+ register = opcode & 7
122
+ if kind == DN:
123
+ self._prefetch()
124
+ if value:
125
+ self._cycles += 2
126
+ self.R[register] = (self.R[register] & 0xFFFFFF00) | value
127
+ return
128
+ address = self._ea_address(kind, register, 1)
129
+ self._read_byte(address)
130
+ self._prefetch()
131
+ self._write_byte(address, value)
132
+
133
+ # -- jumps and returns ----------------------------------------------------
134
+
135
+ def _control_address(self, kind: int, register: int) -> int:
136
+ """The address of a control-mode operand, for JMP/JSR/LEA/PEA (UM Table 8-10).
137
+
138
+ Unlike an operand read, these modes do not take the extension word
139
+ from the queue as they go: the refill happens later, as part of the
140
+ jump or the next step. Internal clocks are the table's.
141
+ """
142
+ R = self.R
143
+ if kind == IND:
144
+ return R[8 + register]
145
+ if kind == DISP:
146
+ return (R[8 + register] + sign_extend_16(self.irc)) & 0xFFFFFFFF
147
+ if kind == INDEX:
148
+ return self._indexed(R[8 + register], self.irc)
149
+ if kind == ABSW:
150
+ return word_to_long(self.irc)
151
+ if kind == ABSL:
152
+ high = self._extension()
153
+ return ((high << 16) | self.irc) & 0xFFFFFFFF
154
+ if kind == PCDISP:
155
+ return (self._pc - 2 + sign_extend_16(self.irc)) & 0xFFFFFFFF
156
+ if kind == PCINDEX:
157
+ return self._indexed(self._pc - 2, self.irc)
158
+ raise AssertionError(f"not a control kind: {kind}")
159
+
160
+ def _op_jmp(self, opcode: int) -> None:
161
+ """JMP -- PC <- effective address (PRM 4-108; UM Table 8-10)."""
162
+ kind = EA_KIND[opcode & 0x3F]
163
+ target = self._control_address(kind, opcode & 7)
164
+ if kind in (DISP, ABSW, PCDISP):
165
+ self._cycles += 2
166
+ elif kind in (INDEX, PCINDEX):
167
+ self._cycles += 6
168
+ self._jump(target)
169
+
170
+ def _op_jsr(self, opcode: int) -> None:
171
+ """JSR -- push the return address, PC <- effective address (PRM 4-109; UM Table 8-10).
172
+
173
+ The target's first word is read before the push, so an odd target faults
174
+ with nothing pushed: SST JSR. Through (d16,An), (d8,An,Xn), (d16,PC) and
175
+ (d8,PC,Xn) WinUAE run stacks 2 less than the gate: contested, the core
176
+ follows the gate (docs/claims.md).
177
+ """
178
+ kind = EA_KIND[opcode & 0x3F]
179
+ target = self._control_address(kind, opcode & 7)
180
+ # The return address is past the extension words.
181
+ returns = self._pc - 2 if kind == IND else self._pc
182
+ if kind in (DISP, ABSW, PCDISP):
183
+ self._cycles += 2
184
+ elif kind in (INDEX, PCINDEX):
185
+ self._cycles += 6
186
+ if kind != IND:
187
+ self._commit_pc()
188
+ # The target's first word is read before the push, so an odd target
189
+ # faults with nothing pushed.
190
+ target &= 0xFFFFFFFF
191
+ self.irc = self._read_program_word(target)
192
+ self._pc = (target + 2) & 0xFFFFFFFF
193
+ self._push_long(returns)
194
+ self._prefetch()
195
+
196
+ def _op_rts(self, opcode: int) -> None:
197
+ """RTS -- PC <- (SP)+ (PRM 4-169; UM Table 8-12: 16 clocks)."""
198
+ self._jump(self._pop_long())
199
+
200
+ def _op_rtr(self, opcode: int) -> None:
201
+ """RTR -- CCR <- (SP)+, then PC <- (SP)+ (PRM 4-168; UM Table 8-12: 20 clocks)."""
202
+ ccr = self._pop_word()
203
+ self._set_ccr(ccr)
204
+ self._jump(self._pop_long())
205
+
206
+ def _op_nop(self, opcode: int) -> None:
207
+ """NOP -- no operation (PRM 4-147; UM Table 8-12: 4 clocks, the prefetch)."""
208
+ self._prefetch()