-
Notifications
You must be signed in to change notification settings - Fork 0
/
CSW2CDT.C
2401 lines (2322 loc) · 110 KB
/
CSW2CDT.C
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//@@@@ //@@@@@//@@ //@@//@@@@ //@@@@//@@@@@ //@@@@@@ -----------------------
//@@//@@/@@ //@@/@@ //@@/@@//@@//@@//@@//@@/@@//@/@@/@ codec of CDT/TZX v1.21
//@@ //@@ //@@ //@@ //@@/@@ //@@//@@ //@@ tape files from CSW v1
//@@ //@@@@@//@@/@/@@//@@@@//@@ //@@//@@ //@@ tape samples, coded by
//@@ //@@/@@@@@@@/@@ //@@ //@@//@@ //@@ Cesar Nicolas-Gonzalez
//@@//@@/@@ //@@/@@@/@@@/@@//@@//@@//@@//@@/@@ //@@ since 2020/05/01-18:50
//@@@@ //@@@@@//@@ //@@/@@@@@@ //@@@@//@@@@@ //@@@@ ------------------------
#define MY_VERSION "20240328"
#define MY_LICENSE "Copyright (C) 2020-2023 Cesar Nicolas-Gonzalez"
#define GPL_3_INFO \
"This program comes with ABSOLUTELY NO WARRANTY; for more details" "\n" \
"please see the GNU General Public License. This is free software" "\n" \
"and you are welcome to redistribute it under certain conditions."
/* This notice applies to the source code of CSW2CDT and its binaries.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact information: <mailto:cngsoft@gmail.com> */
#include <stdio.h> // fopen,printf...
#include <stdlib.h> // atoll,malloc...
#include <string.h> // memcmp,strcmp...
#include <time.h> // time_t,strftime...
#define length(x) (sizeof(x)/sizeof*(x))
unsigned char buffer[512]; FILE *fi,*fo,*fu;
char *autosuffix(char *t,char *s,char *x) // returns a valid target path, generating it from the source and an extension if required
{
if (t) return t; else if (!s) return NULL;
if ((char*)buffer!=s) strcpy(buffer,s);
if ((t=strrchr(buffer,'.'))&&(!(s=strrchr(buffer,
#ifdef _WIN32
'\\'
#else
'/'
#endif
))||t>s)) return strcpy(t,x),buffer; // override old suffix
return strcat(buffer,x); // append new suffix
}
int ucase(int i) { return i>='a'&&i<='z'?i-32:i; } // returns ASCII upper case
int endswith(char *s,char *x) // does the string `s` end in the suffix `x`? returns zero if FALSE
{
int i=strlen(s),j=strlen(x);
if (i>=j) for (s+=i,x+=j;j&&ucase(*--s)==ucase(*--x);) --j;
return !j;
}
char topdigit(int i) { return (i+(i<10?'0':'7')); } // returns "fake" decimals beyond 9
// I/O file operations, buffering and Intel lil-endian integer logic -------- //
// non-buffered shortcuts
#define fget1() fgetc(fi)
#define fput1(n) fputc((n),fo)
#define fread1(t,i) fread((t),1,(i),fi)
#define fwrite1(t,i) fwrite((t),1,(i),fo)
int fget4(void) { int i=fget1(); i|=fget1()<<8; i|=fget1()<<16; return (fget1()<<24)|i; }
int fput4(int i) { fput1(i); fput1(i>>8); fput1(i>>16); return fput1(i>>24); }
// buffered I/O operations
unsigned char tsrc[1<<12],ttgt[1<<12]; int isrc=0,ilen=0,itgt=0;
#define flush1() (fwrite1(ttgt,itgt),itgt=0)
int frecv1(void) { while (isrc>=ilen) if (isrc-=ilen,!(ilen=fread1(tsrc,sizeof(tsrc)))) return -1; return tsrc[isrc++]; }
int frecv2(void) { int i=frecv1();return i|(frecv1()<<8); }
int frecv3(void) { int i=frecv1(); i|=frecv1()<<8; return i|(frecv1()<<16); }
int frecv4(void) { int i=frecv1(); i|=frecv1()<<8; i|=frecv1()<<16; return i|(frecv1()<<24); }
int ftell1(void) { return ftell(fi)-ilen+isrc; }
void fseek1(int i) { fseek(fi,i&-sizeof(tsrc),SEEK_SET); ilen=fread1(tsrc,sizeof(tsrc)); isrc=i&(sizeof(tsrc)-1); }
void fskip1(int i) { isrc+=i; } // should really handle negative cases beyond -1 here...
int fsend1(int i) { ttgt[itgt++]=i; if (itgt>=length(ttgt)) { if (!flush1()) return -1; itgt=0; } return i; }
int fsend2(int i) { fsend1(i); return fsend1(i>>8); }
int fsend3(int i) { fsend1(i); fsend1(i>>8); return fsend1(i>>16); }
int fsend4(int i) { fsend1(i); fsend1(i>>8); fsend1(i>>16); return fsend1(i>>24); }
// common variables and functions ------------------------------------------- //
#define ZXKHZ (3500)
#define ZX1HZ (3500000)
int flag_e=5,hz=44100,ordinal=0,seconds=0; char tape_sign=0;
const char csw24b[24]="Compressed Square Wave\032\001";
int tape_head[256][2]; // head pairs: {length, weight}; can't be "signed short", legit tones can go beyond 32768 edges, f.e. 45000 in MSX hi-speed header blocks :-/
int tape_word[256][32]; // word items: {type, weight..., -1}; can't be "unsigned short" because we need "-1" as the end marker: 0 is a legit value!
#define tape_bit0l tape_word[0][0] // BIT0 pair:
#define tape_bit0w tape_word[0][1] // {length, weight}
#define tape_bit1l tape_word[1][0] // BIT1 pair:
#define tape_bit1w tape_word[1][1] // {length, weight}
#define tape_datas tape_twotwo // 99% cases use x2:x2
int tape_heads=0,tape_waves=0,tape_kansas=0; int tape_tails=0; // amount of tape block primitives
int tape_twofor=0,tape_twotwo=0,tape_onetwo=0,tape_oneone=0; // lengths of DATA primitives: 4:2 (FMx2), 2:2 (normal), 2:1 (FM) and 1:1 (halved)
int tape_wave,tape_width; // parameters of the primitives WAVE and DATA: sample weight in T and word bitwidth
int tape_kansasi,tape_kansasin,tape_kansaso,tape_kansason,tape_kansasio,tape_kansasn[2]; // KANSAS CITY parameters
unsigned char tape_temp[1<<18]; // when we're decoding we must store the bytes before sending them to file
#define APPROX2(x) (((x)+1)>>1)
#define APPROX4(x) (((x)+2)>>2)
#define APPROX8(x) (((x)+4)>>3)
#define APPROX16(x) (((x)+8)>>4)
#define APPROX32(x) (((x)+16)>>5)
#define APPROX64(x) (((x)+32)>>6)
int approx(int x,int y) { return (x+(y>>1))/y; } // division and rounding of integer/integer x/y
int approxy(int a,int b,int y) { long long int x=a; x*=b; return (x+(y>>1))/y; } // ditto, a*b/c
int approxyz(int a,int b,int c,int d) { long long int x=a,y=c; x*=b,y*=d; return (x+(y>>1))/y; }
int within100(int x,int y,int z) { z=approxy(y,z,100); return x+z>=y&&x<=y+z; }
char tape_pzx=0,tape_tzx=20,tape_tsx=0; // TZX (PZX if nonzero) 1.20 by default, but it can be 0 (disable extended blocks such as $19)
char *tape_name="",tape_closegroup=0; int tape_groupcount=0; // encoding description (if any), "close block" flag and open block count
void uprintl(void) { fprintf(fu,"-------------------------------------------------------------------------------\n"); }
void uprinth(void) { fprintf(fu,tape_pzx?"##### type encoding length check\n":
"##### type encoding pilot syncs BIT0:BIT1 pct length check pause\n"); }
void uprintm(void) { fprintf(fu,tape_pzx?"##### type contents millis\n":
"##### type pilot syncs BIT0:BIT1 pct length pause millis\n"); }
char uprint_t=1; // timeline or ordinals?
void do_print(void)
{
++ordinal; if (uprint_t)
fprintf(fu,"%c%c:%02d ",topdigit(seconds/600),((seconds/60)%10)+48,seconds%60);
else
fprintf(fu,"%c%04d ",topdigit(ordinal/10000),ordinal%10000);
}
#define uprints(s) (do_print(),fprintf(fu,s))
#define uprintf(s,...) (do_print(),fprintf(fu,s,__VA_ARGS__))
void uprintz(int i)
{
uprintf("STATS: %d bytes, ",i);
if (uprint_t)
fprintf(fu,"%d blocks\n",ordinal-1);
else
fprintf(fu,"%d:%02d time\n",seconds/60,seconds%60);
}
// encoding-only vars and funcs --------------------------------------------- //
int oo=0,samples,*sample;
void do_clock(void) { static int ooo=0,tt=0; while (ooo<oo) tt+=sample[ooo++]; seconds+=tt/hz; tt%=hz; } // exactly what it says: tick the clock!
//#define ZX2HZ(x) approxy((x),hz,ZX1HZ)
#define ZX2HZ(x) zx2hz[x] // x must stay within length(zx2hz)!
#define HZ2ZX(x) approxy((x),ZX1HZ,hz)
#define HZ2MS(x) approxy((x),+1000,hz)
#define APPROX_HZ2ZX(x,y) approxyz((x),ZX1HZ,hz,(y))
#define APPROX_ZX2HZ(x,y) approxyz((x),hz,ZX1HZ,(y))
int zx2hz[1<<14]; // i.e. up to 16383 T; 8191 T aren't enough (f.e. Keytone)
void zx2hz_setup(void) { for (int i=0;i<length(zx2hz);++i) zx2hz[i]=approxy(i,hz,ZX1HZ); }
// TZX1 and PZX1 output logic, based on a generalised block: a HEAD made of TONES and EDGES, a body made of WORDS or WAVE, and a silent TAIL
int flag_m=70,flag_t=70,flag_z=5,tape_checksum,tape_howtosum;
int tape_parity8b(unsigned char *s,int l) // calculates XOR8 of a string of bytes
{ for (;l>0;--l) tape_checksum^=*s++; return tape_checksum; }
int tape_ccitt256(unsigned char *s,int l) // checks CCITT of a string of 256-byte chunks; `tape_checksum` is the amount of mistakes
{
for (;(l-=258)>=0;)
{
int t=-1; for (int n=256;n;++s,--n)
t=(t<<4)^(4129*(((*s>>4)^(t>>12))&15)),
t=(t<<4)^(4129*(( *s ^(t>>12))&15));
t^=*s++<<8; if (~(t^=*s++)&65535) ++tape_checksum;
}
return tape_checksum;
}
char *tape_is_ok(void)
{
int i=-1; switch (tape_howtosum)
{
case 1: // amstrad: amount of errors must be ZERO
i=tape_checksum;
break;
case 2: // spectrum: the low 8 bits must be ZERO
i=tape_checksum&255;
break;
case 3: // negative spectrum: the low 8 bits must be ONE
i=~tape_checksum&255;
break;
}
return i?"--":"OK";
}
int chrcmp(char *t,char k,int n) { while (n>0) { if (*t!=k) return n; ++t,--n; } return 0; }
int tape_tzx12n13[2]={0,0}; char flag_ll=0;
void tape_export_tzx12n13(void) // shows an abridged log instead of the normal style
{ if (tape_tzx12n13[0]) {
if (flag_ll) uprintf("PULSES %-10s%5dx%04d --------- --------- --- -------- -- -- ------\n",tape_name,tape_tzx12n13[0],approx(tape_tzx12n13[1],tape_tzx12n13[0]));
} tape_tzx12n13[0]=tape_tzx12n13[1]=0; }
void tape_export_tzx12(int j,int k) // stores a series of `k` edges of `j` T each; not to be used directly
{ if (k) {
fsend1(0X12),fsend2(j),fsend2(k);
if (flag_ll) tape_tzx12n13[0]+=k,tape_tzx12n13[1]+=k*j;
else uprintf("TONE %-10s%5dx%04d --------- --------- --- -------- -- -- ------\n",tape_name,k,j);
} }
void tape_export_tzx13(int j,int k) // stores a set of `k` edges starting at HEAD[`j`]; not to be used directly
{ if (k) {
fsend1(0X13),fsend1(k);
int t=0; for (int i=0;i<k;++i) fsend2(tape_head[j][1]),t+=tape_head[j][1],++j;
if (flag_ll) tape_tzx12n13[0]+=k,tape_tzx12n13[1]+=t;
else uprintf("SYNC %-10s ---------%5dx%04d --------- --- -------- -- -- ------\n",tape_name,k,approx(t,k));
} }
#define TAPE_TWOX1 do{ if (q&1) o|=oq; }while(0)
#define TAPE_TWOX2 do{ if (!(oq>>=2)) fsend1(o),o=0,oq=192; }while(0)
#define TAPE_TWOX9 do{ if (oq<192) fsend1(o); }while(0) // send last byte if available
#define TAPE_ONEX1 do{ if (q&1) o|=oq; }while(0)
#define TAPE_ONEX2 do{ if (!(oq>>=1)) fsend1(o),o=0,oq=128; }while(0)
#define TAPE_ONEX9 do{ if (oq<128) fsend1(o); }while(0) // send last byte if available
#define tape_onetwo2length(l) (l<<1) // all bits are stored as two samples!
#define tape_twofor2length(l) (l<<2) // all bits are stored as four samples!
int tape_oneone2length(int l) // bit 0 is one sample, bit 1 is two samples
{ int i=0,o=0; unsigned char iq=128; while (l--) { if (tape_temp[i]&iq) ++o; ++o; if (!(iq>>=1)) iq=128,++i; } return o; }
int tape_twofor2sample(char q,int l) // write samples of a 4:2 data block; return the new output status (only the lowest bit matters!)
{
int i=0; unsigned char iq=128,oq=192,o=0; while (l-->0)
{
TAPE_TWOX1; if (!(tape_temp[i]&iq)) ++q; // i.e. always send two samples, but the second may change
oq>>=2; TAPE_TWOX1; TAPE_TWOX2; ++q; if (!(iq>>=1)) iq=128,++i; // we can reduce a TAPE_TWOX2 to oq>>=2
}
TAPE_TWOX9; return q;
}
int tape_onetwo2sample(char q,int l) // write samples of a 2:1 data block; return the new output status (only the lowest bit matters!)
{
int i=0; unsigned char iq=128,oq=128,o=0; while (l-->0)
{
TAPE_ONEX1; if (!(tape_temp[i]&iq)) ++q; // i.e. always send two samples, but the second may change
oq>>=1; TAPE_ONEX1; TAPE_ONEX2; ++q; if (!(iq>>=1)) iq=128,++i; // we can reduce a TAPE_ONEX2 to oq>>=1
}
TAPE_ONEX9; return q;
}
int tape_oneone2sample(char q,int l) // write samples of a 1:1 data block; return the new output status (ditto!)
{
int i=0; unsigned char iq=128,oq=128,o=0; while (l-->0)
{
if (tape_temp[i]&iq) { TAPE_ONEX1; TAPE_ONEX2; } // i.e. send either one or two equal samples
TAPE_ONEX1; TAPE_ONEX2; ++q; if (!(iq>>=1)) iq=128,++i; // we must always check bits with TAPE_ONEX2
}
TAPE_ONEX9; return q;
}
void tape_export_pascal4(char *s) { fsend4(strlen(s)); while (*s) fsend1(*s++); }
void tape_export_pascal1(char *s) { fsend1(strlen(s)); while (*s) fsend1(*s++); }
void tape_export_newblock(char *s) // stores the beginning of a super-block (i.e. a container of sub-blocks)
{
tape_closegroup=-1;
sprintf(buffer,"%s %04d",tape_name,++tape_groupcount);
uprintf("GROUP: %s\n",buffer); if (tape_pzx) // PZX format?
fsend4(0X53575242),tape_export_pascal4(buffer); // "BRWS"
else
fsend1(0X21),tape_export_pascal1(buffer);
}
#define tape_export_endblock() (tape_closegroup=tape_closegroup?1:0) // tags an open super-block as finished
void tape_export_oldblock(void) // stores the end of a super-block
{
tape_closegroup=0;
uprints("GROUP.\n"); if (tape_pzx) // PZX format?
fsend4(0X42525753),fsend4(0); // "SWRB" (end of BRWS)
else
fsend1(0X22);
}
void tape_export_infos(char *s) // stores an information text string
{
uprintf("INFOS: %s\n",s); if (tape_pzx) // PZX format?
fsend4(0X54584554),tape_export_pascal4(s); // "TEXT"
else
fsend1(0X30),tape_export_pascal1(s);
}
void tape_export(void) // exports a general tape block and flushes the primitives' data structures
{
static char q=1; // output status: only bit 0 matters
if (tape_tails<0) tape_tails=0; // sanity check!
if (tape_pzx) // PZX format?
{
if (tape_heads) // "PULS"?
{
fsend4(0X534C5550);
int j=0; // actual block size
int n=10; // fields in the log
char *t=buffer+sprintf(buffer,"%-10s",tape_name);
if ((q&1)) ++j; // HIGH first signal
for (int i=0;i<tape_heads;++i)
if (tape_head[i][0]>1)
{ if (j+=2,(n-=2)>=0) t+=sprintf(t,(tape_head[i][0]<10000?" %04dx%04d":"%5dx%04d"),tape_head[i][0],tape_head[i][1]); }
else
{ if (++j,--n>=0) t+=sprintf(t," %04d",tape_head[i][1]); }
if (n<0) sprintf(t," +%4d",-n);
uprintf("PULSES %s\n",buffer);
fsend4(j<<1);
if ((q&1)) fsend2(0); // first signal HIGH is encoded with a ZERO here
for (int i=0;i<tape_heads;++i)
{
if ((j=tape_head[i][0])>1) fsend2(j+0X8000); // pilot? set bit 15!
fsend2(tape_head[i][1]);
q+=j; // the sign changes as many times as there are edges
}
}
if (tape_twofor|tape_twotwo|tape_onetwo|tape_oneone) // "DATA"?
{
fsend4(0X41544144);
int i,j=approx(hz,1000),k; // not 50! (see ZXKHZ below)
char *t=buffer+sprintf(buffer,"%-10s",tape_name);
if (i=tape_twofor) // 4:2?
k=8+4;
else if (i=tape_twotwo) // 2:2?
k=4+4;
else if (i=tape_onetwo) // 2:1?
k=2+4;
else // if (tape_oneone) // 1:1?
i=tape_oneone,k=2+2;
if ((q&1)&&tape_tails>=j)
tape_tails-=j,j=ZXKHZ; // create tail from silence
else
j=0; // no tail, do nothing
t+=sprintf(t," BIT0 =%3dx%04d BIT1 =%3dx%04d%4d%%%5d",tape_bit0l,tape_bit0w,tape_bit1l,tape_bit1w,
approx(171000+342000,tape_bit0l*tape_bit0w+tape_bit1l*tape_bit1w),j);
uprintf("DATA %s%8d:%d %02X %s\n",buffer,(i+7)>>3,((i-1)&7)+1,tape_checksum&255,tape_is_ok());
i=(i+7)>>3; // turn bits into bytes
fsend4(i+8+k);
fsend4((tape_twofor|tape_twotwo|tape_onetwo|tape_oneone)+(q<<31));
fsend2(j);
if (j) j=1; // if there's a pause, the signal changes once at the end
if (tape_twofor) // 4:2?
{
fsend1(4); // =tape_bit0l
fsend1(2); // =tape_bit1l
fsend2(tape_bit0w);
fsend2(tape_bit0w);
fsend2(tape_bit0w);
fsend2(tape_bit0w);
fsend2(tape_bit1w);
fsend2(tape_bit1w);
// signals always come in pairs, final sign stays the same
}
else if (tape_twotwo) // 2:2?
{
fsend1(2); // =tape_bit0l
fsend1(2); // =tape_bit1l
fsend2(tape_bit0w);
fsend2(tape_bit0w);
fsend2(tape_bit1w);
fsend2(tape_bit1w);
// signals always come in pairs, final sign stays the same
}
else if (tape_onetwo) // 2:1?
{
fsend1(2); // =tape_bit0l
fsend1(1); // =tape_bit1l
fsend2(tape_bit0w);
fsend2(tape_bit0w);
fsend2(tape_bit1w);
for (int x=i>>3;x-->0;)
for (unsigned char y=128;y;y>>=1)
if (!(tape_temp[x]&y)) ++j; // single signal? actual change!
}
else //if (tape_oneone) // 1:1?
{
fsend1(1); // =tape_bit0l
fsend1(1); // =tape_bit1l
fsend2(tape_bit0w);
fsend2(tape_bit1w);
j+=tape_oneone; // all signals are single: as many actual changes as signals
}
if (j&1) q=0; // set the signal's final status
flush1(); fwrite1(tape_temp,i);
}
if (tape_tails) // "PAUS"?
{
long long int j=tape_tails; j=(j*ZX1HZ+(hz>>1))/hz;
do
{
int i=/*(q&1)?ZX1HZ/50:*/0X7FFFFFFF; if (i>j) i=j;
fsend4(0X53554150); fsend4(4); fsend4(i+(q<<31)); j-=i; q=0;
uprintf("PAUSE ---------- %09d%8dms\n",i,approx(i,ZXKHZ));
}
while (j); // it loops every 10 minutes and 14 seconds :-/
++q;
}
}
else // TZX format, 1.00 if possible
{
tape_tails=HZ2MS(tape_tails);
tape_tzx12n13[0]=tape_tzx12n13[1]=0;
int i,j=0,k=0,l,h; for (i=0;i<tape_heads;++i)
q+=tape_head[i][0]; // toggle the output as many times as the pulses change!
if (tape_kansas&&tape_heads<2) // KANSAS CITY bytes; limited to the unofficial TSX (TZX 1.21) format!
{
// this block won't be generated if tape_pzx is true or tape_tzx is false!
fsend1(0X4B);
fsend4(tape_kansas+12);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i; fsend2(i);
if (tape_heads)
fsend2(tape_head[0][1]),fsend2(tape_head[0][0]);
else // no tone
fsend2(tape_head[0][1]=tape_bit0w),fsend2(tape_head[0][0]=0);
fsend2(tape_bit0w);
fsend2(tape_bit1w);
fsend1((tape_kansasn[0]<<4)+(tape_kansasn[1]&15));
fsend1((tape_kansasin<<6)+(tape_kansasi<<5)+(tape_kansason<<3)+(tape_kansaso<<2)+(tape_kansasio&1));
flush1(); fwrite1(tape_temp,tape_kansas);
uprintf("KANSAS %-10s%5dx%04d %c-(%c:%c)-%c %04d:%04d%4d%7d:8 -- --%7d\n",
tape_name,tape_head[0][0],tape_head[0][1],48+tape_kansasin,48+tape_bit0l,48+tape_bit1l,48+tape_kansason,tape_bit0w,tape_bit1w,
approx(512800,tape_bit0w*tape_bit0l+tape_bit1w*tape_bit1l),tape_kansas,i);
tape_heads=0;
}
for (i=0;i<tape_heads;++i)
{
if (((i+3==tape_heads&&tape_head[i+1][0]==1&&tape_head[i+2][0]==1) // ... PILOT + SYNCS + DATAS [+ PAUSE]?
||(i+2==tape_heads&&tape_head[i+1][0]==2))&&/*tape_head[i][0]>1&&*/tape_twotwo) // 2-edge TONE = two SYNCS
{
tape_export_tzx13(j,k); tape_export_tzx12n13(); // PURE SYNC (EARLY)
j=tape_bit0w,k=tape_bit1w;
int h1,h2; if (i+2==tape_heads)
h1=h2=tape_head[i+1][1]; // 2-edge tone
else
h1=tape_head[i+1][1],h2=tape_head[i+2][1];
int z=tape_temp[0]<128?8062:3222;
if (tape_twotwo<(1<<19)&&!(tape_twotwo&7)
&&within100(j+k,855+1710,flag_z)
&&within100(tape_head[i+0][0],z,flag_z)
&&within100(tape_head[i+0][1],2168,flag_z)
&&within100(h1+h2,667+735,flag_z))
{
fsend1(0X10); tape_head[i+0][0]=z; z=i;
i=tape_tails; if (i>65535) i=65535; tape_tails-=i; fsend2(i);
l=(tape_twotwo+7)>>3; fsend2(l);
flush1(); fwrite1(tape_temp,l);
uprintf("NORMAL %-10s%5dx2168 0667,0735 0855:1710 100%7d:8 %02X %s%7d\n",
tape_name,tape_head[z+0][0],l,tape_checksum&255,tape_is_ok(),i);
}
else
{
fsend1(0X11); z=i;
fsend2(tape_head[z+0][1]);
fsend2(h1);
fsend2(h2);
fsend2(j); fsend2(k);
fsend2(tape_head[z+0][0]);
h=((tape_twotwo+7)&7)+1; fsend1(h);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i; fsend2(i);
l=(tape_twotwo+7)>>3; fsend3(l);
flush1(); fwrite1(tape_temp,l);
uprintf("CUSTOM %-10s%5dx%04d %04d,%04d %04d:%04d%4d%7d:%d %02X %s%7d\n",
tape_name,tape_head[z+0][0],tape_head[z+0][1],h1,h2,
j,k,approx(256500,j+k),l,h,tape_checksum&255,tape_is_ok(),i);
if (i==1) ++q; else if (i>0) q=1;
}
tape_twotwo=j=k=0; break; // force exit!
}
else if (tape_head[i][0]>1) // is it a tone?
{
tape_export_tzx13(j,k); k=0; // PURE SYNC
tape_export_tzx12(tape_head[i][1],tape_head[i][0]);
}
else // not a tone, add to list of syncs!
if (!k++) j=i;
}
tape_export_tzx13(j,k); // PURE SYNC (FINAL)
tape_export_tzx12n13();
if (tape_twotwo) // DATAS [+ PAUSE]?
{
fsend1(0X14);
j=tape_bit0w,k=tape_bit1w;
fsend2(j); fsend2(k);
h=((tape_twotwo+7)&7)+1; fsend1(h);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i; fsend2(i);
l=(tape_twotwo+7)>>3; fsend3(l);
flush1(); fwrite1(tape_temp,l);
uprintf("DATA %-10s --------- --------- %04d:%04d%4d%7d:%d %02X %s%7d\n",
tape_name,j,k,approx(256500,j+k),l,h,tape_checksum&255,tape_is_ok(),i);
}
else if (tape_twofor) // 4:2-edged DATAS
{
j=APPROX4(tape_bit0w*2+tape_bit1w);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i;
if (!tape_tzx) // compatible but heavier
{
fsend1(0X15);
fsend2(j);
fsend2(i);
l=tape_twofor2length(tape_twofor); h=((l+7)&7)+1; l=(l+7)>>3;
fsend1(h); fsend3(l); // convert bits (0/1) into samples (short+short/long)
q=tape_twofor2sample(q,tape_twofor);
uprintf("SAMPLE %-10s --------- --------- %04d:%04d%4d%7d:%d -- --%7d\n",
tape_name,j,j*2,approx(64125,j),l,h,i);
if (i==1) ++q; else if (i>0) q=1;
}
else // compact but requiring 1.20 support
{
fsend1(0X19);
l=(tape_twofor+7)>>3; h=((tape_twofor+7)&7)+1; fsend4(14+0+18+l);
fsend2(i);
fsend4(0); fsend1(0); fsend1(0);
fsend4(tape_twofor); fsend1(4); fsend1(2);
fsend1(0); fsend2(tape_bit0w); fsend2(tape_bit0w); fsend2(tape_bit0w); fsend2(tape_bit0w);
fsend1(0); fsend2(tape_bit1w); fsend2(tape_bit1w); fsend2(0); fsend2(0);
flush1(); fwrite1(tape_temp,l);
uprintf("DATA42 %-10s --------- --------- %04d:%04d%4d%7d:%d -- --%7d\n",
tape_name,tape_bit0w,tape_bit1w,approx(64125,j),l,h,i);
}
}
else if (tape_onetwo) // 2:1-edged DATAS
{
j=APPROX4(tape_bit0w*2+tape_bit1w);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i;
if (!tape_tzx) // compatible but heavier
{
fsend1(0X15);
fsend2(j);
fsend2(i);
l=tape_onetwo2length(tape_onetwo); h=((l+7)&7)+1; l=(l+7)>>3;
fsend1(h); fsend3(l); // convert bits (0/1) into samples (short+short/long)
q=tape_onetwo2sample(q,tape_onetwo);
uprintf("SAMPLE %-10s --------- --------- %04d:%04d%4d%7d:%d -- --%7d\n",
tape_name,j,j*2,approx(128250,j),l,h,i);
if (i==1) ++q; else if (i>0) q=1;
}
else // compact but requiring 1.20 support
{
fsend1(0X19);
l=(tape_onetwo+7)>>3; h=((tape_onetwo+7)&7)+1; fsend4(14+0+10+l);
fsend2(i);
fsend4(0); fsend1(0); fsend1(0);
fsend4(tape_onetwo); fsend1(2); fsend1(2);
fsend1(0); fsend2(tape_bit0w); fsend2(tape_bit0w);
fsend1(0); fsend2(tape_bit1w); fsend2(0);
flush1(); fwrite1(tape_temp,l);
uprintf("DATA21 %-10s --------- --------- %04d:%04d%4d%7d:%d -- --%7d\n",
tape_name,tape_bit0w,tape_bit1w,approx(128250,j),l,h,i);
}
}
else if (tape_oneone) // 1:1-edged DATAS
{
j=APPROX4(tape_bit0w*2+tape_bit1w);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i;
if (!tape_tzx) // compatible but heavier
{
fsend1(0X15);
fsend2(j);
fsend2(i);
l=tape_oneone2length(tape_oneone); h=((l+7)&7)+1; l=(l+7)>>3;
fsend1(h); fsend3(l); // convert bits (0/1) into samples (short/long)
q=tape_oneone2sample(q,tape_oneone);
uprintf("SAMPLE %-10s --------- --------- %04d:%04d%4d%7d:%d -- --%7d\n",
tape_name,j,j*2,approx(171000,j),l,h,i);
if (i==1) ++q; else if (i>0) q=1;
}
else // compact but requiring 1.20 support
{
fsend1(0X19);
l=(tape_oneone+7)>>3; h=((tape_oneone+7)&7)+1; fsend4(14+0+ 6+l);
fsend2(i);
fsend4(0); fsend1(0); fsend1(0);
fsend4(tape_oneone); fsend1(1); fsend1(2);
fsend1(0); fsend2(tape_bit0w);
fsend1(0); fsend2(tape_bit1w);
flush1(); fwrite1(tape_temp,l);
uprintf("DATA11 %-10s --------- --------- %04d:%04d%4d%7d:%d -- --%7d\n",
tape_name,tape_bit0w,tape_bit1w,approx(171000,j),l,h,i);
}
}
while (tape_tails) // PAUSE?
{
fsend1(0X20);
i=tape_tails; if (i>65535) i=65535; tape_tails-=i; fsend2(i);
uprintf("PAUSE ---------- --------- --------- --------- --- -------- -- --%7d\n",i);
if (i==1) ++q; else if (i>0) q=1;
}
}
do_clock(); if (tape_closegroup>0) tape_export_oldblock();
tape_heads=tape_twotwo=tape_twofor=tape_onetwo=tape_oneone=tape_kansas=tape_tails=0; // reset all counters!
}
// CSW1 input analysis
// the general idea behind all these operations is that there are three main ways to encode bits:
// 1.- "double" or "twotwo": the most widespread and reliable encodings use two short edges for BIT0 and two long edges for BIT1;
// 2.- "hybrid" or "onetwo": frequency modulation (used by the MSX world, rare elsewhere) uses two short edges for BIT0 and a long one for BIT1;
// 3.- "single" or "oneone": the least widespread encodings (they're the most vulnerable to noise) use a short edge for BIT0 and a long one for BIT1;
// 4.- the Kansas City Standard "twofor" is a "onetwo" that sends bits twice (four short edges, two long ones): the MSX1 firmware's default encoding.
// telling whether a stream of bits is a single-signal encoding is trivial, while hybrid-signal and double-signal encodings need more attention.
int detect_singles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int u) // guess the longest self-contained single-signal stream:
// o = offset, w = NULL / *stream weight, w_l = NULL / *minimum detected signal, w_h = NULL / *maximum detected signal,
// l_l = minimum stream length, l_h = maximum stream length, v = threshold minimum/maximum double-percentage;
// return 0 if failed (this won't update the optional variables!) or a value in the [l_l,l_h] range if succesful.
{
if (o+l_l>samples) return 0; // early exit!
int i,l=o,u_l=sample[o],u_h=u_l,v=u_l; if ((l_h+=o)>samples) l_h=samples;
while (++l<l_h)
{
if (u_h<(i=sample[l]))
if (i*u>=u_l*200) break; else u_h=i;
else if (u_l>i)
if (u_h*u>=i*200) break; else u_l=i;
v+=i;
}
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=v;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int detect_singlez(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int u) // slightly more strict version: reject maximum length
{ return ((o=detect_singles(o,w,w_l,w_h,l_l,l_h,u))&&o<l_h)?o:0; }
int define_singles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_h) // measure the longest valid single-signal stream:
// o = offset, w = NULL / *stream weight, w_l = NULL / *minimum detected signal, w_h = NULL / *maximum detected signal,
// l_l = minimum stream length, l_h = maximum stream length, v_l = minimum signal weight, v_h = maximum signal weight;
// return 0 if failed (this won't update the optional variables!) or a value in the [l_l,l_h] range if succesful.
{
if (o+l_l>samples) return 0; // early exit!
int i,l=o,v=0,u_l=v_h,u_h=v_l; if ((l_h+=o)>samples) l_h=samples;
while (l<l_h&&(i=sample[l])>=v_l&&i<=v_h) { v+=i,++l; if (u_l>i) u_l=i; if (u_h<i) u_h=i; }
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=v;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int define_singlez(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_h) // slightly more strict version: reject maximum length
{ return ((o=define_singles(o,w,w_l,w_h,l_l,l_h,v_l,v_h))&&o<l_h)?o:0; }
int choose_singles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_lh,int v_hl,int v_h) // measure the longest valid single-signal stream:
// similar to define_singles() with additional parameters v_lh and v_hl to specify the longest short signal and the shortest long signal
{
if (o+l_l>samples) return 0; // early exit!
int i,l=o,v=0,u_l=v_h,u_h=v_l; if ((l_h+=o)>samples) l_h=samples;
while (l<l_h&&(i=sample[l])>=v_l&&i<=v_h) { v+=i,++l; if (u_l>i) u_l=i; if (u_h<i) u_h=i; }
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=v;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int choose_singlez(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_lh,int v_hl,int v_h) // slightly more strict version: reject maximum length
{ return ((o=choose_singles(o,w,w_l,w_h,l_l,l_h,v_l,v_lh,v_hl,v_h))&&o<l_h)?o:0; }
int divide_singles(int o,int *w,int l,int v) // tell apart between short and long signals in a single-signal stream
// o = offset, w = NULL / *short signals' weight, l = stream length, v = average signal weight;
// return the amount of short signals -- n.b. the amount of long signals is l minus the amount of short signals.
{
if (!l) return 0; // early exit!
int i,j=0,u=0; do { if ((i=sample[o])<v) u+=i,++j; ++o; } while (--l);
if (w) *w=u;
return j;
}
// no need for strict_singles() because single-signal streams are the finest possible granularity
int encode_singles(int o,unsigned char *t,int l,int v) // encode a single-signal stream into bits
// o = offset, *t = target buffer, l = stream length, v = average signal weight;
// return the amount of encoded bits (in this case, it's equal to l)
{
if (!l) return 0; // early exit!
unsigned char m=128,b=0; int n=l;
do { if (sample[o]>=v) b|=m; ++o; if (!(m>>=1)) m=128,*t++=b,b=0; } while (--l);
if (m!=128) *t=b;
return n;
}
// notice that detect_hybrids(), define_hybrids() and divide_hybrids() are effectively identical to their single-signal counteparts;
// the differences only apply to strict_hybrids() and encode_hybrids() because they deal with the actual bits' validity and content.
int strict_hybrids(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v) // check whether a single-signal stream is actually made of hybrid signals
// o = offset, w = NULL / *stream weight, w_l = NULL / *minimum detected signal, w_h = NULL / *maximum detected signal,
// l_l = minimum stream length, l_h = maximum stream length, v = average signal weight;
// return the length of the valid hybrid stream, or zero if the stream wasn't valid at all.
{
if (o+l_l>samples) return 0; // this should never happen!
int i,l=o,u=0,u_l=v,u_h=v; if ((l_h+=o)>samples) l_h=samples;
while (l<l_h) { if ((i=sample[l])>=v) { u+=i,++l; if (u_h<i) u_h=i; } else if (l+1<l_h&&(i+=sample[l+1])<=v*2) { u+=i,l+=2; if (u_l>i) u_l=i; } else break; }
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=u;
if (w_l) *w_l=APPROX2(u_l);
if (w_h) *w_h=u_h;
return l;
}
int encode_hybrids(int o,unsigned char *t,int l,int v) // encode a hybrid-signal stream into bits
// (beware: the stream must have been deemed valid in advance and its length must be known)
// o = offset, *t = target buffer, l = stream length, v = average signal weight;
// returns the amount of encoded bits (halfway between l and l/2)
{
if (!l) return 0; // early exit!
unsigned char m=128,b=0; int n=0; l+=o;
do { ++n; if (sample[o]>=v) b|=m; else ++o; if (!(m>>=1)) m=128,*t++=b,b=0; } while (++o<l);
if (m!=128) *t=b;
return n;
}
// because the bulk of tapes use double-signal encodings, we're better off giving them their own analysis functions
// instead of calling detect_singles() or define_singles() first, then running strict_doubles() and the like on them.
int detect_doubles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int u) // guess the longest self-contained double-signal stream:
// o = offset, w = NULL / *stream weight, w_l = NULL / *minimum detected signal, w_h = NULL / *maximum detected signal,
// l_l = minimum stream length, l_h = maximum stream length, u = threshold minimum/maximum double-percentage;
// return 0 if failed (this won't update the optional variables!) or a value in the [l_l,l_h] range if succesful.
{
if (o+l_l>samples) return 0; // early exit!
int i,l=o,u_l=sample[o]+sample[o+1],u_h=u_l,v=u_l; if ((l_h+=o)>samples) l_h=samples;
while ((l+=2)<l_h)
{
if (u_h<(i=sample[l]+sample[l+1]))
if (i*u>=u_l*200) break; else u_h=i;
else if (u_l>i)
if (u_h*u>=i*200) break; else u_l=i;
v+=i;
}
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=v;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int detect_doublez(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int u) // slightly more strict version: reject maximum length
{ return ((o=detect_doubles(o,w,w_l,w_h,l_l,l_h,u))&&o<l_h)?o:0; }
int define_doubles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_h) // measure the longest possible double-signal stream
// o = offset, w = NULL / *stream weight, w_l = NULL / *minimum detected signal, w_h = NULL / *maximum detected signal,
// l_l = minimum stream length, l_h = maximum stream length, v_l = minimum signal weight, v_h = maximum signal weight;
// return 0 if failed (this won't update the optional variables!) or a value in the [l_l,l_h] range if succesful.
{
if (o+l_l>samples) return 0; // early exit!
int i,l=o,v=0,u_l=v_h,u_h=v_l; if ((l_h+=o)>=samples) l_h=samples-1;
while (l<l_h&&(i=sample[l]+sample[l+1])>=v_l&&i<=v_h) { v+=i,l+=2; if (u_l>i) u_l=i; if (u_h<i) u_h=i; }
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=v;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int define_doublez(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_h) // slightly more strict version: reject maximum length
{ return ((o=define_doubles(o,w,w_l,w_h,l_l,l_h,v_l,v_h))&&o<l_h)?o:0; }
int choose_doubles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_lh,int v_hl,int v_h) // measure the longest possible double-signal stream
// similar to define_doubles() with additional parameters v_lh and v_hl to specify the longest short signal and the shortest long signal
{
if (o+l_l>samples) return 0; // early exit!
int i,l=o,v=0,u_l=v_h,u_h=v_l; if ((l_h+=o)>=samples) l_h=samples-1;
while (l<l_h&&(i=sample[l]+sample[l+1])>=v_l&&i<=v_h&&(i<=v_lh||i>=v_hl)) { v+=i,l+=2; if (u_l>i) u_l=i; if (u_h<i) u_h=i; }
if ((l-=o)<l_l) return 0; // failure!
if (w) *w=v;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int choose_doublez(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v_l,int v_lh,int v_hl,int v_h) // slightly more strict version: reject maximum length
{ return ((o=choose_doubles(o,w,w_l,w_h,l_l,l_h,v_l,v_lh,v_hl,v_h))&&o<l_h)?o:0; }
int divide_doubles(int o,int *w,int l,int v) // tell apart between short and long signals in a double-signal stream
// o = offset, w = NULL / *short signals' weight, l = stream length, v = average signal weight;
// return the amount of short signals -- n.b. the long signals' amount is l minus the short signals' amount; their weight is the stream weight minus the short signals' weight.
{
if (!(l>>=1)) return 0; // early exit!
int i,j=0,u=0; do { if ((i=sample[o]+sample[o+1])<v) u+=i,++j; o+=2; } while (--l);
if (w) *w=u;
return j<<1;
}
/*int strict_doubles(int o,int *w,int *w_l,int *w_h,int l_l,int l_h,int v) // check whether a single-signal stream is actually made of double signals
// o = offset, w = NULL / stream weight, l_l = minimum stream length, l_h = maximum stream length, v = average signal weight;
// return the length of the valid double stream, or zero if the stream wasn't valid at all.
{
if (o+l_l>samples) return 0; // this should never happen!
int i,l=o,u=0,u_l=v,u_h=v; if ((l_h+=o)>=samples) l_h=samples-1;
while (l<l_h) { if (((i=sample[l])>=v?sample[l+1]>=v:sample[l+1]<v)) { u+=(i+=sample[l+1]),l+=2; if (u_h<i) u_h=i; if (u_l>i) u_l=i; } else break; }
if ((l-=o)<l_l) return 0;
if (w) *w=u;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}*/
int encode_doubles(int o,unsigned char *t,int l,int v) // encode a double-signal stream into bits
// o = offset, *t = target buffer, l = stream length, v = average signal weight;
// return the amount of encoded bits (in this case, it's equal to l/2)
{
if (!(l>>=1)) return 0; // early exit!
unsigned char m=128,b=0; int n=l;
do { if (sample[o]+sample[o+1]>=v) b|=m; o+=2; if (!(m>>=1)) m=128,*t++=b,b=0; } while (--l);
if (m!=128) *t=b;
return n;
}
int weight_singles(int o,int l) // just sum all the weights of the samples at offset `o` and length `l`
{ int i=0; if ((l+=o)>samples) l=samples; while (o<l) i+=sample[o++]; return i; }
int weight_couples(int o,int l) // sum the weights of the interleaved samples at offset `o` and length `l` (i.e. 0, 2, 3.. l-1)
{ int i=0; if ((l+=o)>samples) l=samples; while (o<l) i+=sample[o],o+=2; return i; }
int recalc_singles(int o,int *w,int *w_l,int *w_h,int l) // recalculate all weights (stream, minimum and maximum) in a known single-signal stream
// o = offset, w = NULL / stream weight, w_l = NULL / minimum weight, w_h = NULL / maximum weight, l = stream length, v = average signal weight;
// return the stream length, or zero if something went wrong.
{
int i,k=(o+l),u=0,u_l,u_h; if (k>samples) return 0; // this should never happen!
u_l=u_h=sample[o]; while (++o<k) { u+=i=sample[o]; if (u_h<i) u_h=i; if (u_l>i) u_l=i; }
if (w) *w=u;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int recalc_doubles(int o,int *w,int *w_l,int *w_h,int l) // recalculate all weights (stream, minimum and maximum) in a known double-signal stream
// o = offset, w = NULL / stream weight, w_l = NULL / minimum weight, w_h = NULL / maximum weight, l = stream length, v = average signal weight;
// return the stream length, or zero if something went wrong.
{
int i,k=(o+l),u=0,u_l,u_h; if (k>samples) return 0; // this should never happen!
u_l=u_h=sample[o]+sample[o+1]; while ((o+=2)<k) { u+=i=sample[o]+sample[o+1]; if (u_h<i) u_h=i; if (u_l>i) u_l=i; }
if (w) *w=u;
if (w_l) *w_l=u_l;
if (w_h) *w_h=u_h;
return l;
}
int middle_doubles(int o,int *w,int l,int h) // validate exactly one double-signal: returns 0 if invalid, 2 if valid
{
if (o+2>samples) return 0; // early exit!
o=sample[o]+sample[o+1]; if (w) *w=o;
return o>=l&&o<=h?2:0;
}
// notice that middle_hybrid() would be equal to middle_single() when sample[o] is long, and to middle_doubles() when it's short;
// middle_single() itself would simply set `*w` to sample[o] and tell whether sample[o] fits between `l` and `h`.
// tape data detection and encoding
char flag_b=0,flag_y=0,flag_r=0,flag_0=-1; // encoding flags: BIT1:BIT0 and SYNC2:SYNC1 ratios, tone parity, incomplete byte handling
int header_length,header_weight,middle_length,middle_weight,stream_length,stream_weight,weight_l,weight_h; // measurements:
// header_* refers to the heading tone/pilot, middle_* refers to the synchro edges/syncs, stream_* refers to the bitstream.
void encode_common_heads(char r,char y) // encode common pilot and syncs, see below
{
tape_heads=0; if (tape_head[0][0]=header_length) // pilot?
{
if (r==1) // odd tones?
{ if (!(tape_head[0][0]&1)) --tape_head[0][0]; } // i.e. turn 2 into 1, but keep 1
else if (r)//==2 // even tones?
{ if (tape_head[0][0]&1) ++tape_head[0][0]; } // inverse: turn 1 into 2 but keep 2
tape_head[0][1]=APPROX_HZ2ZX(header_weight,header_length),tape_heads=1;
}
if (middle_length==1) // syncs? notice that some block types use one sync
tape_head[tape_heads][0]=1,tape_head[tape_heads][1]=HZ2ZX(middle_weight),++tape_heads;
else if (middle_length)//==2 // all other block types with syncs use exactly two
{
tape_head[tape_heads][0]=tape_head[tape_heads+1][0]=1; int i=HZ2ZX(middle_weight);
if (y)
tape_head[tape_heads][0]=2,tape_head[tape_heads][1]=APPROX2(i),++tape_heads; // turn syncs into a couple
else
tape_head[tape_heads][1]=i-(tape_head[tape_heads+1][1]=HZ2ZX(sample[oo+header_length+1])),tape_heads+=2;
}
}
#define TAPE_BITWZERO do{ if (!tape_bit0w) tape_bit0w=APPROX2(tape_bit1w); else if (!tape_bit1w) tape_bit1w=tape_bit0w*2; }while(0) // sanity check: fill empty fields
#define TAPE_FLAG_B(w,n) do{ if (flag_b) tape_bit1w=(tape_bit0w=APPROX_HZ2ZX(w,n))*2; }while(0) // flag "-b": set BIT1 twice as long as BIT0
void encode_common_double(char r,char y,char z) // encode a common 2:2 tape block previously detected
{
encode_common_heads(r,y); oo+=header_length+middle_length;
tape_bit0l=tape_bit1l=2;
int w,n; if (stream_length)
{
n=divide_doubles(oo,&w,stream_length,APPROX2(weight_l+weight_h));
tape_bit0w=n?APPROX_HZ2ZX(w,n):0; // BIT0 values
w=stream_weight-w; n=stream_length-n;
tape_bit1w=n?APPROX_HZ2ZX(w,n):0; // BIT1 values
TAPE_BITWZERO;
tape_twotwo=encode_doubles(oo,tape_temp,stream_length,ZX2HZ(tape_bit0w+tape_bit1w));
TAPE_FLAG_B(stream_weight,n+stream_length);
}
else
tape_bit0w=tape_bit1w=0;
oo+=stream_length; tape_tails=0; // this value will develop as rejected samples are appended
if (n=(tape_twotwo&7)) switch (z)
{
case 5: // clip or pad incomplete bytes with 1s
case 4: // clip or pad incomplete bytes with 0s
if (n>=4)
{
case 1: // pad all incomplete bytes with 1s
case 0: // pad all incomplete bytes with 0s
if (z&1) tape_temp[tape_twotwo>>3]|=255>>n;
tape_twotwo+=8-n;
tape_tails-=APPROX2((8-n)*(weight_l+weight_h)); // decrease tails
break;
}
else
case 8: // clip all incomplete bytes
{
tape_twotwo-=n;
tape_tails+=APPROX2(n*(weight_l+weight_h)); // increase tails
break;
}
}
tape_checksum=0; tape_parity8b(tape_temp,tape_twotwo>>3); // default to XOR8
}
void encode_common_hybrid(char r,char y) // encode a common 2:1 hybrid-edge block
{
encode_common_heads(r,y); oo+=header_length+middle_length;
tape_bit0l=2,tape_bit1l=1;
int w,n; if (stream_length)
{
n=divide_singles(oo,&w,stream_length,APPROX2(weight_l+weight_h));
tape_bit0w=n?APPROX_HZ2ZX(w,n):0; // BIT0 values
w=stream_weight-w; n=stream_length-n;
tape_bit1w=n?APPROX_HZ2ZX(w,n):0; // BIT1 values
TAPE_BITWZERO;
tape_onetwo=encode_hybrids(oo,tape_temp,stream_length,APPROX_ZX2HZ(tape_bit0w+tape_bit1w,2));
TAPE_FLAG_B(stream_weight,n+stream_length);
}
else
tape_bit0w=tape_bit1w=0;
oo+=stream_length; tape_tails=0; // this value will develop as rejected samples are appended
tape_checksum=0; tape_parity8b(tape_temp,tape_onetwo>>3);
}
void encode_common_single(char r,char y) // encode a common 1:1 single-edge block
{
encode_common_heads(r,y); oo+=header_length+middle_length;
tape_bit0l=tape_bit1l=1;
int w,n; if (stream_length)
{
n=divide_singles(oo,&w,stream_length,APPROX2(weight_l+weight_h));
tape_bit0w=n?APPROX_HZ2ZX(w,n):0; // BIT0 values
w=stream_weight-w; n=stream_length-n;
tape_bit1w=n?APPROX_HZ2ZX(w,n):0; // BIT1 values
TAPE_BITWZERO;
tape_oneone=encode_singles(oo,tape_temp,stream_length,APPROX_ZX2HZ(tape_bit0w+tape_bit1w,2));
TAPE_FLAG_B(stream_weight,n+stream_length);
}
else
tape_bit0w=tape_bit1w=0;
oo+=stream_length; tape_tails=0; // this value will develop as rejected samples are appended
tape_checksum=0; tape_parity8b(tape_temp,tape_oneone>>3);
}
int splice_stream(unsigned char *t,int l) // reduce a stream made of double bits to a single-bit stream, top bit first, bottom bit last
{
int i,n=l>>1; l=(l+15)>>4; unsigned char *s=t; do
{
i=(*s&128)+(*s&32)*2+(*s&8)*4+(*s&2)*8; ++s;
*t++=i+((*s&64)>>3)+((*s&16)>>2)+((*s&4)>>1)+(*s&1); ++s;
}
while (--l>0); return n;
}
int flag_w=0;
int detect_config_pl1,detect_config_pl2,detect_config_ph1,detect_config_ph2,detect_config_p_l,detect_config_p_h; // PILOT TONE parameters
int detect_config_s_l,detect_config_s_h,detect_config_b_l,detect_config_b_h,detect_config_n_l,detect_config_n_h; // SYNCS + DATAS param's
int detect_call_double(int o) // detect normal blocks defined by parameters
{
return ((header_length=define_singlez(o,&header_weight,NULL,NULL,detect_config_pl1,detect_config_ph2,detect_config_p_l,detect_config_p_h))
&&(header_length<detect_config_ph1||header_length>=detect_config_pl2)
&&(middle_length=middle_doubles(o+header_length,&middle_weight,detect_config_s_l,detect_config_s_h))
&&(stream_length=define_doublez(o+header_length+2,&stream_weight,&weight_l,&weight_h,detect_config_n_l,detect_config_n_h,detect_config_b_l,detect_config_b_h)));
}
void encode_call_double(void) // encode normal blocks defined by parameters
{ tape_howtosum=0; encode_common_double(flag_r,flag_y,flag_0); }
void config_call_alkatraz(void)
{
detect_config_pl1= 144- 20; detect_config_ph1= 144+ 20;
detect_config_pl2=3122-400; detect_config_ph2=3122+400;
detect_config_p_l=ZX2HZ(2180*4/5); detect_config_p_h=ZX2HZ(2380*5/4);
detect_config_s_l=ZX2HZ(1200*4/5); detect_config_s_h=ZX2HZ(2100*5/4);
detect_config_b_l=ZX2HZ(1080*4/5); detect_config_b_h=ZX2HZ(1200*5/2);
detect_config_n_l= 48 ; detect_config_n_h=21E5;
}
void config_call_hexagon(void)
{
detect_config_pl1=detect_config_pl2=2944-350;
detect_config_ph1=detect_config_ph2=2944+350;
detect_config_p_l=ZX2HZ(2280*4/5); detect_config_p_h=ZX2HZ(2280*5/4);
detect_config_s_l=ZX2HZ(1600*4/5); detect_config_s_h=ZX2HZ(1600*5/4);
detect_config_b_l=ZX2HZ(1280*4/5); detect_config_b_h=ZX2HZ(1280*5/2);
detect_config_n_l= 48 ; detect_config_n_h=21E5;
}
void sanitize_middle(int i,int j) // catch biased syncs! slow encodings like Amstrad and Cassys are prone to develop noise that makes SYNC1 much longer than SYNC2
{
if (header_length&&header_weight*i/header_length<middle_weight*j)
if ((i=sample[oo+header_length])*3>(j=sample[oo+header_length+1])*4)
{ i=APPROX4(i+j*7); header_weight+=middle_weight-i; middle_weight=i; }
}
int detect_call_amstrad(int o)
{
int i=sample[o]+sample[o+1]; if (!(header_length=define_singlez(o,&header_weight,NULL,NULL,4096-400,4096+400,i*2/5,i*5/8))) return 0;
i=approx(header_weight,header_length); return (middle_length=middle_doubles(o+header_length,&middle_weight,i*1/2,i*3/2))
&&(stream_length=define_doublez(o+header_length+2,&stream_weight,&weight_l,&weight_h,32,21E5,i*1/2,i*5/2));
}
void encode_call_amstrad(void) // encode Amstrad-like blocks
{
sanitize_middle(8,7); encode_common_double(flag_r,flag_y,flag_0);
tape_howtosum=1; tape_checksum=0; tape_ccitt256(&tape_temp[1],tape_twotwo>>3); // the CCITT spans the bytes between the ID (first) and the trailer
if (!tape_checksum) // all the CRCs are right, but what about the trailer? (the firmware ignores the trailer, but some protections require the dummy bits)
{
int i=tape_twotwo%(258<<3),j=(tape_twotwo+7)>>3;
if (i<40) tape_checksum=250; // the ideal trailer is made of 64 edges!
else if (i>40) tape_checksum=255;
else if (tape_temp[j-4]!=255) tape_checksum=251;
else if (tape_temp[j-3]!=255) tape_checksum=252;
else if (tape_temp[j-2]!=255) tape_checksum=253;
else if (tape_temp[j-1]!=255) tape_checksum=254; // the 64 edges must be BIT1!
} // final 8-bit error code: $00 success; $01..$F9 amount of bad CRCs in the bitstream; $FA trailer is too short; $FB..$FE first non-$FF in trailer; $FF trailer is too long
}