-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathopcodes_6502.py
124 lines (95 loc) · 3.56 KB
/
opcodes_6502.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
from typing import Iterable, Tuple
import numpy
class Opcode:
"""6502 assembly language opcode with cycle length"""
def __init__(self, cycles, num_bytes, asm="", indent=4, toggle=False):
self.cycles = cycles
self.bytes = num_bytes
self.asm = asm
self.indent = indent
# The opcode toggles speaker (only) on the last cycle
self.toggle = toggle
def __repr__(self):
asm = "[%s] " % self.asm if self.asm else ""
return "%s<%d cycles>" % (asm, self.cycles)
def __str__(self):
indent = " " * self.indent
comment = ' ; %d cycles' % self.cycles if self.cycles else ""
return indent + self.asm + comment
class Literal(Opcode):
def __init__(self, asm, indent=4):
super(Literal, self).__init__(
cycles=0, num_bytes=0, asm=asm, indent=indent)
def __repr__(self):
return "<literal>"
class PaddingOpcode(Opcode):
"""Opcode variant that can be replaced by other interleaved opcodes."""
pass
STA_C030 = Opcode(4, 3, "STA $C030", toggle=True)
# TODO: support 6502 cycle timings (5 cycles instead of 6)
JMP_WDATA = Opcode(6, 3, "JMP (WDATA)")
def padding(cycles):
return PaddingOpcode(cycles, None, "; pad %d cycles" % cycles)
def nops(cycles: int) -> Iterable[Opcode]:
if cycles < 2:
raise ValueError
while cycles:
if cycles == 3:
yield Opcode(3, 2, "STA zpdummy")
cycles -= 3
continue
yield Opcode(2, 1, "NOP")
cycles -= 2
def interleave_opcodes(
base_opcodes: Iterable[Opcode],
interleaved_opcodes: Iterable[Opcode]) -> Iterable[Opcode]:
for op in base_opcodes:
if isinstance(op, PaddingOpcode):
padding_cycles = op.cycles
while padding_cycles > 0:
if interleaved_opcodes:
interleaved_op = interleaved_opcodes[0]
if (padding_cycles - interleaved_op.cycles) >= 0 and (
padding_cycles - interleaved_op.cycles) != 1:
yield interleaved_op
padding_cycles -= interleaved_op.cycles
interleaved_opcodes = interleaved_opcodes[1::]
if not interleaved_opcodes:
return
continue
if padding_cycles == 3:
yield PaddingOpcode(3, 2, "STA zpdummy")
padding_cycles -= 3
else:
yield PaddingOpcode(2, 1, "NOP")
padding_cycles -= 2
assert padding_cycles == 0
else:
yield op
if interleaved_opcodes:
print(interleaved_opcodes)
raise OverflowError
def total_bytes(opcodes: Iterable[Opcode]) -> int:
return sum(op.bytes for op in opcodes)
def total_cycles(opcodes: Iterable[Opcode]) -> int:
return sum(op.cycles for op in opcodes)
def voltages(opcodes: Iterable[Opcode]) -> Tuple[float]:
res = []
speaker = 1.0
for op in opcodes:
if not op.cycles:
continue
res.extend([speaker] * (op.cycles - 1))
if op.toggle:
speaker *= -1
res.append(speaker)
return tuple(res)
def join_voltages(op_seq: Iterable[Iterable[Opcode]]) -> numpy.ndarray:
res = []
last_voltage = 1.0
for ops in op_seq:
op_voltages = voltages(ops)
res.extend(
numpy.array(op_voltages, dtype=numpy.float32) * last_voltage)
last_voltage = res[-1]
return numpy.array(res, dtype=numpy.float32)