From d7150ef2ebe0ce812c996eece7bb280c720eb139 Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Sun, 4 Oct 2026 19:11:14 +0200 Subject: [PATCH] Full file transfer, no mem loading yet --- apps/casette/encode.py | 39 +++++++++++++---- apps/casette/tape_loader.asm | 80 ++++++++++++++++++++++++++++++++++- apps/casette/test2.wav | Bin 137132 -> 1009004 bytes 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/apps/casette/encode.py b/apps/casette/encode.py index 690cd60..d1b518e 100644 --- a/apps/casette/encode.py +++ b/apps/casette/encode.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from __future__ import annotations +import os import argparse import math import struct @@ -8,7 +9,7 @@ import wave from pathlib import Path class FSKWriter: - def __init__(self, sample_rate: int, c_low: int, c_high: int, f_low: float, f_high: float, amplitude: float): + def __init__(self, sample_rate: int, c_low: int, c_high: int, f_low: float, f_high: float, amplitude: float, block_spacing: float): self.sample_rate = sample_rate self.c_low = c_low self.c_high = c_high @@ -18,12 +19,16 @@ class FSKWriter: self.samples_per_low = (sample_rate * c_low) // f_low self.samples_per_high = (sample_rate * c_high) // f_high self.phase = 0.0 + self.block_spacing = block_spacing self.samples = bytearray() - self.samples.extend(b'\0'*int(1.0*self.sample_rate)) + self.space(1.0) def finalize(self): - self.samples.extend(b'\0'*int(1.0*self.sample_rate)) + self.space(1.0) + + def space(self, time:float = 0.03): + self.samples.extend(b'\0'*int(time*self.sample_rate)) def _tone(self, frequency: float, sample_count: int) -> None: phase_step = 2.0 * math.pi * frequency / self.sample_rate @@ -64,14 +69,16 @@ class FSKWriter: self.byte(value) def block(self, nr: int, data: bytes) -> None: + print(f'> {data}') self.leader() self.byte(nr) l = len(data) self.byte(l>>8) self.byte(l&0xff) + self.samples.extend(b'\0'*int(self.samples_per_low*8)) self.bytes(data) self.byte(self.chsum(data)) - self.samples.extend(b'\0'*int(0.3*self.sample_rate)) + self.space(self.block_spacing) def chsum(self, data: bytes) -> int: cs = 0 @@ -83,21 +90,37 @@ class FSKWriter: def main() -> None: parser = argparse.ArgumentParser(description="Encode a binary file as two tone FSK WAV") parser.add_argument("input", type=Path, help="Binary input file") - parser.add_argument("output", type=Path, help="output WAVl default .wav") + parser.add_argument("output", type=Path, help="output WAV; default .wav") parser.add_argument("--sample-rate", type=int, default=48000) parser.add_argument("--low", type=float, default=2000.0, help="low tone in Hz") parser.add_argument("--high", type=float, default=4000.0, help="high tone in Hz") parser.add_argument("--low-cycles", type=int, default=2, help="low tone cycle count for a 0") parser.add_argument("--high-cycles", type=int, default=4, help="high tone cycle count for a 1") parser.add_argument("--amplitude", type=float, default=0.75) - parser.add_argument("--block-size", type=int, default=64) + parser.add_argument("--block-size", type=int, default=128) + parser.add_argument("--block-spacing", type=float, default=1.0) args = parser.parse_args() output_path = args.output or args.input.with_name(args.input.stem + ".wav") - tones = FSKWriter(args.sample_rate, args.low_cycles, args.high_cycles, args.low, args.high, args.amplitude) - tones.block(0, args.input.name.encode() + b'\0') + tones = FSKWriter(args.sample_rate, args.low_cycles, args.high_cycles, args.low, args.high, args.amplitude, args.block_spacing) + # Create header block + nrblocks = math.ceil(os.path.getsize(args.input)/args.block_size) + print(f"Number of blocks {nrblocks}*2") + + tones.block(0, struct.pack('g$l(ribhF|RH=ju6k2C~djvUX;o@cJ~h1;__VtF4>L- zUWuD;%BJ}}o@Q)zfU(o##lX!!jefp3>wo$Dbb7vD9xc9&%Hze?|LB3sZfFwS+`6JPX0Bh!O@<1)bNQ!xi za>OVhX)IKSYE>fGu0NXlnp|_x&05hf>YQ3*wO4git8&?g>@Rwo*x}I^H(6&~7>U%j z;1oU>BLW9yA7>$Y+9$iBSo_v4Le3*2qv3TpF_+*6q*nw zh0fAeZX$}9ZOa}r4&LxljBBABf zK(dWto-D%&T(NhknJtDY=BYWdv#PEL*4ex6F{<&3T1p3+tg94bpGUAuOukj@uziwT zgWWqkBP6OIyTpocUj4(7Rbr;vCM1GOzR~7c#|gw9n1Fcn2jV&o&*(}1CN`auobG9# zm6b|(JO(?nsZQKFN_bbZ(lScil2Pq~n#(e|9&nrqw-ijF&K-sM_Nf=9m9DXbMr+i( z1Ygke=~fK0OG402;^|lu7X|)ZXli*6@mRH+5bO>9-e+m$-h(ILDn7bq@BZrr&VMwlDp1(m|qOSN9`RDFQsd!KyTo8 zlW+@{D;hy#lFC??`wcA)(4{+ZH}z+%Rvl*v8@WHxBB&tFuw^HrY4c6HpBRu{7 zf@nC7n7MCw-pqW>%%_(-e`9H8zGmig&CF+q{Y-ynU|ecuKD|W9?_kZ$$2cs%MA$7X zxM}$gddtZ+X=XmT@iX@}GoKb$HSsqyUo-Qq{NNVMHa1>0GoStr7U0s%eDeFj?Pfmt Iudo07Plv8rO8@`> delta 98 zcmaEJ%683q4)!2VH@79?OcU7^nH$8JHX7FMp6I}~dEM>=kL`YEc^sIwpWDfKfNlG_ yhg=|bD(3;_t$NHrW}iB<0?YPwlUWj&xAR$mMBc4tUcj_{T^9?GooB_k?=t{B!6jG#