Autogenerate eof with speaker opcode interleaving

This commit is contained in:
kris 2022-06-21 22:32:12 +01:00
parent cb5de62cc8
commit bc032ae3ca
2 changed files with 199 additions and 15 deletions

168
opcodes_6502.py Normal file
View File

@ -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 "<literal>"
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 #<S0RXRD"),
Opcode(4, "STA WADRL"),
Opcode(4, "LDA RXRD ; TODO: in principle we could update RXRD outside of "
"the EOF path"),
Opcode(2, "CLC"),
Opcode(2, "ADC #$08"),
Opcode(4, "STA WDATA ; Store new high byte of received read pointer"),
Opcode(4, "STA RXRD ; Save for next time"),
Literal("; Send the Receive command"),
Opcode(2, "LDA #<S0CR"),
Opcode(4, "STA WADRL"),
Opcode(2, "LDA #SCRECV"),
Opcode(4, "STA WDATA"),
Literal(
"; Make sure we have at least 2KB more in the socket buffer so we can "
"start another frame."
),
Opcode(2, "LDA #$07 ; 2"),
Opcode(2, "LDX #<S0RXRSR ; Socket 0 Received Size register"),
Literal(
"; we might loop an unknown number of times here waiting for data but "
"the default should be to"),
Literal("; fall straight through"),
Literal("@0:", indent=0),
Opcode(4, "STX WADRL"),
Opcode(4, "CMP WDATA ; High byte of received size"),
Opcode(2,
"BCS @0 ; 2 cycles in common case when there is already sufficient "
"data waiting."),
Literal(
"; We're good to go for another frame. Restore W5100 address pointer "
"where we last found it, to"),
Literal("; begin iterating through the next 2KB of the socket buffer."),
Literal(";"),
Literal(
"; It turns out that the W5100 automatically wraps the address pointer "
"at the end of the 8K"),
Literal(
"; RX/TX buffers. Since we're using an 8K socket, that means we don't "
"have to do any work to"),
Literal("; manage the read pointer!"),
Opcode(4, "STY WADRH"),
Opcode(2, "LDA #$00"),
Opcode(4, "STA WADRL"),
Opcode(6, "JMP (WDATA)"),
]
def toggles(opcodes: Iterable[Opcode]) -> 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))

View File

@ -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__":