-
Notifications
You must be signed in to change notification settings - Fork 0
/
tzx.js
106 lines (91 loc) · 2.54 KB
/
tzx.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Attach TZX header for encoded zx81 tape.
// Ref: http://www.worldofspectrum.org/TZXformat.html
function ascii(str) {
let arr = [];
for (let idx in str) {
arr.push(str.charCodeAt(idx));
}
return arr;
}
function word(value) {
let arr = [];
const lobyte = value % 0x100;
arr.push(lobyte);
arr.push((value - lobyte) / 0x100);
return arr;
}
function dword(value) {
let arr = [];
for (var b = 0; b < 4; b++) {
const lobyte = value % 0x100;
arr.push(lobyte);
value = (value - lobyte) / 0x100;
}
return arr;
}
function encode(rawData) {
const headerBlock = [ascii("ZXTape!"), 0x1a, 1, 20];
const text = ascii("github.com/mvindahl/zx81-dat-tape-reader");
const textBlock = [0x30, text.length, text];
// The generic data block header is the most complicated part. It's reverse engineered from
// similar tools and spcified according to the spec.
const dataBlockContents = _.flatten([
word(0), // Pause after this block (ms)
dword(0), // Total number of symbols in pilot/sync block (can be 0)
0, // Maximum number of pulses per pilot/sync symbol NPP
0, // Number of pilot/sync symbols in the alphabet table (0=256)
dword(8 * rawData.length), // Total number of symbols in data stream (can be 0)
0x12, // Maximum number of pulses per data symbol NPD
2, // Number of data symbols in the alphabet table (0=256) ASD
// Zero bit pulse:
3, // 0b11: force high level
// Durations: top state, bottom state, top state etc.
// The numbers are in z80 clock cycles (it runs at 3.5 Mhz)
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x1251),
word(0x0000),
word(0x0000),
word(0x0000),
word(0x0000), // zero length terminated sequence
word(0x0000),
word(0x0000),
word(0x0000),
word(0x0000),
word(0x0000),
word(0x0000),
// One bit pulse:
3, // 0b11: force high level
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x0208),
word(0x0212),
word(0x1251),
// The actual data now follows
rawData,
]);
const dataBlock = [0x19, dword(dataBlockContents.length), dataBlockContents];
const tzxEncodedBytes = _.flattenDeep([headerBlock, textBlock, dataBlock]);
return tzxEncodedBytes;
}
module.exports = {
encode: encode,
};