From bc032ae3ca4c359847cf2a75d47e4e06ee1440da Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 21 Jun 2022 22:32:12 +0100 Subject: [PATCH] Autogenerate eof with speaker opcode interleaving --- opcodes_6502.py | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ waveform.py | 46 ++++++++----- 2 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 opcodes_6502.py diff --git a/opcodes_6502.py b/opcodes_6502.py new file mode 100644 index 0000000..3f35716 --- /dev/null +++ b/opcodes_6502.py @@ -0,0 +1,168 @@ +import itertools +from typing import Iterable, List + + +class Opcode: + """6502 assembly language opcode with cycle length""" + + def __init__(self, cycles, asm="", indent=4, toggle=False): + self.cycles = cycles + self.asm = asm + self.indent = indent + # Assume toggles speaker 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, asm=asm, indent=indent) + + def __repr__(self): + return "" + + +class PaddingOpcode(Opcode): + """Opcode variant that can be replaced by other interleaved opcodes.""" + pass + + +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, "STA zpdummy") + padding_cycles -= 3 + else: + yield PaddingOpcode(2, "NOP") + padding_cycles -= 2 + assert padding_cycles == 0 + else: + yield op + if interleaved_opcodes: + print(interleaved_opcodes) + raise OverflowError + + +STA_C030 = Opcode(4, "STA $C030", toggle=True) + + +def padding(cycles): + return PaddingOpcode(cycles, "; pad %d cycles" % cycles) + + +CORE_EOF = [ + Literal( + "; We've read exactly 2KB from the socket buffer. Before continuing " + "we need to ACK this read,"), + Literal("; and make sure there's at least another 2KB in the buffer."), + Literal(";"), + Literal("; Save the W5100 address pointer so we can continue reading the " + "socket buffer once we are done."), + Literal( + "; We know the low-order byte is 0 because Socket RX memory is " + "page-aligned and so is 2K frame."), + Literal( + "; IMPORTANT - from now on until we restore this below, we can't " + "trash the Y register!"), + Opcode(4, "LDY WADRH"), + Literal("; Update new Received Read pointer."), + Literal(";"), + Literal( + "; We know we have received exactly 2KB, so we don't need to read the " + "current value from the"), + Literal("; hardware. We can track it ourselves instead, which saves a " + "few cycles."), + Opcode(2, "LDA #>S0RXRD"), + Opcode(4, "STA WADRH"), + Opcode(2, "LDA # List[bool]: + res = [] + speaker = True + for op in opcodes: + if not op.cycles: + continue + res.extend([speaker] * (op.cycles - 1)) + if op.toggle: + speaker = not speaker + res.append(speaker) + return res + + +base = itertools.cycle([STA_C030, PaddingOpcode(6)]) + +eof = list(interleave_opcodes(base, CORE_EOF)) +for op in eof: + print(op) + +print(toggles(eof)) diff --git a/waveform.py b/waveform.py index 2af3c7c..6958721 100644 --- a/waveform.py +++ b/waveform.py @@ -3,19 +3,13 @@ import random import soundfile as sf -def wave(count: int): - freq = 3875 - dt = 1 / 44100. - damping = -1210 # -0.015167 +def params(freq, damping, dt): w = freq * 2 * math.pi * dt d = damping * dt e = math.exp(d) c1 = 2 * e * math.cos(w) c2 = e * e - y1 = y2 = 0 - x1 = 0 - x2 = 0 t0 = (1 - 2 * e * math.cos(w) + e * e) / (d * d + w * w) t = d * d + w * w - math.pi * math.pi t1 = (1 + 2 * e * math.cos(w) + e * e) / math.sqrt(t * t + 4 * d * d * @@ -23,20 +17,42 @@ def wave(count: int): b2 = (t1 - t0) / (t1 + t0) b1 = b2 * dt * dt * (t0 + t1) / 2 + return c1, c2, b1, b2 + + +def wave(count: int): + freq = 3875 + dt = 1 / 44100. + damping = -1210 # -0.015167 + + c1, c2, b1, b2 = params(freq, damping, dt) + + # freq2 = 525 + # damping2 = -130 + # + # cc1, cc2, bb1, bb2 = params(freq2, damping2, dt) + # mult2 = 1# 0.11 + + y1 = y2 = 0 + x1 = 0 + x2 = 0 + # tm = math.atan(w/d)/w # scale = 500 * math.sqrt(d * d+ w * w) * math.exp(-d * tm) / (dt * 2000) x1 = 1.0 - th = 44100/3875/2 + th = 44100 switch = th scale = 3 # TODO: analytic expression maxy = 0 for i in range(count): - y = c1 * y1 - c2 * y2 + b1 * x1 + b2 * x2 + # y = (c1 * y1 - c2 * y2 + b1 * x1 + b2 * x2) + mult2 * ( + # 1 - cc1 * y1 + cc2 * y2 - bb1 * x1 - bb2 * x2) + y = (c1 * y1 - c2 * y2 + b1 * x1 + b2 * x2) x2 = x1 - # if i >= switch: - # x1 = -x1 - # switch += th + if i >= switch: + x1 = -x1 + switch += th y2 = y1 y1 = y if math.fabs(y) > maxy: @@ -46,9 +62,9 @@ def wave(count: int): def main(): - print(list(wave(1020400))) - # with sf.SoundFile("out.wav", "w", samplerate=44100, channels=1) as f: - # f.write(list(wave(441000))) + # print(list(wave(1020400))) + with sf.SoundFile("out.wav", "w", samplerate=44100, channels=1) as f: + f.write(list(wave(441000))) if __name__ == "__main__":