Remy Sharp
October 06, 2017
180

# Recreating the ZX Spectrum loader with Web APIs

This talk is about using new technology to replicate an old, reasonably useless, technology: replicating the ZX Spectrum tape loader audio and visuals (but without the tape…), and sharing what I learned along the way.

October 06, 2017

## Transcript

11. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave
12. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave * 1 bit of data = 2 equal pulses
13. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave * 1 bit of data = 2 equal pulses * binary 0 = 855T
14. ### * 3.5Hmz * 1 t-state = 1/3,500,000 * 1T =

1/2 pulse wave * 1 bit of data = 2 equal pulses * binary 0 = 855T * binary 1 = 1710T

18. ### const T = 1/3500000; const SAMPLE_RATE = 44100; // 44.1Mhz

const ONE = 1710 * 2; // 2 = HIGH + LOW const asHz = pulse => 1 / (T * pulse); const toRadian = hz => hz * Math.PI * 2; function generateSample(output) { const length = ONE * T * SAMPLE_RATE; for (let i = 0; i < length; i++) { const time = i / SAMPLE_RATE; const angle = time * toRadian(asHz(ONE)); // store a square wave output[i] = Math.sin(angle) < 0 ? -1 : 1; } }
19. ### 000100110000000000000000000000110110001101101111011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011 011101000000000011111111001000011011101011000011001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101 001111000010000000010001011110101100001100000001010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011 010101000010000000110100000100000000011100010001000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000 100100001001000010010000100100001001000010010000000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000 100100001001000010010000100100001001000000010110000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000 100100001001000010010000100100000000000000011000001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110 011001010110001101110100001101000010000000100000011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011 010111000011111000000010110011010000000100010110000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000 110011010011110000100000110010010001011000000110000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110

000010100000110010010000100100001001000010010000100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100 000011001001000010010000100100001001000010010000100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100 100100001001000010010000100100001001000010010000100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110 011111100011110000011000000000001000010100010011000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000 000000000000000000000000100000001011101101110100000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110 110000110000000100001100000000001100110100111100001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011 010011110100111001001110010001010100001101010100001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000 100100000001011000001011000011001001000010010000100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000 000101100000110100001100100100001001000010010000100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110 000011110000110010010000100100001001000010010000100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000 000000110110001101101111011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111 001000011011101011000011001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001 011110101100001100000001010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100 000100000000011100010001000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000 100100001001000010010000000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000 100100001001000000010110000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000 100100000000000000011000001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110011001010110001101110100 001101000010000000100000011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011010111000011111000000010 110011010000000100010110000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000110011010011110000100000 110010010001011000000110000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110000010100000110010010000 100100001001000010010000100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100000011001001000010010000 100100001001000010010000100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100100100001001000010010000 100100001001000010010000100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110011111100011110000011000 000000001000010100010011000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000000000000000000000000000 100000001011101101110100000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110110000110000000100001100 000000001100110100111100001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011010011110100111001001110 010001010100001101010100001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000100100000001011000001011 000011001001000010010000100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000000101100000110100001100 100100001001000010010000100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110000011110000110010010000 100100001001000010010000100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000000000110110001101101111 011011100110111001100101011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111001000011011101011000011 001000100111101101011100001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001011110101100001100000001 010000000000000011001101001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100000100000000011100010001 000000010001011000001010000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000100100001001000010010000 000101100000110000001100100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000100100001001000000010110 000011100000110010010000100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000100100000000000000011000 001111000111111001111110001111000001100000000000100001010001001100000000000000000000001101100011011011110110111001101110011001010110001101110100001101000010000000100000 011100100000000000000000000000000000000010000000101110110111010000000000111111110010000110111010110000110010001001111011010111000011111000000010110011010000000100010110 000100010110111011000011000000010000110000000000110011010011110000100000000100010111101011000011000000010100000000000000110011010011110000100000110010010001011000000110 000011000100001101001111010011100100111001000101010000110101010000100000001101000001000000000111000100010000000100010110000010100000110010010000100100001001000010010000 100100001001000010010000000101100000101100001100100100001001000010010000100100001001000010010000100100000001011000001100000011001001000010010000100100001001000010010000 100100001001000000010110000011010000110010010000100100001001000010010000100100001001000010010000000101100000111000001100100100001001000010010000100100001001000010010000 100100000001011000001111000011001001000010010000100100001001000010010000100100001001000000000000000110000011110001111110011111100011110000011000000000001000010100010011 000000000000000000000011011000110110111101101110011011100110010101100011011101000011010000100000001000000111001000000000000000000000000000000000100000001011101101110100 000000001111111100100001101110101100001100100010011110110101110000111110000000101100110100000001000101100001000101101110110000110000000100001100000000001100110100111100 001000000001000101111010110000110000000101000000000000001100110100111100001000001100100100010110000001100000110001000011010011110100111001001110010001010100001101010100 001000000011010000010000000001110001000100000001000101100000101000001100100100001001000010010000100100001001000010010000100100000001011000001011000011001001000010010000 100100001001000010010000100100001001000000010110000011000000110010010000100100001001000010010000100100001001000010010000000101100000110100001100100100001001000010010000 100100001001000010010000100100000001011000001110000011001001000010010000100100001001000010010000100100001001000000010110000011110000110010010000100100001001000010010000 100100001001000010010000000000000001100000111100011111100111111000111100000110000000000010000101000100110000000000000000000000110110001101101111011011100110111001100101 011000110111010000110100001000000010000001110010000000000000000000000000000000001000000010111011011101000000000011111111001000011011101011000011001000100111101101011100 001111100000001011001101000000010001011000010001011011101100001100000001000011000000000011001101001111000010000000010001011110101100001100000001010000000000000011001101 001111000010000011001001000101100000011000001100010000110100111101001110010011100100010101000011010101000010000000110100000100000000011100010001000000010001011000001010 000011001001000010010000100100001001000010010000100100001001000000010110000010110000110010010000100100001001000010010000100100001001000010010000000101100000110000001100 100100001001000010010000100100001001000010010000100100000001011000001101000011001001000010010000100100001001000010010000100100001001000000010110000011100000110010010000 100100001001000010010000100100001001000010010000000101100000111100001100100100001001000010010000100100001001000010010000100100000000000000011000001111000111111001111110

21. ### this.node = ctx.createScriptProcessor(bufferSize, 1, 1); this.node.onaudioprocess = audioProcessingEvent => {

const channel = 0; const inputBuffer = audioProcessingEvent.inputBuffer; const input = inputBuffer.getChannelData(channel); // then we'll read the values for own processing this.read(input, performance.now()); // copy the input directly across to the output const outputBuffer = audioProcessingEvent.outputBuffer; const output = outputBuffer.getChannelData(channel); inputBuffer.copyFromChannel(output, channel, channel); }; // constructor continues...

30. ### * Draw image into canvas * export canvas as blob

* File Reader API to read as binary string * convert char to binary
31. ### * Draw image into canvas * export canvas as blob

* File Reader API to read as binary string * convert char to binary * TURN binary INTO audio
32. ### function imageToBlob(img) { const c = document.createElement('canvas'); const ctx =

c.getContext('2d'); c.width = img.width; c.height = img.height; ctx.drawImage(img, 0, 0); return new Promise(resolve => { c.toBlob(file => resolve(file)); }) }
33. ### function imageToBlob(img) { const c = document.createElement('canvas'); const ctx =

c.getContext('2d'); c.width = img.width; c.height = img.height; ctx.drawImage(img, 0, 0); return new Promise(resolve => { c.toBlob(file => resolve(file)); }) }
34. ### function fileToBinary(blob) { return new Promise(resolve => { const reader

= new window.FileReader(); reader.onloadend = () => { const binary = []; const result = reader.result; for (let i = 0; i < result.length; i++) { const char = result[i]; binary.push(charToBinary(char)); } resolve( binary.reduce((acc, byte) => { return acc.concat(byte.split('')); }, []) ); }; reader.readAsBinaryString(blob); }); }
35. ### function fileToBinary(blob) { return new Promise(resolve => { const reader

= new window.FileReader(); reader.onloadend = () => { const binary = []; const result = reader.result; for (let i = 0; i < result.length; i++) { const char = result[i]; binary.push(charToBinary(char)); } resolve( binary.reduce((acc, byte) => { return acc.concat(byte.split('')); }, []) ); }; reader.readAsBinaryString(blob); }); }
36. ### function charToBinary(chr) { return char .charCodeAt(0) // R = 82

.toString(2) // 82 = 1010010 .padStart(8, '0'); // 1010010 = 01010010 }

pulse
45. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len
46. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1
47. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits
48. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits * <Repeat>
49. ### * SAMple audio * detect edge * length = 1/2

pulse * read 2nd pulse, ?=== len * Bit = len == 855 ? 0 : 1 * Make bytes from 8 bits * <Repeat> * make image from bytes
50. ### export function loadEdge1(buffer) { if (!buffer.length) { return null; }

let last = null; let point = buffer.shift(); pulseBuffer.push(point); do { // search for when the buffer point crosses the zero threshold if (last !== null) { // important: when we hit an edge, the data doesn't include the edge if (edge(point, last)) { // create a new array and return that instead const res = Array.from(pulseBuffer); // reset the buffer pulseBuffer = []; return res; } } pulseBuffer.push(point); last = point; } while ((point = buffer.shift())); return null; // no edge found }

52. ### PULSE = 1 / sample rate * (HI count) BIT

= PULSE === 855 ? 0 : 1;

54. ### 4b FF 80 32 00 a1 42 02 BB 80

c3 20 d1 d9 ff 02