New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

laser500-wav

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

laser500-wav - npm Package Compare versions

Comparing version 1.0.3 to 1.1.0

dist/bytes.d.ts

272

dist/laser500wav.js

@@ -12,9 +12,5 @@ #!/usr/bin/env node

const fs_1 = __importDefault(require("fs"));
const wav_encoder_1 = __importDefault(require("wav-encoder"));
const options_1 = require("./options");
const turbo_loader_1 = require("./turbo_loader");
const bytes_1 = require("./bytes");
const checksum_1 = require("./checksum");
const turbo_encoder_1 = require("./turbo_encoder");
const vz_1 = require("./vz");
const tape_creator_1 = require("./tape_creator");
function main() {

@@ -29,8 +25,7 @@ if (options_1.options.input === undefined || options_1.options.output === undefined || (options_1.options.l310 === undefined && options_1.options.l500 === undefined)) {

console.log(" -v or --volume number volume between 0 and 1 (1.0 default)");
console.log(" --stereoboost boost volume for stereo cables by inverting the RIGHT channel");
console.log(" --invert inverts the polarity of the audio");
console.log(" --stereoboost boost volume for stereo cables by inverting the RIGHT channel (default off)");
console.log(" --invert inverts the polarity of the audio (default off)");
console.log(" --header number of header bytes (128 default as in ROM loader)");
console.log(" --pulsems n ROM loader pulse width in microseconds (277 default)");
console.log(" -x or --turbo generates a turbo tape loadable file");
console.log(" --turbo-speed speed speed 1,2,3,4 defaults to 4 (fastest)");
console.log(" -x or --turbo speed 0=normal ROM loader WAV, 1-4 turbo tape (4 fastest)");
process.exit(0);

@@ -42,254 +37,17 @@ }

}
const laser500 = options_1.options.l500 && !options_1.options.l310;
// note on SAMPLE_RATE: when using turbo tape 48000 Hz is the minimum to work
// on the real machine. One the emulator the minimum is 18000 Hz
const SAMPLE_RATE = options_1.options.samplerate || 96000;
const VOLUME = options_1.options.volume || 1.0;
const HEADER_LEN = options_1.options.header || 128;
const TAIL_LEN = 4; // 128; // TODO make it option?
const pulsems = (options_1.options.pulsems || 277) / 1000000; // for a total of 277 microseconds
const PULSE_SHORT = pulsems * SAMPLE_RATE;
const PULSE_LONG = PULSE_SHORT * 2;
const turboparams = (0, turbo_encoder_1.decodeBitSize)(options_1.options['turbo-speed'], SAMPLE_RATE, laser500);
const turbo = {
THRESHOLD: turboparams.THRESHOLD,
TURBO_HALFPULSE_SIZE: turboparams.TURBO_HALFPULSE_SIZE,
TURBO_INVERT: turboparams.TURBO_INVERT
};
const fileName = options_1.options.input;
const outputName = options_1.options.output;
const wavName = outputName + ".wav";
if (!fs_1.default.existsSync(fileName)) {
console.log(`file "${fileName}" not found`);
const VZ_file_name = options_1.options.input;
const WAV_file_name = options_1.options.output + ".wav";
if (!fs_1.default.existsSync(VZ_file_name)) {
console.log(`file "${VZ_file_name}" not found`);
process.exit(0);
}
const VZ = (0, vz_1.unpackvz)(fs_1.default.readFileSync(fileName));
const fileType = VZ.type;
const tape = {
tapeName: VZ.filename,
fileType,
startAddress: VZ.start,
program: Buffer.from(VZ.data),
headerLen: HEADER_LEN,
tailLen: TAIL_LEN,
PULSE_SHORT: PULSE_SHORT,
PULSE_LONG: PULSE_LONG,
SAMPLE_RATE: SAMPLE_RATE,
VOLUME: VOLUME,
laser500
};
console.log(`target is ${tape.laser500 ? 'Laser 500' : 'Laser 310'} `);
console.log(`SAVING ${fileType === vz_1.VZ_BASIC ? "T" : "B"}: '${VZ.filename}' from $${(0, bytes_1.hex)(VZ.start, 4)}, ${VZ.data.length} bytes`);
let samples;
// normal tape
if (options_1.options.turbo === undefined) {
samples = getNormalSamples(tape);
}
else {
samples = getTurboSamples(tape, turbo);
}
// invert audio samples if --invert option was given
if (options_1.options.invert)
samples = invertSamples(samples);
// fix_cassette_port(samples, SAMPLE_RATE);
const f_samples = new Float32Array(samples);
const f_samples_inv = new Float32Array(invertSamples(samples));
const wavData = {
sampleRate: SAMPLE_RATE,
channelData: !options_1.options.stereoboost ? [f_samples] : [f_samples, f_samples_inv]
};
const wavoptions = { bitDepth: 16, float: false, symmetric: false };
const buffer = wav_encoder_1.default.encode.sync(wavData, wavoptions);
fs_1.default.writeFileSync(wavName, Buffer.from(buffer));
let gentype = fileType === vz_1.VZ_BINARY ? "B: standard file" : "T: standard file";
if (options_1.options.turbo !== undefined)
const VZ_file = fs_1.default.readFileSync(VZ_file_name);
const VZ = (0, vz_1.unpackvz)(VZ_file);
const buffer = (0, tape_creator_1.VZ_to_WAV)(VZ, options_1.options);
fs_1.default.writeFileSync(WAV_file_name, Buffer.from(buffer));
let gentype = VZ.type === vz_1.VZ_BINARY ? "B: standard file" : "T: standard file";
if (options_1.options.turbo !== 0)
gentype = "TURBO tape";
console.log(`file "${wavName}" generated as ${gentype}`);
/*
// write inverted FFT
{
const { fft, ifft, util } = require('fft-js');
const FFTSIZE = 256;
let in_samples = Array.from(f_samples);
let out_samples: number[] = [];
for(let i=0; i<in_samples.length-FFTSIZE; i+=FFTSIZE) {
const realInput = in_samples.slice(i,i+FFTSIZE);
const phasors = fft(realInput);
for(let j=32; j<FFTSIZE; j++) {
phasors[j][0] = 0;
phasors[j][1] = 0;
}
const signal = ifft(phasors) as [number, number][];
const real = signal.map(e=>e[0]);
out_samples.push(...real);
}
const wavData = {
sampleRate: SAMPLE_RATE,
channelData: [ new Float32Array(out_samples) ]
};
const wavoptions: WavEncoder.Options = { bitDepth: 16, float: false, symmetric: false };
const buffer = WavEncoder.encode.sync(wavData, wavoptions);
fs.writeFileSync("testfft.wav", Buffer.from(buffer));
}
*/
console.log(`file "${WAV_file_name}" generated as ${gentype}`);
}
// ***************************************************************************************
function getNormalSamples(tape) {
const { header_bytes, body_bytes } = tapeStructure(tape);
// header
const header_bits = bytesToBits(header_bytes);
const header_pulses = bitsToPulses(header_bits);
const header_samples = pulsesToSamples(header_pulses, tape);
// gap between header and body in Laser310
let gap = tape.laser500 ? [] : getGapSamples(tape);
// body
const body_bits = bytesToBits(body_bytes);
const body_pulses = bitsToPulses(body_bits);
const body_samples = pulsesToSamples(body_pulses, tape);
const samples = header_samples.concat(gap).concat(body_samples);
return samples;
}
function getTurboSamples(tape, turbo) {
const { startAddress, program } = tape;
const turbo_address = startAddress + program.length;
const loader_program = (0, turbo_loader_1.getTurboLoader)(tape.laser500, turbo.THRESHOLD, turbo_address, tape.fileType);
tape.fileType = vz_1.VZ_BINARY;
tape.startAddress = turbo_address;
tape.program = loader_program;
const { header_bytes, body_bytes } = tapeStructure(tape);
const header_bits = bytesToBits(header_bytes);
const header_pulses = bitsToPulses(header_bits);
const header_samples = pulsesToSamples(header_pulses, tape);
// gap between header and body in Laser310
let gap = tape.laser500 ? [] : getGapSamples(tape);
// body
const body_bits = bytesToBits(body_bytes);
const body_pulses = bitsToPulses(body_bits);
const body_samples = pulsesToSamples(body_pulses, tape);
const turbo_bytes = (0, turbo_encoder_1.getTurboBytes)(startAddress, program);
// patch
//for(let t=0; t<turbo_bytes.length; t++) turbo_bytes[t] = t % 2 + 2;
const turbo_bits = bytesToBits(turbo_bytes);
const turbo_samples = (0, turbo_encoder_1.TT_bitsToSamples)(turbo_bits, tape, turbo);
const samples = header_samples.concat(gap).concat(body_samples).concat(turbo_samples);
return samples;
}
function invertSamples(samples) {
return samples.map(e => -e);
}
function tapeStructure(tape) {
const header_bytes = [];
const body_bytes = [];
const { tapeName, fileType, startAddress, program, headerLen, tailLen, laser500 } = tape;
// header
for (let t = 0; t < headerLen; t++)
header_bytes.push(0x80);
for (let t = 0; t < 5; t++)
header_bytes.push(0xfe);
// file type
header_bytes.push(fileType);
// file name
for (let t = 0; t < tapeName.length; t++)
header_bytes.push(tapeName.charCodeAt(t));
header_bytes.push(0x00);
if (laser500) {
// laser 500 has additional bytes and a marker to allow print of file name
// laser 310 elongates the last pulse of the "0" bit to allow print of file name
for (let t = 0; t < 5; t++)
header_bytes.push(0x80); // additional header to allow print of file name
for (let t = 0; t < 10; t++)
header_bytes.push(0x80);
header_bytes.push(0xff); // end of header
}
// start address
body_bytes.push((0, bytes_1.lo)(startAddress));
body_bytes.push((0, bytes_1.hi)(startAddress));
// end address
const endAddress = startAddress + program.length;
body_bytes.push((0, bytes_1.lo)(endAddress));
body_bytes.push((0, bytes_1.hi)(endAddress));
// program
for (let t = 0; t < program.length; t++)
body_bytes.push(program[t]);
// checksum
const checksum = (0, checksum_1.calculate_checksum)(program, startAddress, endAddress);
body_bytes.push((0, bytes_1.lo)(checksum));
body_bytes.push((0, bytes_1.hi)(checksum));
// terminator
for (let t = 0; t < tailLen; t++)
body_bytes.push(0x00);
return { header_bytes, body_bytes };
}
function bytesToBits(bytes) {
const bits = [];
for (let t = 0; t < bytes.length; t++) {
const b = bytes[t] & 0xFF;
bits.push((b & 128) >> 7);
bits.push((b & 64) >> 6);
bits.push((b & 32) >> 5);
bits.push((b & 16) >> 4);
bits.push((b & 8) >> 3);
bits.push((b & 4) >> 2);
bits.push((b & 2) >> 1);
bits.push((b & 1) >> 0);
}
return bits;
}
function bitsToPulses(bits) {
const pulses = [];
for (let t = 0; t < bits.length; t++) {
const b = bits[t];
if (b == 0) {
pulses.push("S");
pulses.push("L"); /*tapeBits.push(1); tapeBits.push(0); tapeBits.push(1); tapeBits.push(1); tapeBits.push(0); tapeBits.push(0);*/
}
else {
pulses.push("S");
pulses.push("S");
pulses.push("S"); /*tapeBits.push(1); tapeBits.push(0); tapeBits.push(1); tapeBits.push(0); tapeBits.push(1); tapeBits.push(0);*/
}
}
return pulses;
}
function pulsesToSamples(tapeBits, tape) {
const samples = [];
let ptr = 0;
const { SAMPLE_RATE, VOLUME, PULSE_LONG, PULSE_SHORT } = tape;
for (let t = 0; t < tapeBits.length; t++) {
if (tapeBits[t] === "S") {
for (; ptr < PULSE_SHORT; ptr++)
samples.push(VOLUME);
ptr -= PULSE_SHORT;
for (; ptr < PULSE_SHORT; ptr++)
samples.push(-VOLUME);
ptr -= PULSE_SHORT;
}
else {
for (; ptr < PULSE_LONG; ptr++)
samples.push(VOLUME);
ptr -= PULSE_LONG;
for (; ptr < PULSE_LONG; ptr++)
samples.push(-VOLUME);
ptr -= PULSE_LONG;
}
}
return samples;
}
function getGapSamples(tape) {
const samples = [];
let ptr = 0;
const { VOLUME, PULSE_SHORT } = tape;
for (let t = 0; t < 11; t++) {
for (; ptr < PULSE_SHORT; ptr++)
samples.push(-VOLUME);
ptr -= PULSE_SHORT;
}
return samples;
}
main();

@@ -9,12 +9,11 @@ "use strict";

exports.options = parseOptions([
{ name: 'input', alias: 'i', type: String, defaultOption: true },
{ name: 'input', alias: 'i', type: String, },
{ name: 'output', alias: 'o', type: String },
{ name: 'samplerate', alias: 's', type: Number },
{ name: 'volume', alias: 'v', type: Number },
{ name: 'stereoboost', type: Boolean },
{ name: 'invert', type: Boolean },
{ name: 'header', type: Number },
{ name: 'pulsems', type: Number },
{ name: 'turbo', alias: 'x', type: Boolean },
{ name: 'turbo-speed', type: Number },
{ name: 'samplerate', alias: 's', type: Number, defaultValue: 96000 },
{ name: 'volume', alias: 'v', type: Number, defaultValue: 1 },
{ name: 'stereoboost', type: Boolean, defaultValue: false },
{ name: 'invert', type: Boolean, defaultValue: false },
{ name: 'header', type: Number, defaultValue: 128 },
{ name: 'pulsems', type: Number, defaultValue: 277 },
{ name: 'turbo', alias: 'x', type: Number, defaultValue: 0 },
{ name: 'l500', type: Boolean },

@@ -21,0 +20,0 @@ { name: 'l310', type: Boolean }

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTurboLoader = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const bytes_1 = require("./bytes");
const turbo_L310_asm_info_1 = require("./turbo_L310_asm_info");
const turbo_L500_asm_info_1 = require("./turbo_L500_asm_info");
// get the Z80 turbo loader routine, patching two bytes and
// relocating it at the desidered address
function getTurboLoader(laser500, THRESHOLD, relocate_address, fileType) {
const rootname = path_1.default.resolve(__dirname, laser500 ? "../turbo_tape/turbo_L500" : "../turbo_tape/turbo_L310");
const loader_program = fs_1.default.readFileSync(`${rootname}.bin`);
patch_bytes(loader_program, THRESHOLD, fileType, rootname);
relocate(loader_program, relocate_address, rootname);
const asm_info = laser500 ? turbo_L500_asm_info_1.turbo_L500_asm_info : turbo_L310_asm_info_1.turbo_L310_asm_info;
const loader_program = asm_info.code;
patch_bytes(loader_program, THRESHOLD, fileType, asm_info);
const relocated = relocate(loader_program, relocate_address, asm_info);
// for debug purposes
{
const symbols = fs_1.default.readFileSync(`${rootname}.sym`).toString();
let set_threshold = getSymbolAddress(symbols, "set_threshold") + relocate_address;
let set_threshold = asm_info.symbols["set_threshold"] + relocate_address;
console.log(`const label_set_threshold = 0x${(0, bytes_1.hex)(set_threshold, 4)};`);
let loop_file = getSymbolAddress(symbols, "loop_file") + relocate_address;
let loop_file = asm_info.symbols["loop_file"] + relocate_address;
console.log(`const label_loop_file = 0x${(0, bytes_1.hex)(loop_file, 4)};`);
let autorun = getSymbolAddress(symbols, "autorun") + relocate_address;
let autorun = asm_info.symbols["autorun"] + relocate_address;
console.log(`const label_autorun = 0x${(0, bytes_1.hex)(autorun, 4)};`);
}
return loader_program;
return relocated;
}
exports.getTurboLoader = getTurboLoader;
function patch_bytes(loader_program, THRESHOLD, fileType, rootname) {
// read turbo.sym symbol files
const symbols = fs_1.default.readFileSync(`${rootname}.sym`).toString();
// do the needed byte patches
loader_program[getSymbolAddress(symbols, "turbo_load") + 1] = fileType;
loader_program[getSymbolAddress(symbols, "set_threshold") + 1] = THRESHOLD;
function patch_bytes(loader_program, THRESHOLD, fileType, info) {
loader_program[info.symbols.turbo_load + 1] = fileType; // patches the file type B: or T:
loader_program[info.symbols.set_threshold + 1] = THRESHOLD; // patches the turbo speed threshold
}
function getSymbolAddress(file, symbolname) {
const regex = new RegExp(symbolname + "\\s*=\\s*\\$(?<address>[0-9a-fA-F]{4})", "g");
const match = regex.exec(file);
if (match === null || match.groups === undefined)
throw `can't find ${symbolname} label in .sym file`;
// get label address from hex format
const address = Number.parseInt(match.groups["address"], 16);
return address;
}
// relocate the Z80 turbo loader routine at the desidered

@@ -50,11 +34,12 @@ // destination by changing all the interested addresses.

// of offsets (16 bits) that needs to be changed
function relocate(loader_program, relocate_address, rootname) {
const reloc_info = fs_1.default.readFileSync(`${rootname}.reloc`);
for (let t = 0; t < reloc_info.length; t += 2) {
const patch_address = reloc_info.readUInt16LE(t);
const offset_value = loader_program.readUInt16LE(patch_address);
const relocated_value = offset_value + relocate_address;
loader_program.writeUInt16LE(relocated_value, patch_address);
//console.log(`${t}: [${patch_address}] = ${offset_value.toString(16)} -> ${relocated_value.toString(16)}`);
function relocate(loader_program, relocate_address, asm_info) {
const reloc_table = asm_info.reloc;
const lp = Buffer.from(loader_program);
for (let t = 0; t < reloc_table.length; t++) {
const index = reloc_table[t];
const address = lp.readUInt16LE(index);
const relocated_value = address + relocate_address;
lp.writeUInt16LE(relocated_value, index);
}
return lp;
}
{
"name": "laser500-wav",
"version": "1.0.3",
"version": "1.1.0",
"description": "Laser310/500 program to WAV (tape) converter",

@@ -5,0 +5,0 @@ "bin": {

@@ -9,35 +9,7 @@ #!/usr/bin/env node

import fs from "fs";
import path from 'path';
import WavEncoder from "wav-encoder";
import { options } from './options';
import { getTurboLoader } from "./turbo_loader";
import { hex, hi, lo } from "./bytes";
import { calculate_checksum } from "./checksum";
import { TT_bitsToSamples, decodeBitSize, getTurboBytes } from "./turbo_encoder";
import { VZ_BASIC, VZ_BINARY, VZFILETYPE, unpackvz } from "./vz";
import { fix_cassette_port } from "./fix_tape";
import { VZ_BINARY, unpackvz } from "./vz";
import { VZ_to_WAV } from "./tape_creator";
type Pulse = "S" | "L";
export interface Tape {
tapeName: string;
fileType: VZFILETYPE;
startAddress: number;
program: Buffer;
headerLen: number;
tailLen: number;
PULSE_SHORT: number;
PULSE_LONG: number;
SAMPLE_RATE: number;
VOLUME: number;
laser500: boolean;
}
export interface TurboTape {
THRESHOLD: number;
TURBO_HALFPULSE_SIZE: number;
TURBO_INVERT: boolean;
}
function main() {

@@ -52,8 +24,7 @@ if(options.input === undefined || options.output === undefined || (options.l310===undefined && options.l500===undefined)) {

console.log(" -v or --volume number volume between 0 and 1 (1.0 default)");
console.log(" --stereoboost boost volume for stereo cables by inverting the RIGHT channel");
console.log(" --invert inverts the polarity of the audio");
console.log(" --stereoboost boost volume for stereo cables by inverting the RIGHT channel (default off)");
console.log(" --invert inverts the polarity of the audio (default off)");
console.log(" --header number of header bytes (128 default as in ROM loader)");
console.log(" --pulsems n ROM loader pulse width in microseconds (277 default)");
console.log(" -x or --turbo generates a turbo tape loadable file");
console.log(" --turbo-speed speed speed 1,2,3,4 defaults to 4 (fastest)");
console.log(" -x or --turbo speed 0=normal ROM loader WAV, 1-4 turbo tape (4 fastest)");
process.exit(0);

@@ -67,295 +38,21 @@ }

const laser500 = options.l500 && !options.l310;
const VZ_file_name = options.input;
const WAV_file_name = options.output + ".wav";
// note on SAMPLE_RATE: when using turbo tape 48000 Hz is the minimum to work
// on the real machine. One the emulator the minimum is 18000 Hz
const SAMPLE_RATE = options.samplerate || 96000;
const VOLUME = options.volume || 1.0;
const HEADER_LEN = options.header || 128;
const TAIL_LEN = 4; // 128; // TODO make it option?
const pulsems = (options.pulsems || 277)/1000000; // for a total of 277 microseconds
const PULSE_SHORT = pulsems * SAMPLE_RATE;
const PULSE_LONG = PULSE_SHORT * 2;
const turboparams = decodeBitSize(options['turbo-speed'], SAMPLE_RATE, laser500);
const turbo: TurboTape = {
THRESHOLD: turboparams.THRESHOLD,
TURBO_HALFPULSE_SIZE: turboparams.TURBO_HALFPULSE_SIZE,
TURBO_INVERT: turboparams.TURBO_INVERT
};
const fileName = options.input;
const outputName = options.output;
const wavName = outputName + ".wav";
if(!fs.existsSync(fileName)) {
console.log(`file "${fileName}" not found`);
if(!fs.existsSync(VZ_file_name)) {
console.log(`file "${VZ_file_name}" not found`);
process.exit(0);
}
const VZ = unpackvz(fs.readFileSync(fileName));
const VZ_file = fs.readFileSync(VZ_file_name);
const VZ = unpackvz(VZ_file);
const buffer = VZ_to_WAV(VZ, options);
const fileType = VZ.type;
const tape: Tape = {
tapeName: VZ.filename,
fileType,
startAddress: VZ.start,
program: Buffer.from(VZ.data),
headerLen: HEADER_LEN,
tailLen: TAIL_LEN,
PULSE_SHORT: PULSE_SHORT,
PULSE_LONG: PULSE_LONG,
SAMPLE_RATE: SAMPLE_RATE,
VOLUME: VOLUME,
laser500
};
fs.writeFileSync(WAV_file_name, Buffer.from(buffer));
console.log(`target is ${tape.laser500 ? 'Laser 500' : 'Laser 310'} `);
console.log(`SAVING ${fileType === VZ_BASIC ? "T" : "B"}: '${VZ.filename}' from $${hex(VZ.start,4)}, ${VZ.data.length} bytes`);
let samples: number[];
// normal tape
if(options.turbo === undefined) {
samples = getNormalSamples(tape);
}
else {
samples = getTurboSamples(tape, turbo);
}
// invert audio samples if --invert option was given
if(options.invert) samples = invertSamples(samples);
// fix_cassette_port(samples, SAMPLE_RATE);
const f_samples = new Float32Array(samples);
const f_samples_inv = new Float32Array(invertSamples(samples));
const wavData = {
sampleRate: SAMPLE_RATE,
channelData: !options.stereoboost ? [ f_samples ] : [ f_samples, f_samples_inv ]
};
const wavoptions: WavEncoder.Options = { bitDepth: 16, float: false, symmetric: false };
const buffer = WavEncoder.encode.sync(wavData, wavoptions);
fs.writeFileSync(wavName, Buffer.from(buffer));
let gentype = fileType === VZ_BINARY ? "B: standard file" : "T: standard file";
if(options.turbo !== undefined) gentype = "TURBO tape";
console.log(`file "${wavName}" generated as ${gentype}`);
/*
// write inverted FFT
{
const { fft, ifft, util } = require('fft-js');
const FFTSIZE = 256;
let in_samples = Array.from(f_samples);
let out_samples: number[] = [];
for(let i=0; i<in_samples.length-FFTSIZE; i+=FFTSIZE) {
const realInput = in_samples.slice(i,i+FFTSIZE);
const phasors = fft(realInput);
for(let j=32; j<FFTSIZE; j++) {
phasors[j][0] = 0;
phasors[j][1] = 0;
}
const signal = ifft(phasors) as [number, number][];
const real = signal.map(e=>e[0]);
out_samples.push(...real);
}
const wavData = {
sampleRate: SAMPLE_RATE,
channelData: [ new Float32Array(out_samples) ]
};
const wavoptions: WavEncoder.Options = { bitDepth: 16, float: false, symmetric: false };
const buffer = WavEncoder.encode.sync(wavData, wavoptions);
fs.writeFileSync("testfft.wav", Buffer.from(buffer));
}
*/
let gentype = VZ.type === VZ_BINARY ? "B: standard file" : "T: standard file";
if(options.turbo !== 0) gentype = "TURBO tape";
console.log(`file "${WAV_file_name}" generated as ${gentype}`);
}
// ***************************************************************************************
function getNormalSamples(tape: Tape) {
const { header_bytes, body_bytes } = tapeStructure(tape);
// header
const header_bits = bytesToBits(header_bytes);
const header_pulses = bitsToPulses(header_bits);
const header_samples = pulsesToSamples(header_pulses, tape);
// gap between header and body in Laser310
let gap: number[] = tape.laser500 ? [] : getGapSamples(tape);
// body
const body_bits = bytesToBits(body_bytes);
const body_pulses = bitsToPulses(body_bits);
const body_samples = pulsesToSamples(body_pulses, tape);
const samples = header_samples.concat(gap).concat(body_samples);
return samples;
}
function getTurboSamples(tape: Tape, turbo: TurboTape) {
const { startAddress, program } = tape;
const turbo_address = startAddress + program.length;
const loader_program = getTurboLoader(tape.laser500, turbo.THRESHOLD, turbo_address, tape.fileType);
tape.fileType = VZ_BINARY;
tape.startAddress = turbo_address;
tape.program = loader_program;
const { header_bytes, body_bytes } = tapeStructure(tape);
const header_bits = bytesToBits(header_bytes);
const header_pulses = bitsToPulses(header_bits);
const header_samples = pulsesToSamples(header_pulses, tape);
// gap between header and body in Laser310
let gap: number[] = tape.laser500 ? [] : getGapSamples(tape);
// body
const body_bits = bytesToBits(body_bytes);
const body_pulses = bitsToPulses(body_bits);
const body_samples = pulsesToSamples(body_pulses, tape);
const turbo_bytes = getTurboBytes(startAddress, program);
// patch
//for(let t=0; t<turbo_bytes.length; t++) turbo_bytes[t] = t % 2 + 2;
const turbo_bits = bytesToBits(turbo_bytes);
const turbo_samples = TT_bitsToSamples(turbo_bits, tape, turbo);
const samples = header_samples.concat(gap).concat(body_samples).concat(turbo_samples);
return samples;
}
function invertSamples(samples: number[])
{
return samples.map(e=>-e);
}
function tapeStructure(tape: Tape) {
const header_bytes = [];
const body_bytes = [];
const { tapeName, fileType, startAddress, program, headerLen, tailLen, laser500 } = tape;
// header
for(let t=0; t<headerLen; t++) header_bytes.push(0x80);
for(let t=0; t<5; t++) header_bytes.push(0xfe);
// file type
header_bytes.push(fileType);
// file name
for(let t=0; t<tapeName.length; t++) header_bytes.push(tapeName.charCodeAt(t));
header_bytes.push(0x00);
if(laser500) {
// laser 500 has additional bytes and a marker to allow print of file name
// laser 310 elongates the last pulse of the "0" bit to allow print of file name
for(let t=0; t<5; t++) header_bytes.push(0x80); // additional header to allow print of file name
for(let t=0; t<10; t++) header_bytes.push(0x80);
header_bytes.push(0xff); // end of header
}
// start address
body_bytes.push(lo(startAddress));
body_bytes.push(hi(startAddress));
// end address
const endAddress = startAddress + program.length;
body_bytes.push(lo(endAddress));
body_bytes.push(hi(endAddress));
// program
for(let t=0; t<program.length; t++) body_bytes.push(program[t]);
// checksum
const checksum = calculate_checksum(program, startAddress, endAddress);
body_bytes.push(lo(checksum));
body_bytes.push(hi(checksum));
// terminator
for(let t=0; t<tailLen; t++) body_bytes.push(0x00);
return { header_bytes, body_bytes };
}
function bytesToBits(bytes: number[]): number[] {
const bits = [];
for(let t=0; t<bytes.length; t++) {
const b = bytes[t] & 0xFF;
bits.push((b & 128) >> 7);
bits.push((b & 64) >> 6);
bits.push((b & 32) >> 5);
bits.push((b & 16) >> 4);
bits.push((b & 8) >> 3);
bits.push((b & 4) >> 2);
bits.push((b & 2) >> 1);
bits.push((b & 1) >> 0);
}
return bits;
}
function bitsToPulses(bits: number[]): Pulse[] {
const pulses: Pulse[] = [];
for(let t=0; t<bits.length; t++) {
const b = bits[t];
if(b == 0) { pulses.push("S"); pulses.push("L"); /*tapeBits.push(1); tapeBits.push(0); tapeBits.push(1); tapeBits.push(1); tapeBits.push(0); tapeBits.push(0);*/ }
else { pulses.push("S"); pulses.push("S"); pulses.push("S"); /*tapeBits.push(1); tapeBits.push(0); tapeBits.push(1); tapeBits.push(0); tapeBits.push(1); tapeBits.push(0);*/ }
}
return pulses;
}
function pulsesToSamples(tapeBits: Pulse[], tape: Tape): number[] {
const samples = [];
let ptr = 0;
const { SAMPLE_RATE, VOLUME, PULSE_LONG, PULSE_SHORT } = tape;
for(let t=0; t<tapeBits.length; t++) {
if(tapeBits[t]==="S") {
for(;ptr<PULSE_SHORT; ptr++) samples.push(VOLUME); ptr -= PULSE_SHORT;
for(;ptr<PULSE_SHORT; ptr++) samples.push(-VOLUME); ptr -= PULSE_SHORT;
}
else {
for(;ptr<PULSE_LONG; ptr++) samples.push(VOLUME); ptr -= PULSE_LONG;
for(;ptr<PULSE_LONG; ptr++) samples.push(-VOLUME); ptr -= PULSE_LONG;
}
}
return samples;
}
function getGapSamples(tape: Tape): number[] {
const samples = [];
let ptr = 0;
const { VOLUME, PULSE_SHORT } = tape;
for(let t=0; t<11; t++) {
for(;ptr<PULSE_SHORT; ptr++) samples.push(-VOLUME); ptr -= PULSE_SHORT;
}
return samples;
}
main();
import commandLineArgs from 'command-line-args';
export interface CommandLineOptions {
input: string;
output: string;
samplerate: number;
volume: number;
stereoboost: boolean;
invert: boolean;
header: number;
pulsems: number;
turbo: number;
l500: boolean;
l310: boolean;
}
export const options = parseOptions([
{ name: 'input', alias: 'i', type: String, defaultOption: true },
{ name: 'input', alias: 'i', type: String, },
{ name: 'output', alias: 'o', type: String },
{ name: 'samplerate', alias: 's', type: Number },
{ name: 'volume', alias: 'v', type: Number },
{ name: 'stereoboost', type: Boolean },
{ name: 'invert', type: Boolean },
{ name: 'header', type: Number },
{ name: 'pulsems', type: Number },
{ name: 'turbo', alias: 'x', type: Boolean },
{ name: 'turbo-speed', type: Number },
{ name: 'samplerate', alias: 's', type: Number, defaultValue: 96000 },
{ name: 'volume', alias: 'v', type: Number, defaultValue: 1 },
{ name: 'stereoboost', type: Boolean, defaultValue: false },
{ name: 'invert', type: Boolean, defaultValue: false },
{ name: 'header', type: Number, defaultValue: 128 },
{ name: 'pulsems', type: Number, defaultValue: 277 },
{ name: 'turbo', alias: 'x', type: Number, defaultValue: 0 },
{ name: 'l500', type: Boolean },
{ name: 'l310', type: Boolean }
]);
]) as CommandLineOptions;

@@ -27,3 +40,1 @@ function parseOptions(optionDefinitions: commandLineArgs.OptionDefinition[]) {

}

@@ -6,4 +6,4 @@ // ************************************************************************

import { calculate_checksum } from "./checksum";
import { Tape, TurboTape } from "./laser500wav";
import { hi, lo } from "./bytes";
import { Tape, TurboTape } from "./tape_creator";

@@ -10,0 +10,0 @@ export function decodeBitSize(speed: number, SAMPLE_RATE: number, laser500: boolean) {

@@ -1,53 +0,36 @@

import fs from "fs";
import path from "path";
import { hex } from "./bytes";
import { turbo_L310_asm_info } from "./turbo_L310_asm_info";
import { turbo_L500_asm_info } from "./turbo_L500_asm_info";
type AsmInfo = typeof turbo_L310_asm_info | typeof turbo_L500_asm_info;
// get the Z80 turbo loader routine, patching two bytes and
// relocating it at the desidered address
export function getTurboLoader(laser500: boolean, THRESHOLD: number, relocate_address: number, fileType: number) {
const rootname = path.resolve(__dirname, laser500 ? "../turbo_tape/turbo_L500" : "../turbo_tape/turbo_L310");
const loader_program = fs.readFileSync(`${rootname}.bin`);
patch_bytes(loader_program, THRESHOLD, fileType, rootname);
relocate(loader_program, relocate_address, rootname);
const asm_info = laser500 ? turbo_L500_asm_info : turbo_L310_asm_info;
const loader_program = asm_info.code;
patch_bytes(loader_program, THRESHOLD, fileType, asm_info);
const relocated = relocate(loader_program, relocate_address, asm_info);
// for debug purposes
{
const symbols = fs.readFileSync(`${rootname}.sym`).toString();
let set_threshold = getSymbolAddress(symbols, "set_threshold") + relocate_address;
{
let set_threshold = asm_info.symbols["set_threshold"] + relocate_address;
console.log(`const label_set_threshold = 0x${hex(set_threshold,4)};`);
let loop_file = getSymbolAddress(symbols, "loop_file") + relocate_address;
let loop_file = asm_info.symbols["loop_file"] + relocate_address;
console.log(`const label_loop_file = 0x${hex(loop_file,4)};`);
let autorun = getSymbolAddress(symbols, "autorun") + relocate_address;
let autorun = asm_info.symbols["autorun"] + relocate_address;
console.log(`const label_autorun = 0x${hex(autorun,4)};`);
}
return loader_program;
return relocated;
}
function patch_bytes(loader_program: Buffer, THRESHOLD: number, fileType: number, rootname: string) {
// read turbo.sym symbol files
const symbols = fs.readFileSync(`${rootname}.sym`).toString();
// do the needed byte patches
loader_program[getSymbolAddress(symbols, "turbo_load" ) + 1] = fileType;
loader_program[getSymbolAddress(symbols, "set_threshold") + 1] = THRESHOLD;
function patch_bytes(loader_program: number[], THRESHOLD: number, fileType: number, info: AsmInfo ) {
loader_program[info.symbols.turbo_load + 1] = fileType; // patches the file type B: or T:
loader_program[info.symbols.set_threshold + 1] = THRESHOLD; // patches the turbo speed threshold
}
function getSymbolAddress(file: string, symbolname: string) {
const regex = new RegExp(symbolname + "\\s*=\\s*\\$(?<address>[0-9a-fA-F]{4})", "g");
const match = regex.exec(file);
if(match === null || match.groups === undefined) throw `can't find ${symbolname} label in .sym file`;
// get label address from hex format
const address = Number.parseInt(match.groups["address"], 16);
return address;
}
// relocate the Z80 turbo loader routine at the desidered

@@ -58,12 +41,12 @@ // destination by changing all the interested addresses.

function relocate(loader_program: Buffer, relocate_address: number, rootname: string) {
const reloc_info = fs.readFileSync(`${rootname}.reloc`);
for(let t=0; t<reloc_info.length; t+=2) {
const patch_address = reloc_info.readUInt16LE(t);
const offset_value = loader_program.readUInt16LE(patch_address);
const relocated_value = offset_value + relocate_address;
loader_program.writeUInt16LE(relocated_value, patch_address);
//console.log(`${t}: [${patch_address}] = ${offset_value.toString(16)} -> ${relocated_value.toString(16)}`);
}
function relocate(loader_program: number[], relocate_address: number, asm_info: AsmInfo) {
const reloc_table = asm_info.reloc;
const lp = Buffer.from(loader_program);
for(let t=0; t<reloc_table.length; t++) {
const index = reloc_table[t];
const address = lp.readUInt16LE(index);
const relocated_value = address + relocate_address;
lp.writeUInt16LE(relocated_value, index);
}
return lp;
}

@@ -43,3 +43,3 @@ /*

interface VZInfo {
export interface VZInfo {
filename: string;

@@ -46,0 +46,0 @@ type: VZFILETYPE;

@@ -52,3 +52,3 @@ {

/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */

@@ -55,0 +55,0 @@ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc