WIP - refactor player generation to be a) deterministic b) use opcodes_6502 c) statically enumerate a minimal set of opcode sequences. Since we're giving up on using arbitrary opcode combinations this is much simpler, and maybe more correct.
This commit is contained in:
parent
bc032ae3ca
commit
e0908a9ced
|
@ -4,278 +4,368 @@ import itertools
|
|||
import numpy
|
||||
from typing import Iterable, List, Tuple
|
||||
|
||||
import opcodes_6502
|
||||
|
||||
class Opcode(enum.Enum):
|
||||
NOP = 1
|
||||
NOP3 = 2
|
||||
STA = 3
|
||||
STAX = 4
|
||||
INC = 5
|
||||
INCX = 6
|
||||
JMP_INDIRECT = 7
|
||||
NOPNOP = 8
|
||||
|
||||
|
||||
# Byte length of opcodes
|
||||
OPCODE_LEN = {
|
||||
Opcode.NOP: 1, Opcode.NOP3: 2, Opcode.STA: 3, Opcode.STAX: 3,
|
||||
Opcode.INC: 3, Opcode.INCX: 3, Opcode.JMP_INDIRECT: 3, Opcode.NOPNOP: 2
|
||||
}
|
||||
|
||||
ASM = {
|
||||
Opcode.NOP: "NOP", Opcode.NOP3: "STA zpdummy",
|
||||
Opcode.STA: "STA $C030", Opcode.STAX: "STA $C030,X",
|
||||
Opcode.INC: "INC $C030", Opcode.INCX: "INC $C030,X",
|
||||
Opcode.JMP_INDIRECT: "JMP (WDATA)", Opcode.NOPNOP: "NOP NOP"
|
||||
}
|
||||
|
||||
# Applied speaker voltages resulting from executing opcode
|
||||
VOLTAGES = {
|
||||
Opcode.STA: [1, 1, 1, -1],
|
||||
Opcode.INC: [1, 1, 1, -1, 1, -1],
|
||||
Opcode.INCX: [1, 1, 1, -1, 1, -1, 1],
|
||||
Opcode.STAX: [1, 1, 1, -1, 1],
|
||||
Opcode.NOP: [1, 1],
|
||||
Opcode.NOP3: [1, 1, 1],
|
||||
# TODO: support 6502 cycle counts as well
|
||||
Opcode.JMP_INDIRECT: [1, 1, 1, 1, 1, 1],
|
||||
Opcode.NOPNOP: [1, 1, 1, 1],
|
||||
}
|
||||
|
||||
|
||||
def voltage_sequence(
|
||||
opcodes: Iterable[Opcode], starting_voltage=1.0
|
||||
) -> Tuple[numpy.float32, int]:
|
||||
"""Voltage sequence for sequence of opcodes."""
|
||||
out = []
|
||||
v = starting_voltage
|
||||
toggles = 0
|
||||
for op in opcodes:
|
||||
for nv in v * numpy.array(VOLTAGES[op]):
|
||||
if v != nv:
|
||||
toggles += 1
|
||||
v = nv
|
||||
out.append(v)
|
||||
return tuple(numpy.array(out, dtype=numpy.float32)), toggles
|
||||
|
||||
|
||||
def all_opcodes(
|
||||
max_len: int, opcodes: Iterable[Opcode], start_opcodes: Iterable[int]
|
||||
) -> Iterable[Tuple[Opcode]]:
|
||||
"""Enumerate all combinations of opcodes up to max_len cycles"""
|
||||
num_opcodes = 0
|
||||
while True:
|
||||
found_one = False
|
||||
for ops in itertools.product(opcodes, repeat=num_opcodes):
|
||||
ops = tuple(list(ops) + [Opcode.JMP_INDIRECT])
|
||||
if ops[0] not in start_opcodes:
|
||||
continue
|
||||
if sum(len(VOLTAGES[o]) for o in ops) <= max_len:
|
||||
found_one = True
|
||||
yield ops
|
||||
if not found_one:
|
||||
break
|
||||
num_opcodes += 1
|
||||
|
||||
|
||||
def _make_end_of_frame_voltages(cycles) -> numpy.ndarray:
|
||||
"""Voltage sequence for end-of-frame TCP processing."""
|
||||
c = []
|
||||
voltage_high = True
|
||||
for i, skip_cycles in enumerate(cycles):
|
||||
c.extend([1.0 if voltage_high else -1.0] * (skip_cycles - 1))
|
||||
if i != len(cycles) - 1:
|
||||
voltage_high = not voltage_high
|
||||
c.append(1.0 if voltage_high else -1.0)
|
||||
return numpy.array(c, dtype=numpy.float32)
|
||||
|
||||
|
||||
# These are duty cycles
|
||||
eof_cycles = [
|
||||
# (16,6),
|
||||
# (14,6),
|
||||
# (12,8), # -0.15
|
||||
# (14, 10), # -0.10
|
||||
# (12,10), # -0.05
|
||||
# (4, 40, 4, 40, 4, 40, 4, 6),
|
||||
# (4, 38, 6, 38, 6, 38, 6, 6),
|
||||
# (4, 36, 8, 36, 8, 36, 8, 6),
|
||||
# (4, 34, 10, 34, 10, 34, 10, 6),
|
||||
# (4, 32, 12, 32, 12, 32, 12, 6),
|
||||
# (4, 30, 14, 30, 14, 30, 14, 6),
|
||||
(4, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 6), # 0.0
|
||||
(4, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 6), # 0.046
|
||||
(4, 24, 20, 24, 20, 24, 20, 6), # 0.09
|
||||
(4, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 6), # 0.11
|
||||
(4, 13, 10, 13, 10, 13, 10, 13, 10, 13, 10, 13, 10, 13, 6), # 0.13
|
||||
(4, 28, 20, 28, 20, 28, 20, 6), # 0.166
|
||||
(4, 26, 18, 26, 18, 26, 18, 6), # 0.18
|
||||
(4, 24, 16, 24, 16, 24, 16, 6), # 0.2
|
||||
|
||||
# (10, 8, 10, 10, 10, 8), # 0.05
|
||||
# (12, 10, 12, 8, 10, 10), # 0.1
|
||||
# (4, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 6), # 0.15
|
||||
# (10, 6, 12, 6), # 0.20
|
||||
# (10, 4), # 0.25
|
||||
# (14, 4, 10, 6), # 0.30
|
||||
# (12, 4), # 0.35
|
||||
# (14, 4), # 0.40
|
||||
SLOW_PATH_TRAMPOLINE = [
|
||||
opcodes_6502.Literal("eof_slow_path:", indent=0),
|
||||
opcodes_6502.STA_C030,
|
||||
opcodes_6502.Opcode(4, 3, "LDX WDATA"),
|
||||
opcodes_6502.Opcode(4, 3, "STX @jmp+1"),
|
||||
opcodes_6502.Literal("@jmp:", indent=0),
|
||||
opcodes_6502.Opcode(6, 3, "JMP ($2000)") # TODO: 5 cycles on 6502
|
||||
]
|
||||
|
||||
import itertools
|
||||
SLOW_PATH_EOF = [
|
||||
opcodes_6502.Literal(
|
||||
"; We've read exactly 2KB from the socket buffer. Before continuing "
|
||||
"we need to ACK this read,"),
|
||||
opcodes_6502.Literal(
|
||||
"; and make sure there's at least another 2KB in the buffer."),
|
||||
opcodes_6502.Literal(";"),
|
||||
opcodes_6502.Literal(
|
||||
"; Save the W5100 address pointer so we can continue reading the "
|
||||
"socket buffer once we are done."),
|
||||
opcodes_6502.Literal(
|
||||
"; We know the low-order byte is 0 because Socket RX memory is "
|
||||
"page-aligned and so is 2K frame."),
|
||||
opcodes_6502.Literal(
|
||||
"; IMPORTANT - from now on until we restore this below, we can't "
|
||||
"trash the Y register!"),
|
||||
opcodes_6502.Opcode(4, 4, "LDY WADRH"),
|
||||
opcodes_6502.Literal("; Update new Received Read pointer."),
|
||||
opcodes_6502.Literal(";"),
|
||||
opcodes_6502.Literal(
|
||||
"; We know we have received exactly 2KB, so we don't need to read the "
|
||||
"current value from the"),
|
||||
opcodes_6502.Literal(
|
||||
"; hardware. We can track it ourselves instead, which saves a "
|
||||
"few cycles."),
|
||||
opcodes_6502.Opcode(2, 2, "LDA #>S0RXRD"),
|
||||
opcodes_6502.Opcode(4, 3, "STA WADRH"),
|
||||
opcodes_6502.Opcode(2, 2, "LDA #<S0RXRD"),
|
||||
opcodes_6502.Opcode(4, 3, "STA WADRL"),
|
||||
opcodes_6502.Opcode(4, 3,
|
||||
"LDA RXRD ; TODO: in principle we could update RXRD outside of "
|
||||
"the EOF path"),
|
||||
opcodes_6502.Opcode(2, 1, "CLC"),
|
||||
opcodes_6502.Opcode(2, 2, "ADC #$08"),
|
||||
opcodes_6502.Opcode(4, 3,
|
||||
"STA WDATA ; Store new high byte of received read pointer"),
|
||||
opcodes_6502.Opcode(4, 3, "STA RXRD ; Save for next time"),
|
||||
opcodes_6502.Literal("; Send the Receive command"),
|
||||
opcodes_6502.Opcode(2, 2, "LDA #<S0CR"),
|
||||
opcodes_6502.Opcode(4, 3, "STA WADRL"),
|
||||
opcodes_6502.Opcode(2, 2, "LDA #SCRECV"),
|
||||
opcodes_6502.Opcode(4, 3, "STA WDATA"),
|
||||
opcodes_6502.Literal(
|
||||
"; Make sure we have at least 2KB more in the socket buffer so we can "
|
||||
"start another frame."
|
||||
),
|
||||
opcodes_6502.Opcode(2, 2, "LDA #$07"),
|
||||
opcodes_6502.Opcode(2, 2, "LDX #<S0RXRSR ; Socket 0 Received Size "
|
||||
"register"),
|
||||
opcodes_6502.Literal(
|
||||
"; we might loop an unknown number of times here waiting for data but "
|
||||
"the default should be to"),
|
||||
opcodes_6502.Literal("; fall straight through"),
|
||||
opcodes_6502.Literal("@0:", indent=0),
|
||||
opcodes_6502.Opcode(4, 3, "STX WADRL"),
|
||||
opcodes_6502.Opcode(4, 3, "CMP WDATA ; High byte of received size"),
|
||||
opcodes_6502.Opcode(2, 2,
|
||||
"BCS @0 ; 2 cycles in common case when there is already sufficient "
|
||||
"data waiting."),
|
||||
opcodes_6502.Literal(
|
||||
"; We're good to go for another frame. Restore W5100 address pointer "
|
||||
"where we last found it, to"),
|
||||
opcodes_6502.Literal(
|
||||
"; begin iterating through the next 2KB of the socket buffer."),
|
||||
opcodes_6502.Literal(";"),
|
||||
opcodes_6502.Literal(
|
||||
"; It turns out that the W5100 automatically wraps the address pointer "
|
||||
"at the end of the 8K"),
|
||||
opcodes_6502.Literal(
|
||||
"; RX/TX buffers. Since we're using an 8K socket, that means we don't "
|
||||
"have to do any work to"),
|
||||
opcodes_6502.Literal("; manage the read pointer!"),
|
||||
opcodes_6502.Opcode(4, 3, "STY WADRH"),
|
||||
opcodes_6502.Opcode(2, 2, "LDA #$00"),
|
||||
opcodes_6502.Opcode(4, 3, "STA WADRL"),
|
||||
opcodes_6502.Opcode(6, 3, "JMP (WDATA)"),
|
||||
]
|
||||
|
||||
# Fast path really only requires 1 byte but we have to burn an extra one to
|
||||
# sync up to 2KB boundary
|
||||
FAST_PATH_EOF = [opcodes_6502.Opcode(4, 3, "LDA WDATA")] + SLOW_PATH_EOF
|
||||
|
||||
|
||||
def _make_end_of_frame_voltages2(cycles) -> numpy.ndarray:
|
||||
"""Voltage sequence for end-of-frame TCP processing."""
|
||||
max_len = 140
|
||||
voltage_high = False
|
||||
c = [1.0, 1.0, 1.0, -1.0] # STA $C030
|
||||
for i, skip_cycles in enumerate(itertools.cycle(cycles)):
|
||||
c.extend([1.0 if voltage_high else -1.0] * (skip_cycles - 1))
|
||||
voltage_high = not voltage_high
|
||||
c.append(1.0 if voltage_high else -1.0)
|
||||
if len(c) >= max_len:
|
||||
break
|
||||
c.extend([1.0 if voltage_high else -1.0] * 6) # JMP (WDATA)
|
||||
return numpy.array(c, dtype=numpy.float32)
|
||||
def fast_path_trampoline(label: str) -> List[opcodes_6502.Opcode]:
|
||||
return [
|
||||
opcodes_6502.Literal("eof_fast_path_%s:", indent=0),
|
||||
opcodes_6502.STA_C030,
|
||||
opcodes_6502.Opcode(3, 3, "JMP _eof_fast_path_%s" % label)
|
||||
]
|
||||
|
||||
|
||||
def _duty_cycles():
|
||||
res = {}
|
||||
# def _make_end_of_frame_voltages(cycles) -> numpy.ndarray:
|
||||
# """Voltage sequence for end-of-frame TCP processing."""
|
||||
# c = []
|
||||
# voltage_high = True
|
||||
# for i, skip_cycles in enumerate(cycles):
|
||||
# c.extend([1.0 if voltage_high else -1.0] * (skip_cycles - 1))
|
||||
# if i != len(cycles) - 1:
|
||||
# voltage_high = not voltage_high
|
||||
# c.append(1.0 if voltage_high else -1.0)
|
||||
# return numpy.array(c, dtype=numpy.float32)
|
||||
|
||||
for i in range(4, 50, 2):
|
||||
for j in range(i, 50, 2):
|
||||
if i + j < 20 or i + j > 50:
|
||||
continue
|
||||
duty = j / (i + j) * 2 - 1
|
||||
res.setdefault(duty, []).append((i + j, i, j))
|
||||
|
||||
cycles = []
|
||||
for c in sorted(list(res.keys())):
|
||||
pair = sorted(sorted(res[c], reverse=False)[0][1:], reverse=True)
|
||||
cycles.append(pair)
|
||||
|
||||
# return [(10, 10), (12, 10), (12, 8), (14, 10), (14, 6), (14, 8)]
|
||||
return cycles
|
||||
#
|
||||
# # These are duty cycles
|
||||
# eof_cycles = [
|
||||
# # (16,6),
|
||||
# # (14,6),
|
||||
# # (12,8), # -0.15
|
||||
# # (14, 10), # -0.10
|
||||
# # (12,10), # -0.05
|
||||
# # (4, 40, 4, 40, 4, 40, 4, 6),
|
||||
# # (4, 38, 6, 38, 6, 38, 6, 6),
|
||||
# # (4, 36, 8, 36, 8, 36, 8, 6),
|
||||
# # (4, 34, 10, 34, 10, 34, 10, 6),
|
||||
# # (4, 32, 12, 32, 12, 32, 12, 6),
|
||||
# # (4, 30, 14, 30, 14, 30, 14, 6),
|
||||
# (4, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 6), # 0.0
|
||||
# (4, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 6), # 0.046
|
||||
# (4, 24, 20, 24, 20, 24, 20, 6), # 0.09
|
||||
# (4, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 6), # 0.11
|
||||
# (4, 13, 10, 13, 10, 13, 10, 13, 10, 13, 10, 13, 10, 13, 6), # 0.13
|
||||
# (4, 28, 20, 28, 20, 28, 20, 6), # 0.166
|
||||
# (4, 26, 18, 26, 18, 26, 18, 6), # 0.18
|
||||
# (4, 24, 16, 24, 16, 24, 16, 6), # 0.2
|
||||
#
|
||||
# # (10, 8, 10, 10, 10, 8), # 0.05
|
||||
# # (12, 10, 12, 8, 10, 10), # 0.1
|
||||
# # (4, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 8, 10, 6), # 0.15
|
||||
# # (10, 6, 12, 6), # 0.20
|
||||
# # (10, 4), # 0.25
|
||||
# # (14, 4, 10, 6), # 0.30
|
||||
# # (12, 4), # 0.35
|
||||
# # (14, 4), # 0.40
|
||||
# ]
|
||||
#
|
||||
# import itertools
|
||||
#
|
||||
#
|
||||
# def _make_end_of_frame_voltages2(cycles) -> numpy.ndarray:
|
||||
# """Voltage sequence for end-of-frame TCP processing."""
|
||||
# max_len = 140
|
||||
# voltage_high = False
|
||||
# c = [1.0, 1.0, 1.0, -1.0] # STA $C030
|
||||
# for i, skip_cycles in enumerate(itertools.cycle(cycles)):
|
||||
# c.extend([1.0 if voltage_high else -1.0] * (skip_cycles - 1))
|
||||
# voltage_high = not voltage_high
|
||||
# c.append(1.0 if voltage_high else -1.0)
|
||||
# if len(c) >= max_len:
|
||||
# break
|
||||
# c.extend([1.0 if voltage_high else -1.0] * 6) # JMP (WDATA)
|
||||
# return numpy.array(c, dtype=numpy.float32)
|
||||
#
|
||||
#
|
||||
# def _duty_cycles():
|
||||
# res = {}
|
||||
#
|
||||
# for i in range(4, 50, 2):
|
||||
# for j in range(i, 50, 2):
|
||||
# if i + j < 20 or i + j > 50:
|
||||
# continue
|
||||
# duty = j / (i + j) * 2 - 1
|
||||
# res.setdefault(duty, []).append((i + j, i, j))
|
||||
#
|
||||
# cycles = []
|
||||
# for c in sorted(list(res.keys())):
|
||||
# pair = sorted(sorted(res[c], reverse=False)[0][1:], reverse=True)
|
||||
# cycles.append(pair)
|
||||
#
|
||||
# # return [(10, 10), (12, 10), (12, 8), (14, 10), (14, 6), (14, 8)]
|
||||
# return cycles
|
||||
#
|
||||
#
|
||||
# eof_cycles = _duty_cycles()
|
||||
|
||||
|
||||
eof_cycles = _duty_cycles()
|
||||
# def voltage_sequence(
|
||||
# opcodes: Iterable[Opcode], starting_voltage=1.0
|
||||
# ) -> Tuple[numpy.float32, int]:
|
||||
# """Voltage sequence for sequence of opcodes."""
|
||||
# out = []
|
||||
# v = starting_voltage
|
||||
# toggles = 0
|
||||
# for op in opcodes:
|
||||
# for nv in v * numpy.array(VOLTAGES[op]):
|
||||
# if v != nv:
|
||||
# toggles += 1
|
||||
# v = nv
|
||||
# out.append(v)
|
||||
# return tuple(numpy.array(out, dtype=numpy.float32)), toggles
|
||||
#
|
||||
#
|
||||
# def all_opcodes(
|
||||
# max_len: int, opcodes: Iterable[Opcode], start_opcodes: Iterable[int]
|
||||
# ) -> Iterable[Tuple[Opcode]]:
|
||||
# """Enumerate all combinations of opcodes up to max_len cycles"""
|
||||
# num_opcodes = 0
|
||||
# while True:
|
||||
# found_one = False
|
||||
# for ops in itertools.product(opcodes, repeat=num_opcodes):
|
||||
# ops = tuple(list(ops) + [Opcode.JMP_INDIRECT])
|
||||
# if ops[0] not in start_opcodes:
|
||||
# continue
|
||||
# if sum(len(VOLTAGES[o]) for o in ops) <= max_len:
|
||||
# found_one = True
|
||||
# yield ops
|
||||
# if not found_one:
|
||||
# break
|
||||
# num_opcodes += 1
|
||||
|
||||
|
||||
def generate_player(player_ops: List[Tuple[Opcode]], opcode_filename: str,
|
||||
player_filename: str):
|
||||
def audio_opcodes() -> Iterable[opcodes_6502.Opcode]:
|
||||
# Interleave 3 x STA_C030 with 0,2,4 intervening NOP cycles
|
||||
# We don't need to worry about 6 or more cycle paddings because
|
||||
# these can always be achieved by chaining JMP (WDATA) to itself
|
||||
for i in range(0, 6, 2):
|
||||
for j in range(0, 6, 2):
|
||||
ops = []
|
||||
# don't need to worry about 0 or 2 since they are subsequences
|
||||
ops.extend(opcodes_6502.nops(4))
|
||||
ops.append(opcodes_6502.STA_C030)
|
||||
if i:
|
||||
ops.extend(opcodes_6502.nops(i))
|
||||
ops.append(opcodes_6502.STA_C030)
|
||||
if j:
|
||||
ops.extend(opcodes_6502.nops(j))
|
||||
ops.append(opcodes_6502.STA_C030)
|
||||
ops.append(opcodes_6502.JMP_WDATA)
|
||||
yield tuple(ops)
|
||||
|
||||
# Add a NOP sled so we can more efficiently chain together longer
|
||||
# runs of NOPs without wasting bytes in the TCP frame chaining
|
||||
# together JMP (WDATA)
|
||||
yield tuple(
|
||||
[nop for nop in opcodes_6502.nops(20)] + [opcodes_6502.JMP_WDATA])
|
||||
|
||||
|
||||
def generate_player(
|
||||
player_ops: Iterable[Tuple[opcodes_6502.Opcode]],
|
||||
opcode_filename: str,
|
||||
player_filename: str
|
||||
):
|
||||
num_bytes = 0
|
||||
seqs = {}
|
||||
num_op = 0
|
||||
seen_op_suffix_toggles = set()
|
||||
offset = 0
|
||||
unique_opcodes = {}
|
||||
toggles = {}
|
||||
done = False
|
||||
with open(player_filename, "w+") as f:
|
||||
for i, k in enumerate(player_ops):
|
||||
new_unique = []
|
||||
for i, ops in enumerate(player_ops):
|
||||
unique_entrypoints = []
|
||||
player_op = []
|
||||
player_op_len = 0
|
||||
|
||||
new_offset = offset
|
||||
for j, o in enumerate(k):
|
||||
seq, tog = voltage_sequence(k[j:], 1.0)
|
||||
dup = seqs.setdefault(seq, i)
|
||||
if dup == i:
|
||||
player_op.append("tick_%02x: ; voltages %s" % (
|
||||
new_offset, seq))
|
||||
new_unique.append((new_offset, seq, tog))
|
||||
player_op.append(" %s" % ASM[o])
|
||||
player_op_len += OPCODE_LEN[o]
|
||||
new_offset += OPCODE_LEN[o]
|
||||
player_op.append("\n")
|
||||
for j, op in enumerate(ops):
|
||||
op_suffix_toggles = opcodes_6502.toggles(ops[j:])
|
||||
if op_suffix_toggles not in seen_op_suffix_toggles:
|
||||
# new subsequence
|
||||
seen_op_suffix_toggles.add(op_suffix_toggles)
|
||||
player_op.append(
|
||||
opcodes_6502.Literal(
|
||||
"tick_%02x: ; voltages %s" % (
|
||||
offset, op_suffix_toggles), indent=0))
|
||||
unique_entrypoints.append((offset, op_suffix_toggles))
|
||||
player_op.append(op)
|
||||
offset += op.bytes
|
||||
|
||||
# If at least one of the partial opcode sequences was not
|
||||
# a dup, then add it to the player
|
||||
if new_unique:
|
||||
# Reserve 9 bytes for END_OF_FRAME and EXIT
|
||||
if (num_bytes + player_op_len) > (256 - 9):
|
||||
print("Out of space, truncating.")
|
||||
break
|
||||
num_op += 1
|
||||
f.write("\n".join(player_op))
|
||||
num_bytes += player_op_len
|
||||
for op_offset, seq, tog in new_unique:
|
||||
unique_opcodes[op_offset] = seq
|
||||
toggles[op_offset] = tog
|
||||
offset = new_offset
|
||||
assert unique_entrypoints
|
||||
player_op_len = opcodes_6502.total_bytes(player_op)
|
||||
# Make sure we reserve 9 bytes for END_OF_FRAME and EXIT
|
||||
assert (num_bytes + player_op_len) <= (256 - 9)
|
||||
|
||||
for op in player_op:
|
||||
f.write("%s\n" % str(op))
|
||||
|
||||
num_bytes += player_op_len
|
||||
for op_offset, seq in unique_entrypoints:
|
||||
unique_opcodes[op_offset] = seq
|
||||
# toggles[op_offset] = tog
|
||||
f.write("\n")
|
||||
|
||||
f.write("; %d bytes\n" % num_bytes)
|
||||
|
||||
with open(opcode_filename, "w") as f:
|
||||
f.write("import enum\nimport numpy\n\n\n")
|
||||
f.write("class Opcode(enum.Enum):\n")
|
||||
for o in unique_opcodes.keys():
|
||||
f.write(" TICK_%02x = 0x%02x\n" % (o, o))
|
||||
f.write(" EXIT = 0x%02x\n" % num_bytes)
|
||||
# f.write(" END_OF_FRAME = 0x%02x\n" % (num_bytes + 3))
|
||||
for i, _ in enumerate(eof_cycles):
|
||||
f.write(" END_OF_FRAME_%d = 0x%02x\n" % (i, num_bytes + 4 + i))
|
||||
|
||||
f.write("\n\nVOLTAGE_SCHEDULE = {\n")
|
||||
for o, v in unique_opcodes.items():
|
||||
f.write(
|
||||
" Opcode.TICK_%02x: numpy.array(%s, dtype=numpy.float32),"
|
||||
"\n" % (o, v))
|
||||
for i, skip_cycles in enumerate(eof_cycles):
|
||||
f.write(" Opcode.END_OF_FRAME_%d: numpy.array([%s], "
|
||||
"dtype=numpy.float32), # %s\n" % (i, ", ".join(
|
||||
str(f) for f in _make_end_of_frame_voltages2(
|
||||
skip_cycles)), skip_cycles))
|
||||
f.write("}\n")
|
||||
|
||||
f.write("\n\nTOGGLES = {\n")
|
||||
for o, v in toggles.items():
|
||||
f.write(
|
||||
" Opcode.TICK_%02x: %d,\n" % (o, v)
|
||||
)
|
||||
f.write("}\n")
|
||||
|
||||
f.write("\n\nEOF_OPCODES = (\n")
|
||||
for i in range(len(eof_cycles)):
|
||||
f.write(" Opcode.END_OF_FRAME_%d,\n" % i)
|
||||
f.write(")\n")
|
||||
# with open(opcode_filename, "w") as f:
|
||||
# f.write("import enum\nimport numpy\n\n\n")
|
||||
# f.write("class Opcode(enum.Enum):\n")
|
||||
# for o in unique_opcodes.keys():
|
||||
# f.write(" TICK_%02x = 0x%02x\n" % (o, o))
|
||||
# f.write(" EXIT = 0x%02x\n" % num_bytes)
|
||||
# # f.write(" END_OF_FRAME = 0x%02x\n" % (num_bytes + 3))
|
||||
# for i, _ in enumerate(eof_cycles):
|
||||
# f.write(" END_OF_FRAME_%d = 0x%02x\n" % (i, num_bytes + 4 + i))
|
||||
#
|
||||
# f.write("\n\nVOLTAGE_SCHEDULE = {\n")
|
||||
# for o, v in unique_opcodes.items():
|
||||
# f.write(
|
||||
# " Opcode.TICK_%02x: numpy.array(%s, dtype=numpy.float32),"
|
||||
# "\n" % (o, v))
|
||||
# for i, skip_cycles in enumerate(eof_cycles):
|
||||
# f.write(" Opcode.END_OF_FRAME_%d: numpy.array([%s], "
|
||||
# "dtype=numpy.float32), # %s\n" % (i, ", ".join(
|
||||
# str(f) for f in _make_end_of_frame_voltages2(
|
||||
# skip_cycles)), skip_cycles))
|
||||
# f.write("}\n")
|
||||
#
|
||||
# f.write("\n\nTOGGLES = {\n")
|
||||
# for o, v in toggles.items():
|
||||
# f.write(
|
||||
# " Opcode.TICK_%02x: %d,\n" % (o, v)
|
||||
# )
|
||||
# f.write("}\n")
|
||||
#
|
||||
# f.write("\n\nEOF_OPCODES = (\n")
|
||||
# for i in range(len(eof_cycles)):
|
||||
# f.write(" Opcode.END_OF_FRAME_%d,\n" % i)
|
||||
# f.write(")\n")
|
||||
|
||||
|
||||
def all_opcode_combinations(
|
||||
max_cycles: int, opcodes: Iterable[Opcode], start_opcodes: List[int]
|
||||
) -> List[Tuple[Opcode]]:
|
||||
return sorted(
|
||||
list(all_opcodes(max_cycles, opcodes, start_opcodes)),
|
||||
key=lambda o: len(o), reverse=True)
|
||||
|
||||
|
||||
def sort_by_opcode_count(
|
||||
player_opcodes: List[Tuple[Opcode]], count_opcodes: List[int]
|
||||
) -> List[Tuple[Opcode]]:
|
||||
return sorted(
|
||||
player_opcodes, key=lambda ops: sum(o in count_opcodes for o in ops),
|
||||
reverse=True)
|
||||
# def all_opcode_combinations(
|
||||
# max_cycles: int, opcodes: Iterable[Opcode], start_opcodes: List[int]
|
||||
# ) -> List[Tuple[Opcode]]:
|
||||
# return sorted(
|
||||
# list(all_opcodes(max_cycles, opcodes, start_opcodes)),
|
||||
# key=lambda o: len(o), reverse=True)
|
||||
#
|
||||
#
|
||||
# def sort_by_opcode_count(
|
||||
# player_opcodes: List[Tuple[Opcode]], count_opcodes: List[int]
|
||||
# ) -> List[Tuple[Opcode]]:
|
||||
# return sorted(
|
||||
# player_opcodes, key=lambda ops: sum(o in count_opcodes for o in ops),
|
||||
# reverse=True)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--max_cycles", type=int, required=True,
|
||||
help="Maximum cycle length of player opcodes")
|
||||
parser.add_argument("opcodes", nargs="+",
|
||||
choices=Opcode.__members__.keys(),
|
||||
help="6502 opcodes to use when generating player "
|
||||
"opcodes")
|
||||
args = parser.parse_args()
|
||||
# parser = argparse.ArgumentParser()
|
||||
# parser.add_argument("--max_cycles", type=int, required=True,
|
||||
# help="Maximum cycle length of player opcodes")
|
||||
# parser.add_argument("opcodes", nargs="+",
|
||||
# choices=Opcode.__members__.keys(),
|
||||
# help="6502 opcodes to use when generating player "
|
||||
# "opcodes")
|
||||
# args = parser.parse_args()
|
||||
|
||||
opcodes = set(Opcode.__members__[op] for op in args.opcodes)
|
||||
# TODO: use Opcode instead of int values
|
||||
non_nops = [Opcode.STA, Opcode.INC, Opcode.INCX, Opcode.STAX,
|
||||
Opcode.JMP_INDIRECT]
|
||||
# opcodes = set(Opcode.__members__[op] for op in args.opcodes)
|
||||
# # TODO: use Opcode instead of int values
|
||||
# non_nops = [Opcode.STA, Opcode.INC, Opcode.INCX, Opcode.STAX,
|
||||
# Opcode.JMP_INDIRECT]
|
||||
|
||||
player_ops = sort_by_opcode_count(all_opcode_combinations(
|
||||
max_cycles=args.max_cycles, opcodes=opcodes, start_opcodes=non_nops),
|
||||
non_nops)
|
||||
# player_ops = sort_by_opcode_count(all_opcode_combinations(
|
||||
# max_cycles=args.max_cycles, opcodes=opcodes, start_opcodes=non_nops),
|
||||
# non_nops)
|
||||
|
||||
player_ops = audio_opcodes()
|
||||
generate_player(
|
||||
player_ops,
|
||||
opcode_filename="opcodes_generated.py",
|
||||
|
|
124
opcodes_6502.py
124
opcodes_6502.py
|
@ -5,8 +5,9 @@ from typing import Iterable, List
|
|||
class Opcode:
|
||||
"""6502 assembly language opcode with cycle length"""
|
||||
|
||||
def __init__(self, cycles, asm="", indent=4, toggle=False):
|
||||
def __init__(self, cycles, bytes, asm="", indent=4, toggle=False):
|
||||
self.cycles = cycles
|
||||
self.bytes = bytes
|
||||
self.asm = asm
|
||||
self.indent = indent
|
||||
# Assume toggles speaker on the last cycle
|
||||
|
@ -24,7 +25,7 @@ class Opcode:
|
|||
|
||||
class Literal(Opcode):
|
||||
def __init__(self, asm, indent=4):
|
||||
super(Literal, self).__init__(cycles=0, asm=asm, indent=indent)
|
||||
super(Literal, self).__init__(cycles=0, bytes=0, asm=asm, indent=indent)
|
||||
|
||||
def __repr__(self):
|
||||
return "<literal>"
|
||||
|
@ -54,10 +55,10 @@ def interleave_opcodes(
|
|||
return
|
||||
continue
|
||||
if padding_cycles == 3:
|
||||
yield PaddingOpcode(3, "STA zpdummy")
|
||||
yield PaddingOpcode(3, 2, "STA zpdummy")
|
||||
padding_cycles -= 3
|
||||
else:
|
||||
yield PaddingOpcode(2, "NOP")
|
||||
yield PaddingOpcode(2, 1, "NOP")
|
||||
padding_cycles -= 2
|
||||
assert padding_cycles == 0
|
||||
else:
|
||||
|
@ -67,83 +68,41 @@ def interleave_opcodes(
|
|||
raise OverflowError
|
||||
|
||||
|
||||
STA_C030 = Opcode(4, "STA $C030", toggle=True)
|
||||
|
||||
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, "; pad %d cycles" % cycles)
|
||||
return PaddingOpcode(cycles, None, "; 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 nops(cycles: int) -> Iterable[Opcode]:
|
||||
print(cycles)
|
||||
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 voltage_sequence(
|
||||
# opcodes: Iterable[Opcode], starting_voltage=1.0
|
||||
# ) -> Tuple[numpy.float32, int]:
|
||||
# """Voltage sequence for sequence of opcodes."""
|
||||
# out = []
|
||||
# v = starting_voltage
|
||||
# toggles = 0
|
||||
# for op in opcodes:
|
||||
# for nv in v * numpy.array(VOLTAGES[op]):
|
||||
# if v != nv:
|
||||
# toggles += 1
|
||||
# v = nv
|
||||
# out.append(v)
|
||||
# return tuple(numpy.array(out, dtype=numpy.float32)), toggles
|
||||
|
||||
|
||||
def toggles(opcodes: Iterable[Opcode]) -> List[bool]:
|
||||
|
@ -156,13 +115,12 @@ def toggles(opcodes: Iterable[Opcode]) -> List[bool]:
|
|||
if op.toggle:
|
||||
speaker = not speaker
|
||||
res.append(speaker)
|
||||
return res
|
||||
return tuple(res)
|
||||
|
||||
|
||||
base = itertools.cycle([STA_C030, PaddingOpcode(6)])
|
||||
def total_bytes(opcodes: Iterable[Opcode]) -> int:
|
||||
return sum(op.bytes for op in opcodes)
|
||||
|
||||
eof = list(interleave_opcodes(base, CORE_EOF))
|
||||
for op in eof:
|
||||
print(op)
|
||||
|
||||
print(toggles(eof))
|
||||
def total_cycles(opcodes: Iterable[Opcode]) -> int:
|
||||
return sum(op.cycles for op in opcodes)
|
||||
|
|
|
@ -1,118 +1,144 @@
|
|||
tick_00: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
tick_03: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
tick_06: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
tick_09: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
NOP
|
||||
tick_0a: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
JMP (WDATA)
|
||||
tick_00: ; voltages (True, True, True, True, True, True, True, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_01: ; voltages (True, True, True, True, True, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_02: ; voltages (True, True, True, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_05: ; voltages (True, True, True, False, False, False, False, True, True, True, True, True, True, True)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_08: ; voltages (True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_0b: ; voltages (True, True, True, True, True, True)
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_0d: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
tick_10: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
tick_13: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
NOP
|
||||
tick_14: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
JMP (WDATA)
|
||||
tick_0e: ; voltages (True, True, True, True, True, True, True, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_0f: ; voltages (True, True, True, True, True, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_10: ; voltages (True, True, True, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_13: ; voltages (True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, True)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_16: ; voltages (True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_1a: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
tick_1d: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
NOP
|
||||
tick_1e: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
STA $C030
|
||||
JMP (WDATA)
|
||||
tick_1d: ; voltages (True, True, True, True, True, True, True, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_1e: ; voltages (True, True, True, True, True, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_1f: ; voltages (True, True, True, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_22: ; voltages (True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, True)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_25: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_27: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
STA $C030
|
||||
STA $C030
|
||||
JMP (WDATA)
|
||||
tick_2d: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_2e: ; voltages (True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_2f: ; voltages (True, True, True, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_32: ; voltages (True, True, True, True, True, False, False, False, False, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_33: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
tick_36: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
tick_39: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
NOP
|
||||
tick_3a: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
NOP
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
tick_3c: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_3d: ; voltages (True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_3e: ; voltages (True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_41: ; voltages (True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_3f: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
tick_42: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
NOP
|
||||
tick_43: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
NOP
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
tick_4c: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_4d: ; voltages (True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_4e: ; voltages (True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_51: ; voltages (True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
NOP ; 2 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_4b: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
tick_4e: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
NOP
|
||||
tick_4f: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
NOP
|
||||
STA $C030
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
tick_5d: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_5e: ; voltages (True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_5f: ; voltages (True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_62: ; voltages (True, True, True, True, True, True, True, False, False, False, False, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_57: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
tick_5a: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
NOP
|
||||
tick_5b: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
NOP
|
||||
NOP
|
||||
STA $C030
|
||||
JMP (WDATA)
|
||||
tick_6d: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_6e: ; voltages (True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_6f: ; voltages (True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_72: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_63: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
STA $C030
|
||||
NOP
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
tick_7e: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_7f: ; voltages (True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
NOP ; 2 cycles
|
||||
tick_80: ; voltages (True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, True, True, False, False, False, False, False, False, False)
|
||||
STA $C030 ; 4 cycles
|
||||
tick_83: ; voltages (True, True, True, True, True, True, True, False, False, False, False, False, False, False, False, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
NOP ; 2 cycles
|
||||
NOP ; 2 cycles
|
||||
STA $C030 ; 4 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_6e: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
NOP
|
||||
STA $C030
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
tick_90: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_91: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_92: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_93: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_94: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_95: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_96: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_97: ; voltages (True, True, True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_98: ; voltages (True, True, True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
tick_99: ; voltages (True, True, True, True, True, True, True, True)
|
||||
NOP ; 2 cycles
|
||||
JMP (WDATA) ; 6 cycles
|
||||
|
||||
tick_79: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
STA $C030
|
||||
NOP
|
||||
NOP
|
||||
STA $C030
|
||||
JMP (WDATA)
|
||||
|
||||
tick_84: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
tick_87: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
NOP
|
||||
tick_88: ; voltages (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
|
||||
NOP
|
||||
NOP
|
||||
NOP
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
|
||||
tick_8f: ; voltages (1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
|
||||
STA $C030
|
||||
NOP
|
||||
NOP
|
||||
NOP
|
||||
NOP
|
||||
JMP (WDATA)
|
||||
|
||||
; 153 bytes
|
||||
; 157 bytes
|
||||
|
|
Loading…
Reference in New Issue