diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj
index 46bb6d8..31b8657 100644
--- a/MiSTer.vcxproj
+++ b/MiSTer.vcxproj
@@ -56,6 +56,7 @@
+
@@ -85,6 +86,7 @@
+
diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters
index a5a7241..b829b6c 100644
--- a/MiSTer.vcxproj.filters
+++ b/MiSTer.vcxproj.filters
@@ -68,6 +68,9 @@
Source Files
+
+ Source Files
+
@@ -151,6 +154,9 @@
Header Files
+
+ Header Files
+
diff --git a/tzx2wav.c b/tzx2wav.c
new file mode 100644
index 0000000..fb7ed50
--- /dev/null
+++ b/tzx2wav.c
@@ -0,0 +1,1392 @@
+/////////////////////////////////////////////////////////////////////
+// TZX to VAV Converter v0.2 for Bloodshed Dev-C++ compiler //
+// (C) 2005-2006 Francisco Javier Crespo //
+// //
+// MiSTer adaptation //
+// (C) 2017 Francisco Javier Crespo //
+// //
+// Originally based on source code from these works: //
+// PLAYTZX v0.60b for Watcom C compiler (C) 1997-2004 Tomaz Kac //
+// PLAYTZX Unix v0.12b (C) 2003 Tero Turtiainen / Fredrick Meunier //
+/////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "tzx2wav.h"
+#include "spi.h"
+
+// Computer entries
+
+const char hwid_01_01[] = "ZX Spectrum 16k";
+const char hwid_01_02[] = "ZX Spectrum 48k, Plus";
+const char hwid_01_03[] = "ZX Spectrum 48k Issue 1";
+const char hwid_01_04[] = "ZX Spectrum 128k (Sinclair)";
+const char hwid_01_05[] = "ZX Spectrum 128k +2 (Grey case)";
+const char hwid_01_06[] = "ZX Spectrum 128k +2A, +3";
+const char hwid_01_07[] = "Timex Sinclair TC-2048";
+const char hwid_01_08[] = "Timex Sinclair TS-2068";
+const char hwid_01_09[] = "Pentagon 128";
+const char hwid_01_10[] = "Sam Coupe";
+const char hwid_01_11[] = "Didaktik M";
+const char hwid_01_12[] = "Didaktik Gama";
+const char hwid_01_13[] = "ZX-81 with 1k RAM";
+const char hwid_01_14[] = "ZX-81 with 16k RAM or more";
+const char hwid_01_15[] = "ZX Spectrum 128k, Spanish version";
+const char hwid_01_16[] = "ZX Spectrum, Arabic version";
+const char hwid_01_17[] = "TK 90-X";
+const char hwid_01_18[] = "TK 95";
+const char hwid_01_19[] = "Byte";
+const char hwid_01_20[] = "Elwro";
+const char hwid_01_21[] = "ZS Scorpion";
+const char hwid_01_22[] = "Amstrad CPC 464";
+const char hwid_01_23[] = "Amstrad CPC 664";
+const char hwid_01_24[] = "Amstrad CPC 6128";
+const char hwid_01_25[] = "Amstrad CPC 464+";
+const char hwid_01_26[] = "Amstrad CPC 6128+";
+const char hwid_01_27[] = "Jupiter ACE";
+const char hwid_01_28[] = "Enterprise";
+const char hwid_01_29[] = "Commodore 64";
+const char hwid_01_30[] = "Commodore 128";
+
+const char *hwids_01[30] =
+{ hwid_01_01, hwid_01_02, hwid_01_03, hwid_01_04, hwid_01_05, hwid_01_06,
+hwid_01_07, hwid_01_08, hwid_01_09, hwid_01_10, hwid_01_11, hwid_01_12,
+hwid_01_13, hwid_01_14, hwid_01_15, hwid_01_16, hwid_01_17, hwid_01_18,
+hwid_01_19, hwid_01_20, hwid_01_21, hwid_01_22, hwid_01_23, hwid_01_24,
+hwid_01_25, hwid_01_26, hwid_01_27, hwid_01_28, hwid_01_29, hwid_01_30 };
+
+
+const char *build= "20060225";
+
+#define MAJREV 1 // Major revision of the format this program supports
+#define MINREV 13 // Minor revision of the format this program supports
+
+// C64 Loader defines ...
+
+#define ROM_S_HALF 616 // ROM Loader SHORT Half Wave
+#define ROM_M_HALF 896 // ROM Loader MEDIUM Half Wave
+#define ROM_L_HALF 1176 // ROM Loader LONG Half Wave
+
+#define STT_0_HALF 426 // Standard Turbo Tape BIT 0 Half Wave
+#define STT_1_HALF 596 // Standard Turbo Tape BIT 1 Half Wave
+
+// Other defines ...
+
+#define SGNLOW 0
+#define SGNHIGH 1
+
+unsigned int sgn; // Sign of the wave being played
+unsigned int freq = 44100; // Default Sample Frequency
+
+int prvi;
+int n,m;
+int num;
+unsigned char *d;
+unsigned char *mem = 0; // File in Memory
+int pos; // Position in File
+int curr; // Current block that is playing
+int numblocks; // Total Num. of blocks
+unsigned long oflen; // Length of output file
+int block[2048]; // Array of Block starts
+double cycle; // Frequency / 3500000 (Z80 clock)
+
+int cpc=0; // Amstrad CPC tape ?
+int sam=0; // SAM Coupe tape ?
+
+int id; // Current Block ID
+int pilot; // Len of Pilot signal (in hp's)
+int sb_pilot; // Pilot pulse
+int sb_sync1; // Sync first half-period (hp)
+int sb_sync2; // Sync second
+int sb_bit0; // Bit-0
+int sb_bit1; // Bit-1
+int sb_pulse; // Pulse in Sequence of pulses and direct recording block
+int lastbyte; // How many bits are in last byte of data ?
+int pause_ms; // Pause after current block (in milliseconds)
+int skippause=0; // Overrides pause value in last TZX block
+
+int singlepulse; // Flag to activate single pulse waves
+int manchester; // Flag to activate manchester encoded waves
+
+unsigned char *data; // Data to be played
+int datalen; // Len of ^^^
+int datapos; // Position in ^^^
+int bitcount; // How many bits to play in current byte ?
+int sb_bit; // should we play bit 0 or 1 ?
+char databyte; // Current Byte to be replayed of the data
+signed short jump; // Relative Jump
+int not_rec; // Some blocks were not recognised ??
+int files=0; // Number of Files on the command line
+int starting=1; // starting block
+int ending=0; // ending block
+
+int pages=0; // Waiting after each page of the info ?
+int expand=0; // Expand Groups ?
+int draw=1; // Local flag for outputing a line when in a group
+int mode128=0; // Are we working in 128k mode ? (for Stop in 48k block)
+
+int nfreq=0; // Did we choose new frequency with /freq switch ?
+char k;
+int speed;
+int x,last,lastlen;
+
+int loop_start=0; // Position of the last Loop Start block
+int loop_count=0; // Counter of the Loop
+int call_pos=0; // Position of the last Call Sequence block
+int call_num=0; // Number of Calls in the last Call Sequence block
+int call_cur=0; // Current Call to be made
+int num_sel; // Number of Selections in the Select block
+int jumparray[256]; // Array of all possible jumps in Select block
+
+int sb_bit0_f, sb_bit0_s, sb_bit1_f, sb_bit1_s, xortype, sb_finishbyte_f,
+ sb_finishbyte_s, sb_finishdata_f, sb_finishdata_s, num_lead_in, xorvalue;
+int trailing, sb_trailing;
+char lead_in_byte;
+int endian;
+char add_bit;
+
+int inv = 0;
+
+char tstr[255];
+char tstr2[255];
+char tstr3[255];
+char tstr4[255];
+char spdstr[255];
+char pstr[255];
+
+int numt, nump, t2;
+
+static void core_write(void *buf, int size)
+{
+ char *addr = (char*)buf;
+ while (size--)
+ {
+ spi8(*addr++);
+ oflen++;
+ }
+}
+
+/////////////////////////////////////////////////
+// Garbage collector and error handling routines
+/////////////////////////////////////////////////
+
+void GarbageCollector(void)
+{
+ if (mem != NULL)
+ {
+ free(mem);
+ mem = 0;
+ }
+}
+
+static void Error(char *errstr)
+{
+ GarbageCollector();
+ printf("\n-- Error: %s\n", errstr);
+}
+
+///////////////////////////////
+// CSW v1.01 handling routines
+///////////////////////////////
+
+void CSW1_Init(void)
+{
+ // Official CSW format documentation at:
+ // http://www.ramsoft.bbk.org/csw.html
+
+ unsigned short Revision = 0x0101;
+ unsigned char CompType = 1;
+ unsigned int Reserved = 0;
+
+ core_write("Compressed Square Wave\032", 23);
+ core_write(&Revision, 2); // Major & Minor revision
+ core_write(&freq, 2); // Sample Rate
+ core_write(&CompType, 1); // Compression Type
+ core_write(&inv, 1); // Polarity
+ core_write(&Reserved, 3); // Reserved bytes
+}
+
+void CSW1_Write(unsigned int samples)
+{
+ if (samples < 256)
+ {
+ core_write(&samples, 1);
+ }
+ else
+ {
+ int zero = 0;
+ core_write(&zero, 1);
+ core_write(&samples, 4);
+ }
+}
+
+//////////////////////////////////
+// Generic wave handling routines
+//////////////////////////////////
+
+unsigned int Samples(unsigned int n)
+{
+ // Convert a sampling value in Z80 T-States to samples for wave output
+ return ((unsigned int)(0.5 + (cycle*(double)n)));
+}
+
+void PlayWave(unsigned int len)
+{
+ CSW1_Write(len);
+}
+
+void PauseWave(unsigned int pause_ms)
+{
+ // Waits for "pause" milliseconds
+
+ int p;
+ if ((!skippause) || (curr != (numblocks - 1)))
+ {
+ p = (unsigned int)((((float)pause_ms)*freq) / 1000.0);
+ PlayWave(p);
+ }
+}
+
+/////////////////////////////
+// TZX Commodore 64 routines
+/////////////////////////////
+
+void PlayC64(unsigned int len)
+{
+ PlayWave(len);
+ PlayWave(len);
+}
+
+void PlayC64ROMByte(char byte, int finish)
+{
+ xorvalue = xortype;
+ while (bitcount)
+ {
+ if (!endian) sb_bit = byte & 0x01;
+ else sb_bit = byte & 0x80;
+ if (sb_bit)
+ {
+ if (sb_bit1_f) PlayC64(sb_bit1_f);
+ if (sb_bit1_s) PlayC64(sb_bit1_s);
+ xorvalue ^= sb_bit;
+ }
+ else
+ {
+ if (sb_bit0_f) PlayC64(sb_bit0_f);
+ if (sb_bit0_s) PlayC64(sb_bit0_s);
+ xorvalue ^= sb_bit;
+ }
+ if (!endian) byte >>= 1;
+ else byte <<= 1;
+ bitcount--;
+ }
+ if (xortype != 0xFF)
+ {
+ if (xorvalue)
+ {
+ if (sb_bit1_f) PlayC64(sb_bit1_f);
+ if (sb_bit1_s) PlayC64(sb_bit1_s);
+ }
+ else
+ {
+ if (sb_bit0_f) PlayC64(sb_bit0_f);
+ if (sb_bit0_s) PlayC64(sb_bit0_s);
+ }
+ }
+ if (!finish)
+ {
+ if (sb_finishbyte_f) PlayC64(sb_finishbyte_f);
+ if (sb_finishbyte_s) PlayC64(sb_finishbyte_s);
+ }
+ else
+ {
+ if (sb_finishdata_f) PlayC64(sb_finishdata_f);
+ if (sb_finishdata_s) PlayC64(sb_finishdata_s);
+ }
+}
+
+void PlayC64TurboByte(char byte)
+{
+ int add_num;
+
+ add_num = add_bit & 3;
+
+ if (add_num && !(add_bit & 4))
+ {
+ while (add_num)
+ {
+ if (add_bit & 8) PlayC64(sb_bit1);
+ else PlayC64(sb_bit0);
+ add_num--;
+ }
+ }
+
+ while (bitcount)
+ {
+ if (!endian) sb_bit = byte & 0x01;
+ else sb_bit = byte & 0x80;
+ if (sb_bit) PlayC64(sb_bit1);
+ else PlayC64(sb_bit0);
+ if (!endian) byte >>= 1;
+ else byte <<= 1;
+ bitcount--;
+ }
+
+ if (add_num && (add_bit & 4))
+ {
+ while (add_num)
+ {
+ if (add_bit & 8) PlayC64(sb_bit1);
+ else PlayC64(sb_bit0);
+ add_num--;
+ }
+ }
+}
+
+////////////////////////////////
+// Game identification routines
+////////////////////////////////
+
+void GetC64ROMName(char *name, unsigned char *data)
+{
+ char d;
+
+ for (n = 0; n < 16; n++)
+ {
+ d = data[14 + n];
+ if (d < 32 || d>125)
+ name[n] = ' ';
+ else
+ name[n] = d;
+ }
+ name[n] = 0;
+}
+
+void GetC64StandardTurboTapeName(char *name, unsigned char *data)
+{
+ char d;
+
+ for (n = 0; n < 16; n++)
+ {
+ d = data[15 + n];
+ if (d < 32 || d>125)
+ name[n] = ' ';
+ else
+ name[n] = d;
+ }
+ name[n] = 0;
+}
+
+void IdentifyC64ROM(int pos, unsigned char *data, int type)
+{
+ char name[255];
+
+ // Determine Loader type
+ if ((sb_pilot == ROM_S_HALF) && (sb_sync1 == ROM_L_HALF) && (sb_sync2 == ROM_M_HALF) &&
+ (sb_bit0_f == ROM_S_HALF) && (sb_bit0_s == ROM_M_HALF) && (sb_bit1_f == ROM_M_HALF) &&
+ (sb_bit1_s == ROM_S_HALF) && (xortype == 0x01))
+ {
+ // ROM Loader
+ if ((data[0] == 0x89) && (data[1] == 0x88) && (data[2] == 0x87) && (data[3] == 0x86) &&
+ (data[4] == 0x85) && (data[5] == 0x84) && (data[6] == 0x83) && (data[7] == 0x82) &&
+ (data[8] == 0x81))
+ {
+ if (pos == 202)
+ {
+ if (!type)
+ {
+ strcpy(name, "Header: ");
+ GetC64ROMName(name + 8, data);
+ }
+ else
+ {
+ strcpy(name, "ROM Header: ");
+ GetC64ROMName(name + 12, data);
+ }
+ }
+ else
+ {
+ if (!type)
+ {
+ strcpy(name, "Data Block ");
+ }
+ else
+ {
+ strcpy(name, "ROM: Data Block");
+ }
+ }
+ }
+ else
+ {
+ if (!type) strcpy(name, "------------------------");
+ else strcpy(name, "ROM: Last Block Repeated");
+ }
+ strcpy(tstr, name);
+ strcpy(spdstr, "C64 ROM Data ");
+ return;
+ }
+
+ if (!type) strcpy(tstr, "------------------------");
+ else strcpy(tstr, "Unknown");
+ strcpy(spdstr, "C64 Data ");
+}
+
+void IdentifyC64Turbo(int pos, unsigned char *data, int type)
+{
+ char name[255];
+
+ // Determine Loader type
+ if (sb_bit0 == STT_0_HALF && sb_bit1 == STT_1_HALF && lead_in_byte == 0x02)
+ {
+ // Standard Turbo Tape Loader
+ if (data[0] == 0x09 && data[1] == 0x08 && data[2] == 0x07 && data[3] == 0x06 &&
+ data[4] == 0x05 && data[5] == 0x04 && data[6] == 0x03 && data[7] == 0x02 &&
+ data[8] == 0x01)
+ {
+ if (pos == 32 && data[9] != 0x00)
+ {
+ if (!type)
+ {
+ strcpy(name, "Header: ");
+ GetC64StandardTurboTapeName(name + 8, data);
+ }
+ else
+ {
+ strcpy(name, "TurboTape Header: ");
+ GetC64StandardTurboTapeName(name + 18, data);
+ }
+ }
+ else
+ {
+ if (!type) strcpy(name, "------------------------");
+ else strcpy(name, "TurboTape Data Block");
+ }
+ }
+ else
+ {
+ if (!type) strcpy(name, "------------------------");
+ else strcpy(name, "TurboTape Unknown");
+ }
+ strcpy(tstr, name);
+ strcpy(spdstr, "C64 Turbo ");
+ return;
+ }
+ if (!type) strcpy(tstr, "------------------------");
+ else strcpy(tstr, "Unknown");
+ strcpy(spdstr, "C64 Data ");
+}
+
+void Identify(int len, unsigned char *temp, int type)
+{
+ int n;
+ int s;
+
+ if (cpc)
+ {
+ if (temp[0] == 44)
+ {
+ if (!type) s = 4;
+ else s = 0;
+ strcpy(tstr, " ");
+ for (n = 0; n < 16; n++)
+ {
+ if (temp[n + 1]) tstr[n + s] = temp[n + 1];
+ else tstr[n + s] = ' ';
+ }
+ for (n = 0; n < 4; n++) tstr[n + s + 16] = ' ';
+ tstr[n + s + 16] = 0;
+ }
+ else
+ {
+ if (!type)
+ strcpy(tstr, " ------------------ ");
+ else
+ strcpy(tstr, "Headerless");
+ }
+ return;
+ }
+
+ if (sam)
+ {
+ if (temp[0] == 1 && (len>80 && len < 84) && (temp[1] >= 0x10 && temp[1] <= 0x13))
+ {
+ if (!type)
+ {
+ s = 14;
+ switch (temp[1])
+ {
+ case 0x10: strcpy(tstr, " Program : "); break;
+ case 0x11: strcpy(tstr, " Num. Array : "); break;
+ case 0x12: strcpy(tstr, "Char. Array : "); break;
+ case 0x13: strcpy(tstr, " Bytes : "); break;
+ }
+ }
+ else
+ {
+ switch (temp[1])
+ {
+ case 0x10: strcpy(tstr, "Program : "); s = 10; break;
+ case 0x11: strcpy(tstr, "Num. Array : "); s = 13; break;
+ case 0x12: strcpy(tstr, "Char. Array : "); s = 14; break;
+ case 0x13: strcpy(tstr, "Bytes : "); s = 8; break;
+ }
+ }
+ for (n = 0; n < 10; n++)
+ {
+ if (temp[n + 2]>31 && temp[n + 2] < 127)
+ tstr[n + s] = temp[n + 2];
+ else
+ tstr[n + s] = 32;
+ }
+ tstr[n + s] = 0;
+ }
+ else
+ {
+ if (!type)
+ strcpy(tstr, " --------------------"); // Not Header
+ else
+ strcpy(tstr, "Headerless");
+ }
+ return;
+ }
+
+ if (temp[0] == 0 && (len == 19 || len == 20) && temp[1] < 4)
+ {
+ if (!type)
+ {
+ s = 14;
+ switch (temp[1])
+ {
+ case 0x00: strcpy(tstr, " Program : "); break;
+ case 0x01: strcpy(tstr, " Num. Array : "); break;
+ case 0x02: strcpy(tstr, "Char. Array : "); break;
+ case 0x03: strcpy(tstr, " Bytes : "); break;
+ }
+ }
+ else
+ {
+ switch (temp[1])
+ {
+ case 0x00: strcpy(tstr, "Program : "); s = 10; break;
+ case 0x01: strcpy(tstr, "Num. Array : "); s = 13; break;
+ case 0x02: strcpy(tstr, "Char. Array : "); s = 14; break;
+ case 0x03: strcpy(tstr, "Bytes : "); s = 8; break;
+ }
+ }
+ for (n = 0; n < 10; n++)
+ {
+ if (temp[n + 2]>31 && temp[n + 2] < 127)
+ tstr[n + s] = temp[n + 2];
+ else
+ tstr[n + s] = 32;
+ }
+ tstr[n + s] = 0;
+ }
+ else
+ {
+ if (!type)
+ strcpy(tstr, " --------------------"); // Not Header
+ else
+ strcpy(tstr, "Headerless");
+ }
+}
+
+//////////////////////////////////////////////////////////
+// Conversion routines to fetch bytes in Big Endian order
+//////////////////////////////////////////////////////////
+
+unsigned int Get2(unsigned char *pointer)
+{
+ return (pointer[0] | (pointer[1] << 8));
+}
+
+unsigned int Get3(unsigned char *pointer)
+{
+ return (pointer[0] | (pointer[1] << 8) | (pointer[2] << 16));
+}
+
+unsigned int Get4(unsigned char *pointer)
+{
+ return (pointer[0] | (pointer[1] << 8) | (pointer[2] << 16) | (pointer[3] << 24));
+}
+
+/////////////////////////
+// Miscelaneous routines
+/////////////////////////
+
+void CopyString(char *destination, unsigned char *source, unsigned int len)
+{
+ // Could just use strcpy ...
+
+ unsigned int n;
+ for (n = 0; n < len; n++)
+ destination[n] = source[n];
+ destination[n] = 0;
+}
+
+void MakeFixedString(char *s, int i)
+{
+ // This will create a fixed length string from null-terminated one...
+
+ int n = 0;
+ int k = 0;
+
+ while (i)
+ {
+ if (!s[n]) k = 1;
+ if (k) s[n] = ' ';
+ n++;
+ i--;
+ }
+ s[n] = 0;
+}
+
+///////////////////////////////
+// TZX Blocks Parsing routines
+///////////////////////////////
+
+void Analyse_ID10(void) // Standard Loading Data block
+{
+ pause_ms = Get2(&data[0]);
+ datalen = Get2(&data[2]);
+ data += 4;
+ if (data[0] == 0x00) pilot = 8064;
+ else pilot = 3220;
+ sb_pilot = Samples(2168);
+ sb_sync1 = Samples(667);
+ sb_sync2 = Samples(735);
+ sb_bit0 = Samples(885);
+ sb_bit1 = Samples(1710);
+ lastbyte = 8;
+}
+
+void Analyse_ID11(void) // Custom Loading Data block
+{
+ sb_pilot = Samples(Get2(&data[0]));
+ sb_sync1 = Samples(Get2(&data[2]));
+ sb_sync2 = Samples(Get2(&data[4]));
+ sb_bit0 = Samples(Get2(&data[6]));
+ sb_bit1 = Samples(Get2(&data[8]));
+ speed = (int)((1710.0 / (double)Get2(&data[8]))*100.0);
+ pilot = Get2(&data[10]);
+ lastbyte = (int)data[12];
+ pause_ms = Get2(&data[13]);
+ datalen = Get3(&data[15]);
+ data += 18;
+}
+
+void Analyse_ID12(void) // Pure Tone
+{
+ sb_pilot = Samples(Get2(&data[0]));
+ pilot = Get2(&data[2]);
+ if (draw) printf(" Pure Tone Length: %5d\n", pilot);
+ while (pilot)
+ {
+ PlayWave(sb_pilot);
+ pilot--;
+ }
+}
+
+void Analyse_ID13(void) // Sequence of Pulses
+{
+ pilot = (int)data[0]; data++;
+ if (draw) printf(" Sequence of Pulses Length: %5d\n", pilot);
+ while (pilot)
+ {
+ sb_pulse = Samples(Get2(&data[0]));
+ PlayWave(sb_pulse);
+ pilot--;
+ data += 2;
+ }
+}
+
+void Analyse_ID14(void) // Pure Data
+{
+ sb_pilot = pilot = sb_sync1 = sb_sync2 = 0;
+ sb_bit0 = Samples(Get2(&data[0]));
+ sb_bit1 = Samples(Get2(&data[2]));
+ speed = (int)((1710.0 / (double)Get2(&data[2]))*100.0);
+ lastbyte = (int)data[4];
+ pause_ms = Get2(&data[5]);
+ datalen = Get3(&data[7]);
+ data += 10;
+}
+
+void Analyse_ID15(void) // Direct Recording
+{
+ // For now the BEST way is to use the sample frequency for replay that is
+ // exactly the SAME as the Original Freq. used when sampling this block !
+ // i.e. NO downsampling is handled YET ... use TAPER when you need it ! ;-)
+
+ sb_pulse = Samples(Get2(&data[0]));
+ if (!sb_pulse) sb_pulse = 1; // In case sample frequency > 44100
+ pause_ms = Get2(&data[2]); // (Should work for frequencies upto 48000)
+ lastbyte = (int)data[4];
+ datalen = Get3(&data[5]);
+ if (draw) printf(" Direct Recording Length:%6d Original Freq.: %5d Hz\n",
+ datalen, (int)(0.5 + (3500000.0 / (double)Get2(&data[0]))));
+ data = &data[8];
+ datapos = 0;
+ // Replay Direct Recording block ...
+ while (datalen)
+ {
+ if (datalen != 1) bitcount = 8;
+ else bitcount = lastbyte;
+ databyte = data[datapos];
+ while (bitcount)
+ {
+ PlayWave(sb_pulse);
+ databyte <<= 1;
+ bitcount--;
+ }
+ datalen--;
+ datapos++;
+ }
+ if (pause_ms) PauseWave(pause_ms);
+}
+
+void Analyse_ID16(void) // C64 ROM Type Data Block
+{
+ data += 4;
+ sb_pilot = Get2(&data[0]);
+ pilot = Get2(&data[2]);
+ sb_sync1 = Get2(&data[4]);
+ sb_sync2 = Get2(&data[6]);
+ sb_bit0_f = Get2(&data[8]);
+ sb_bit0_s = Get2(&data[10]);
+ sb_bit1_f = Get2(&data[12]);
+ sb_bit1_s = Get2(&data[14]);
+ xortype = (int)(data[16]);
+ sb_finishbyte_f = Get2(&data[17]);
+ sb_finishbyte_s = Get2(&data[19]);
+ sb_finishdata_f = Get2(&data[21]);
+ sb_finishdata_s = Get2(&data[23]);
+ sb_trailing = Get2(&data[25]);
+ trailing = Get2(&data[27]);
+ lastbyte = (int)(data[29]);
+ endian = data[30];
+ pause_ms = Get2(&data[31]);
+ datalen = Get3(&data[33]);
+ data += 36;
+ IdentifyC64ROM(datalen, data, 1);
+}
+
+void Analyse_ID17(void) // C64 Turbo Tape Data Block
+{
+ data += 4;
+ sb_bit0 = Get2(&data[0]);
+ sb_bit1 = Get2(&data[2]);
+ add_bit = data[4];
+ num_lead_in = Get2(&data[5]);
+ lead_in_byte = data[7];
+ lastbyte = (int)data[8];
+ endian = data[9];
+ trailing = Get2(&data[10]);
+ sb_trailing = data[12];
+ pause_ms = Get2(&data[13]);
+ datalen = Get3(&data[15]);
+ data += 18;
+ IdentifyC64Turbo(datalen, data, 1);
+}
+
+void Analyse_ID20(void) // Pause or Stop the Tape command
+{
+ pause_ms = Get2(&data[0]);
+ if (pause_ms)
+ {
+ if (draw) printf(" Pause Length: %2.3fs\n", ((float)pause_ms) / 1000.0);
+ PauseWave(pause_ms);
+ }
+ else
+ {
+ if (draw) printf(" Stop the tape command!\n");
+ PauseWave(2000); // 2 seconds of pause in "Stop Tape" wave output
+ }
+}
+
+void Analyse_ID21(void) // Group Start
+{
+ CopyString(pstr, &data[1], data[0]);
+ if (draw) printf(" Group: %s\n", pstr);
+ if (!expand) draw = 0;
+}
+
+void Analyse_ID22(void) // Group End
+{
+ if (draw) printf(" Group End\n");
+ draw = 1;
+}
+
+void Analyse_ID23(void) // Jump To Relative
+{
+ jump = (signed short)(data[0] + data[1] * 256);
+ if (draw) printf(" Jump Relative: %d (To Block %d)\n", jump, curr + jump + 1);
+ curr += jump;
+ curr--;
+}
+
+void Analyse_ID24(void) // Loop Start
+{
+ loop_start = curr;
+ loop_count = Get2(&data[0]);
+ if (draw) printf(" Loop Start, Counter: %d\n", loop_count);
+}
+
+void Analyse_ID25(void) // Loop End
+{
+ loop_count--;
+ if (loop_count > 0)
+ {
+ if (draw) printf(" Loop End, Still To Go %d Time(s)!\n", loop_count);
+ curr = loop_start;
+ }
+ else
+ {
+ if (draw) printf(" Loop End, Finished\n");
+ }
+}
+
+void Analyse_ID26(void) // Call Sequence
+{
+ call_pos = curr;
+ call_num = Get2(&data[0]);
+ call_cur = 0;
+ jump = (signed short)(data[2] + data[3] * 256);
+ if (draw) printf(" Call Sequence, Number of Calls : %d, First: %d (To Block %d)\n", call_num, jump, curr + jump + 1);
+ curr += jump;
+ curr--;
+}
+
+void Analyse_ID27(void) // Return from Sequence
+{
+ call_cur++;
+ if (call_cur == call_num)
+ {
+ if (draw) printf(" Return from Call, Last Call Finished\n");
+ curr = call_pos;
+ }
+ else
+ {
+ curr = call_pos;
+ data = &mem[block[curr] + 1];
+ jump = (signed short)(data[call_cur * 2 + 2] + data[call_cur * 2 + 3] * 256);
+ if (draw) printf(" Return from Call, Calls Left: %d, Next: %d (To Block %d)\n",
+ call_num - call_cur, jump, curr + jump + 1);
+ curr += jump;
+ curr--;
+ }
+}
+
+void Analyse_ID28(void) // Select Block
+{
+ num_sel = data[2];
+ printf(" Select :\n");
+ data += 3;
+ for (n = 0; n < num_sel; n++)
+ {
+ jump = (signed short)(data[0] + data[1] * 256);
+ jumparray[n] = jump;
+ CopyString(spdstr, &data[3], data[2]);
+ printf("%5d : %s\n", n + 1, spdstr);
+ data += 3 + data[2];
+ }
+
+ //no interactive shell. choose 1.
+ PauseWave(200);
+ k = 1;
+
+ /*
+ printf(">> Press the number!\n");
+ PauseWave(5000); // Why?!?!?!?!
+ k = getchar();
+ if (k == 27) Error("ESCAPE key pressed!");
+ k -= 48;
+ if (k<1 || k>num_sel) printf("Illegal Selection... Continuing...\n");
+ else
+ */
+ {
+ curr += jumparray[k - 1];
+ curr--;
+ }
+}
+
+void Analyse_ID2A(void) // Stop the tape if in 48k mode
+{
+ if (mode128)
+ {
+ if (draw) printf(" Stop the tape only in 48k mode!\n");
+ }
+ else
+ {
+ if (draw) printf(" Stop the tape in 48k mode!\n");
+ PauseWave(3000);
+ }
+}
+
+void Analyse_ID30(void) // Description
+{
+ CopyString(pstr, &data[1], data[0]);
+ if (draw) printf(" Description: %s\n", pstr);
+}
+
+void Analyse_ID31(void) // Message
+{
+ CopyString(pstr, &data[2], data[1]);
+ // Pause in Message block is ignored ...
+ if (draw) printf(" Message: %s\n", pstr);
+}
+
+void Analyse_ID32(void) // Archive Info
+{
+ if (draw)
+ {
+ if (data[3] == 0)
+ {
+ CopyString(spdstr, &data[5], data[4]);
+ sprintf(tstr, " Title: %s", spdstr);
+ MakeFixedString(tstr, 69);
+ strcpy(tstr + 52, " (-v for more)");
+ printf("%s\n", tstr);
+ }
+ else
+ {
+ sprintf(tstr, " Archive Info");
+ MakeFixedString(tstr, 69);
+ strcpy(tstr + 52, " (-v for more)");
+ printf("%s\n", tstr);
+ }
+ }
+}
+
+void Analyse_ID33(void) // Hardware Info
+{
+ if (data[1] == 0 && data[2] > 0x14 && data[2] < 0x1a && data[3] == 1) cpc = 1;
+ if (data[1] == 0 && data[2] == 0x09 && data[3] == 1) sam = 1;
+ if (draw)
+ {
+ if (data[1] != 0 || data[3] != 1)
+ {
+ sprintf(tstr, " Hardware Type");
+ MakeFixedString(tstr, 69);
+ strcpy(tstr + 52, " (-v for more)");
+ printf("%s\n", tstr);
+ }
+ else
+ {
+ printf(" This tape is made for %s !\n", hwids_01[data[2]]);
+ }
+ }
+}
+
+void Analyse_ID34(void) // Emulation info
+{
+ if (draw) printf(" Information for emulators.\n");
+}
+
+void Analyse_ID35(void) // Custom Info
+{
+ CopyString(pstr, data, 16);
+ if (draw)
+ {
+ if (strcmp(pstr, "POKEs "))
+ printf(" Custom Info: %s\n", pstr);
+ // Only Name of Custom info except POKEs is used ...
+ else
+ {
+ sprintf(tstr, " Custom Info: %s", pstr);
+ MakeFixedString(tstr, 69);
+ strcpy(tstr + 52, " (-v for more)");
+ printf("%s\n", tstr);
+ }
+ }
+}
+
+void Analyse_ID40(void) // Snapshot
+{
+ if (draw) printf(" Snapshot (Not Supported yet)\n");
+}
+
+void Analyse_ID5A(void) // ZXTape!
+{
+ if (draw) printf(" Start of the new tape (Merged Tapes)\n");
+}
+
+void Analyse_Unknown(void) // Unknown blocks
+{
+ if (draw) printf(" Unknown block %02X !\n", id);
+}
+
+////////////////////////
+// Main TZX2WAV program
+////////////////////////
+
+int tzx2csw(fileTYPE *f)
+{
+ nfreq = 44100;
+ starting = 1;
+ ending = 0;
+ expand = 0;
+ mode128 = 0;
+ skippause = 0;
+ inv = 0;
+ oflen = 0;
+
+ if (nfreq) freq = nfreq;
+
+ mem = (unsigned char *)malloc(f->size);
+ if (mem == NULL)
+ {
+ Error("Not enough memory to load the file!");
+ GarbageCollector();
+ return 0;
+ }
+
+ // Start reading file...
+ FileReadAdv(f, mem, 10);
+ mem[7] = 0;
+
+ if (strcmp((const char*)mem, "ZXTape!"))
+ {
+ Error("File is not in ZXTape format!");
+ GarbageCollector();
+ return 0;
+ }
+
+ printf("\nZXTape file revision %d.%02d\n", mem[8], mem[9]);
+ if (!mem[8])
+ {
+ Error("Development versions of ZXTape format are not supported!");
+ GarbageCollector();
+ return 0;
+ }
+
+ if (mem[8] > MAJREV) printf("\n-- Warning: Some blocks may not be recognised and used!\n");
+ if (mem[8] == MAJREV && mem[9] > MINREV) printf("\n-- Warning: Some of the data might not be properly recognised!\n");
+ FileReadAdv(f, mem, f->size - 10);
+ numblocks = 0; pos = 0;
+ not_rec = 0;
+
+ // Go through the file and record block starts ...
+ // (not necessary, could just go right through it)
+
+ while (pos < f->size - 10)
+ {
+ block[numblocks] = pos;
+ pos++;
+ switch (mem[pos - 1])
+ {
+ case 0x10: pos += Get2(&mem[pos + 0x02]) + 0x04; break;
+ case 0x11: pos += Get3(&mem[pos + 0x0F]) + 0x12; break;
+ case 0x12: pos += 0x04; break;
+ case 0x13: pos += (mem[pos + 0x00] * 0x02) + 0x01; break;
+ case 0x14: pos += Get3(&mem[pos + 0x07]) + 0x0A; break;
+ case 0x15: pos += Get3(&mem[pos + 0x05]) + 0x08; break;
+ case 0x16: pos += Get4(&mem[pos + 0x00]) + 0x04; break;
+ case 0x17: pos += Get4(&mem[pos + 0x00]) + 0x04; break;
+
+ case 0x20: pos += 0x02; break;
+ case 0x21: pos += mem[pos + 0x00] + 0x01; break;
+ case 0x22: break;
+ case 0x23: pos += 0x02; break;
+ case 0x24: pos += 0x02; break;
+ case 0x25: break;
+ case 0x26: pos += Get2(&mem[pos + 0x00]) * 0x02 + 0x02; break;
+ case 0x27: break;
+ case 0x28: pos += Get2(&mem[pos + 0x00]) + 0x02; break;
+
+ case 0x2A: pos += 0x04; break;
+
+ case 0x30: pos += mem[pos + 0x00] + 0x01; break;
+ case 0x31: pos += mem[pos + 0x01] + 0x02; break;
+ case 0x32: pos += Get2(&mem[pos + 0x00]) + 0x02; break;
+ case 0x33: pos += (mem[pos + 0x00] * 0x03) + 0x01; break;
+ case 0x34: pos += 0x08; break;
+ case 0x35: pos += Get4(&mem[pos + 0x10]) + 0x14; break;
+
+ case 0x40: pos += Get3(&mem[pos + 0x01]) + 0x04; break;
+
+ case 0x5A: pos += 0x09; break;
+
+ default: pos += Get4(&mem[pos + 0x00]) + 0x04;
+ not_rec = 1;
+ }
+ numblocks++;
+ }
+
+ printf("Number of Blocks: %d\n", numblocks);
+
+ if (not_rec)
+ {
+ printf("\n-- Warning: Some blocks were *NOT* recognised!\n");
+ }
+
+ curr = 0;
+
+ if (starting > 1)
+ {
+ if (starting > numblocks)
+ {
+ Error("Invalid Starting Block");
+ GarbageCollector();
+ return 0;
+ }
+ curr = starting - 1;
+ }
+
+ if (ending > 0)
+ {
+ if (ending > numblocks || ending < starting)
+ {
+ Error("Invalid Ending Block");
+ GarbageCollector();
+ return 0;
+ }
+ numblocks = ending;
+ }
+
+ printf("\nCreating CSW v1");
+ printf(" file using %d Hz frequency ...\n\n", freq);
+
+ CSW1_Init();
+
+ singlepulse = 0;
+ manchester = 0;
+ cycle = (double)freq / 3500000.0; // This is for the conversion later ...
+
+ /////////////////////////////////////////////////////
+ // Start replay of blocks (Main loop of the program)
+ /////////////////////////////////////////////////////
+
+ while (curr < numblocks)
+ {
+ if (draw) printf("Block %03d:", curr + 1);
+
+ id = mem[block[curr]];
+ data = &mem[block[curr] + 1];
+ switch (id)
+ {
+ case 0x10: Analyse_ID10(); // Standard Loading Data block
+ break;
+ case 0x11: Analyse_ID11(); // Custom Loading Data block
+ break;
+ case 0x12: Analyse_ID12(); // Pure Tone
+ break;
+ case 0x13: Analyse_ID13(); // Sequence of Pulses
+ break;
+ case 0x14: Analyse_ID14(); // Pure Data
+ break;
+ case 0x15: Analyse_ID15(); // Direct Recording
+ break;
+ case 0x16: Analyse_ID16(); // C64 ROM Type Data Block
+ break;
+ case 0x17: Analyse_ID17(); // C64 Turbo Tape Data Block
+ break;
+ case 0x20: Analyse_ID20(); // Pause or Stop the Tape command
+ break;
+ case 0x21: Analyse_ID21(); // Group Start
+ break;
+ case 0x22: Analyse_ID22(); // Group End
+ break;
+ case 0x23: Analyse_ID23(); // Jump To Relative
+ break;
+ case 0x24: Analyse_ID24(); // Loop Start
+ break;
+ case 0x25: Analyse_ID25(); // Loop End
+ break;
+ case 0x26: Analyse_ID26(); // Call Sequence
+ break;
+ case 0x27: Analyse_ID27(); // Return from Sequence
+ break;
+ case 0x28: Analyse_ID28(); // Select Block
+ break;
+ case 0x2A: Analyse_ID2A(); // Stop the tape if in 48k mode
+ break;
+ case 0x30: Analyse_ID30(); // Description
+ break;
+ case 0x31: Analyse_ID31(); // Message
+ break;
+ case 0x32: Analyse_ID32(); // Archive Info
+ break;
+ case 0x33: Analyse_ID33(); // Hardware Info
+ break;
+ case 0x34: Analyse_ID34(); // Emulation info
+ break;
+ case 0x35: Analyse_ID35(); // Custom Info
+ break;
+ case 0x40: Analyse_ID40(); // Snapshot
+ break;
+ case 0x5A: Analyse_ID5A(); // ZXTape!
+ break;
+ default: Analyse_Unknown(); // Unknown blocks
+ }
+
+ // TZX file blocks analysis finished
+ // Now we start generating the sound waves
+
+ if ((id == 0x10 || id == 0x11 || id == 0x14)) // One of the data blocks ...
+ {
+ if (id != 0x14) Identify(datalen, data, 0);
+ else strcpy(tstr, " Pure Data ");
+ if (id == 0x10) sprintf(spdstr, "Normal Speed");
+ else sprintf(spdstr, " Speed: %3d%%", speed);
+ sprintf(pstr, "Pause: %5d ms", pause_ms);
+ if (draw) printf("%s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr);
+
+ {
+ while (pilot) // Play PILOT TONE
+ {
+ PlayWave(sb_pilot);
+ pilot--;
+ }
+ if (sb_sync1) // Play first SYNC pulse
+ {
+ PlayWave(sb_sync1);
+ }
+ if (sb_sync2) // Play second SYNC pulse
+ {
+ PlayWave(sb_sync2);
+ }
+ datapos = 0;
+ while (datalen) // Play actual DATA
+ {
+ if (datalen != 1) bitcount = 8;
+ else bitcount = lastbyte;
+ databyte = data[datapos];
+ while (bitcount)
+ {
+ if (databyte & 0x80) sb_bit = sb_bit1;
+ else sb_bit = sb_bit0;
+ PlayWave(sb_bit); // Play first pulse of the bit
+ if (!singlepulse)
+ {
+ PlayWave(sb_bit); // Play second pulse of the bit
+ }
+ databyte <<= 1;
+ bitcount--;
+ }
+ datalen--; datapos++;
+ }
+ singlepulse = 0; // Reset flag for next TZX blocks
+
+ // If there is pause after block present then make first millisecond the oposite
+ // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause
+ if (pause_ms)
+ {
+ PauseWave(1);
+ if (pause_ms > 1) PauseWave(pause_ms - 1);
+ }
+ }
+ }
+
+ if (id == 0x16) // C64 ROM data block ...
+ {
+ IdentifyC64ROM(datalen, data, 0);
+ sprintf(pstr, "Pause: %5d ms", pause_ms);
+ if (draw) printf(" %s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr);
+
+ {
+ sb_pilot = Samples(sb_pilot);
+ sb_sync1 = Samples(sb_sync1); sb_sync2 = Samples(sb_sync2);
+ sb_bit1_f = Samples(sb_bit1_f); sb_bit1_s = Samples(sb_bit1_s);
+ sb_bit0_f = Samples(sb_bit0_f); sb_bit0_s = Samples(sb_bit0_s);
+ sb_finishbyte_f = Samples(sb_finishbyte_f);
+ sb_finishbyte_s = Samples(sb_finishbyte_s);
+ sb_finishdata_f = Samples(sb_finishdata_f);
+ sb_finishdata_s = Samples(sb_finishdata_s);
+ sb_trailing = Samples(sb_trailing);
+ num_lead_in = 0;
+ while (pilot) // Play PILOT TONE
+ {
+ PlayC64(sb_pilot);
+ pilot--;
+ }
+ if (sb_sync1) PlayC64(sb_sync1); // Play SYNC PULSES
+ if (sb_sync2) PlayC64(sb_sync2);
+ datapos = 0;
+ while (datalen) // Play actual DATA
+ {
+ if (datalen != 1)
+ {
+ bitcount = 8;
+ PlayC64ROMByte(data[datapos], 0);
+ }
+ else
+ {
+ bitcount = lastbyte;
+ PlayC64ROMByte(data[datapos], 1);
+ }
+ databyte = data[datapos];
+ datalen--; datapos++;
+ }
+ while (trailing) // Play TRAILING TONE
+ {
+ PlayC64(sb_trailing);
+ trailing--;
+ }
+
+ // If there is pause after block present then make first millisecond the oposite
+ // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause
+
+ if (pause_ms)
+ {
+ PauseWave(pause_ms / 2);
+ PauseWave((pause_ms / 2) + (pause_ms % 2));
+ }
+ }
+ }
+
+ if (id == 0x17) // C64 Turbo Tape data block ...
+ {
+ IdentifyC64Turbo(datalen, data, 0);
+ sprintf(pstr, "Pause: %5d ms", pause_ms);
+ if (draw) printf(" %s Length:%6d %s %s\n", tstr, datalen, spdstr, pstr);
+
+ {
+ sb_bit1 = Samples(sb_bit1);
+ sb_bit0 = Samples(sb_bit0);
+ while (num_lead_in) // Play Lead In bytes
+ {
+ bitcount = 8;
+ PlayC64TurboByte(lead_in_byte);
+ num_lead_in--;
+ }
+ datapos = 0;
+ while (datalen) // Play actual DATA
+ {
+ if (datalen != 1) bitcount = 8;
+ else bitcount = lastbyte;
+ PlayC64TurboByte(data[datapos]);
+ databyte = data[datapos];
+ datalen--; datapos++;
+ }
+ while (trailing) // Play Trailing bytes
+ {
+ bitcount = 8;
+ PlayC64TurboByte((unsigned char)sb_trailing);
+ trailing--;
+ }
+
+ // If there is pause after block present then make first millisecond the oposite
+ // pulse of last pulse played and the rest in LOAMP ... otherwise don't do ANY pause
+
+ if (pause_ms)
+ {
+ PauseWave(pause_ms / 2);
+ PauseWave((pause_ms / 2) + (pause_ms % 2));
+ }
+ }
+ }
+
+ curr++; // We continue to replay the next TZX block
+ } // This is the main loop end
+
+ PauseWave(200); // Finish always with 200 ms of pause after the last block
+ printf("\n%d bytes sent to the core.\n", oflen);
+ GarbageCollector();
+ return 1;
+}
diff --git a/tzx2wav.h b/tzx2wav.h
new file mode 100644
index 0000000..64e6a9e
--- /dev/null
+++ b/tzx2wav.h
@@ -0,0 +1,3 @@
+#include "file_io.h"
+
+int tzx2csw(fileTYPE *f);
diff --git a/user_io.c b/user_io.c
index 5d1c442..bdb349e 100644
--- a/user_io.c
+++ b/user_io.c
@@ -20,6 +20,7 @@
#include "config.h"
#include "menu.h"
#include "x86.h"
+#include "tzx2wav.h"
#define BREAK 0x8000
@@ -656,20 +657,32 @@ int user_io_file_tx(char* name, unsigned char index)
spi8(0xff);
DisableFpga();
- while (bytes2send)
+ if (strlen(f.name) > 4 && !strcasecmp(f.name + strlen(f.name) - 4, ".tzx"))
{
- iprintf(".");
-
- uint16_t chunk = (bytes2send>512) ? 512 : bytes2send;
-
- FileReadSec(&f, buf);
-
+ printf("Processing TZX...\n");
+
EnableFpga();
spi8(UIO_FILE_TX_DAT);
- spi_write(buf, chunk, fio_size);
+ tzx2csw(&f);
DisableFpga();
+ }
+ else
+ {
+ while (bytes2send)
+ {
+ iprintf(".");
- bytes2send -= chunk;
+ uint16_t chunk = (bytes2send > 512) ? 512 : bytes2send;
+
+ FileReadSec(&f, buf);
+
+ EnableFpga();
+ spi8(UIO_FILE_TX_DAT);
+ spi_write(buf, chunk, fio_size);
+ DisableFpga();
+
+ bytes2send -= chunk;
+ }
}
FileClose(&f);