diff --git a/sdk/bin/.gitignore b/sdk/bin/.gitignore index ccf57af..2c6336a 100644 --- a/sdk/bin/.gitignore +++ b/sdk/bin/.gitignore @@ -10,6 +10,8 @@ fcut fpad fpoke rcs +Png2Rcs +GenTape GenRom AddItem Bit2Bin diff --git a/sdk/bin/GenTape.exe b/sdk/bin/GenTape.exe new file mode 100755 index 0000000..8f7c969 Binary files /dev/null and b/sdk/bin/GenTape.exe differ diff --git a/sdk/bin/GenTape.exe.license b/sdk/bin/GenTape.exe.license new file mode 100644 index 0000000..2e6530d --- /dev/null +++ b/sdk/bin/GenTape.exe.license @@ -0,0 +1,11 @@ +SPDX-FileName: GenTape.exe + +SPDX-FileType: BINARY + +SPDX-FileChecksum: SHA1: a45dbb2e6ed331da5258f0d1a1d8348a7f9e4868 + +SPDX-FileCopyrightText: Copyright (C) 2013-2015, 2021 Antonio Villena + +SPDX-License-Identifier: GPL-3.0-only + +SPDX-FileComment: GenTape version 1.0 (1 Jun 2015) - a Tape File Generator. diff --git a/sdk/src/tools/GenTape.c b/sdk/src/tools/GenTape.c new file mode 100644 index 0000000..ea50e22 --- /dev/null +++ b/sdk/src/tools/GenTape.c @@ -0,0 +1,538 @@ +/* + * GenTape - a Tape File Generator. + * + * Copyright (C) 2013-2015, 2021 Antonio Villena + * + * 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, version 3. + * + * 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 . + * + * SPDX-FileCopyrightText: Copyright (C) 2013-2015, 2021 Antonio Villena + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include + +#define PROGRAM "GenTape" +#define DESCRIPTION "a Tape File Generator." +#define VERSION "1.0 (1 Jun 2015)" +#define COPYRIGHT "Copyright (C) 2013-2015, 2021 Antonio Villena" +#define LICENSE \ +"This program is free software: you can redistribute it and/or modify\n" \ +"it under the terms of the GNU General Public License as published by\n" \ +"the Free Software Foundation, version 3." +#define HOMEPAGE "https://github.com/zxdos/zxuno/" + +unsigned char *mem, *precalc; +char *ext, *command; +unsigned char rem= 0, inibit= 0, tzx= 0, wav= 0, channel_type= 1, + checksum, turbo, mod; +FILE *fi, *fo; +int i, j, k, l, ind= 0, nextsilence= 0; +float silence; +unsigned short length, param, frequency= 44100; + +int my_strcasecmp(const char *s1, const char *s2){ + register const unsigned char *us1= (const unsigned char *)s1, + *us2= (const unsigned char *)s2; + while ( (*us1 & 95) == (*us2++ & 95) ) + if( *us1++ == '\0' ) + return 0; + return (*us1 & 95) - (*--us2 & 95); +} + +void outbits( short val ){ + for ( i= 0; i0xff000 ) + fwrite( precalc, 1, ind, fo ), + ind= 0; + inibit^= 1; +} + +void obgen( int nor ){ + outbits( (nor+rem)/mod ); + rem= (nor+rem)%mod; +} + +char char2hex(char value, char * name){ + if( value<'0' || value>'f' || value<'A' && value>'9' || value<'a' && value>'F' ) + printf("Invalid character %c or '%s' not exists\n", value, name), + exit(-1); + return value>'9' ? 9+(value&7) : value-'0'; +} + +int parseHex(char * name, int index){ + int flen= strlen(name); + if( name[0]!='-' ) + for ( i= 0; i<10 && name[i]; i++ ) + mem[i+7]= name[i]; + else if( flen & 1 ){ + flen>>= 1; + flen>10 && index==7 && (flen= 10); + for ( i= 0; i < flen; i++ ) + mem[i+index]= char2hex(name[i+1<<1], name) | char2hex(name[i<<1|1], name) << 4; + } + while( ++i<11 ) + mem[i+6]= ' '; + return flen; +} + +int wavsilence( float msecs ){ + fwrite( precalc, 1, ind, fo ); + rem= ind= 0; + fwrite( precalc+0x100000, 1, frequency*(channel_type&3)*msecs/1000, fo); +} + +void tapewrite( unsigned char *buff, int length ){ + if( wav ){ + buff+= 2; + length-= 2; + j= *buff>>7&1 ? 3223 : 8063; + while( j-- ) + obgen( 2168*2 ); + obgen( 667*2 ); + obgen( 735*2 ); + while ( length-- ) + for( k= 0, j= *buff++; k<8; k++, j<<= 1 ) + obgen( l= 1710 << ((j & 0x80)>>7) ), + obgen( l ); + obgen( l ); + } + else + fwrite(buff, 1, length, fo); +} + +void show_help() { + printf( + PROGRAM " version " VERSION " - " DESCRIPTION "\n" + COPYRIGHT "\n" + LICENSE "\n" + "Home page: " HOMEPAGE "\n" + "\n" + "Usage:\n" + " " PROGRAM " [] [] \n" + " [ basic \n" + " | hdata
\n" + " | data \n" + " | pilot \n" + " | pulse .. \n" + " | pause \n" + " | pure \n" + " | turbo \n" + " \n" + " | stop48\n" + " | plug-xxx-N .. ]\n" + "\n" + " Target file, between TAP, TZX or WAV file\n" + " Up to 10 chars name between single quotes or in hexadecimal\n" + " In decimal, first BASIC line to execute\n" + "
In hexadecimal, address of the binary block\n" + " Hexadecimal string or filename as data origin of that block\n" + " \n" + " Length of zero/one/syncs/pilot pulses at 3.528MHz clock\n" + " \n" + " Duration of pilot/pause after block in milliseconds\n" + " Number of pulses in the sequence of pulses\n" + " Length of X-th pulse in the sequence at 3.528MHz clock\n" + " External generator, must exists xxx.exe and accept N params\n" + " stop48 Only TZX. Signal end of tape on 48K machines\n" + "\n" + " WAV options:\n" + " Sample frequency, 44100 or 48000. Default is 44100\n" + " Possible values are: mono (default), stereo or stereoinv\n" + ); +} + +int main(int argc, char* argv[]){ + mem= (unsigned char *) malloc (0x20000); + if( argc==1 ) + show_help(), + exit(0); + while( 1 ) + if( !my_strcasecmp(argv[1], "mono") || !my_strcasecmp(argv[1], "44100") ) + ++argv, --argc; + else if( !my_strcasecmp(argv[1], "stereo") ) + channel_type= 2, ++argv, --argc; + else if( !my_strcasecmp(argv[1], "stereoinv") ) + channel_type= 6, ++argv, --argc; + else if( !my_strcasecmp(argv[1], "48000") ) + frequency= 48000, ++argv, --argc; + else + break; + mod= 7056000/frequency; + if( !(ext= strchr(argv[1], '.')) ) + printf("Invalid argument name: %s\n", argv[1]), + exit(-1); + fo= fopen(argv[1], "wb+"); + if( !fo ) + printf("Cannot create output file: %s\n", argv[1]), + exit(-1); + char filename[40]; + strcpy( filename, argv[1] ); + precalc= (unsigned char *) malloc (0x200000); + if( !my_strcasecmp((char *)strchr(argv[1], '.'), ".tzx" ) ) + fprintf( fo, "ZXTape!" ), + *(int*)mem= 0xa011a, + fwrite(mem, ++tzx, 3, fo), + mem[0]= 0x10; + else if( !my_strcasecmp((char *)strchr(argv[1], '.'), ".wav" ) ){ + memset(mem, wav++, 44); + memset(precalc, 128, 0x200000); + *(int*)mem= 0x46464952; + *(int*)(mem+8)= 0x45564157; + *(int*)(mem+12)= 0x20746d66; + *(char*)(mem+16)= 0x10; + *(char*)(mem+20)= 0x01; + *(char*)(mem+22)= *(char*)(mem+32)= channel_type&3; + *(short*)(mem+24)= frequency; + *(int*)(mem+28)= frequency*(channel_type&3); + *(char*)(mem+34)= 8; + *(int*)(mem+36)= 0x61746164; + fwrite(mem, 1, 44, fo); + } + while ( argc-- > 2 ){ + wav && nextsilence && wavsilence( silence ); + if( !my_strcasecmp(argv++[2], "basic")){ + *(short*)(mem+1)= 1000; + tzx && fwrite(mem, 1, 3, fo); + param= atoi(argv[3]); + fi= fopen(argv[4], "rb"); + if( fi ) + length= fread(mem+27, 1, 0x20000-27, fi); + else + length= parseHex(argv[4], 27); + *(int*)(mem+3)= 19; + parseHex(argv[2], 7); + *(short*)(mem+17)= *(short*)(mem+21)= length; + *(short*)(mem+19)= param; + length+= 2; + *(short*)(mem+24)= length; + mem[26]= 255; + for ( checksum= 0, i= 5; i<23; ++i ) + checksum^= mem[i]; + mem[23]= checksum; + for ( checksum= 0, i= 26; i<26+length-1; ++i ) + checksum^= mem[i]; + mem[length+25]= checksum; + tapewrite(mem+3, 21); + wav && wavsilence( 1000 ); + *(short*)(mem+1)= 2000; + tzx && fwrite(mem, 1, 3, fo); + tapewrite(mem+24, length+2); + silence= nextsilence= 2000; + fclose(fi); + argc-= 3; + argv+= 3; + } + else if( !my_strcasecmp(argv[1], "hdata")){ + *(short*)(mem+1)= 1000; + tzx && fwrite(mem, 1, 3, fo); + param= strtol(argv[3], NULL, 16); + fi= fopen(argv[4], "rb"); + if( fi ) + length= fread(mem+27, 1, 0x20000-27, fi); + else + length= parseHex(argv[4], 27); + *(short*)(mem+1)= 1000; + *(short*)(mem+3)= 19; + *(short*)(mem+5)= 0x300; + parseHex(argv[2], 7); + *(short*)(mem+17)= length; + *(short*)(mem+19)= param; + *(unsigned short*)(mem+21)= 0x8000; + length+= 2; + *(short*)(mem+24)= length; + mem[26]= 255; + for ( checksum= 0, i= 5; i<23; ++i ) + checksum^= mem[i]; + mem[23]= checksum; + for ( checksum= 0, i= 26; i<26+length-1; ++i ) + checksum^= mem[i]; + mem[length+25]= checksum; + tapewrite(mem+3, 21); + wav && wavsilence( 1000 ); + *(short*)(mem+1)= 2000; + tzx && fwrite(mem, 1, 3, fo); + tapewrite(mem+24, length+2); + silence= nextsilence= 2000; + fclose(fi); + argc-= 3; + argv+= 3; + } + else if( !my_strcasecmp(argv[1], "data")){ + *(short*)(mem+1)= 2000; + tzx && fwrite(mem, 1, 3, fo); + fi= fopen(argv[2], "rb"); + if( fi ) + length= fread(mem+6, 1, 0x20000-6, fi); + else + length= parseHex(argv[2], 6); + *(short*)(mem+3)= length+= 2; + mem[5]= 255; + for ( checksum= 0, i= 5; i<5+length-1; ++i ) + checksum^= mem[i]; + mem[length+4]= checksum; + tapewrite(mem+3, length+2); + silence= nextsilence= 2000; + fclose(fi); + --argc; + ++argv; + } + else if( !my_strcasecmp(argv[1], "pause")){ + nextsilence= silence= atof(argv[2]); + if( tzx ) + mem[1]= 0x20, + *(short*)(mem+2)= nextsilence, + fwrite(mem+1, 1, 3, fo); + else if( !wav ) + printf("Error: pause command not allowed in TAP files\n"), + exit(-1); + --argc; + ++argv; + } + else if( !my_strcasecmp(argv[1], "pilot")){ + k= atoi(argv[2]); + if( tzx ) + mem[1]= 0x12, + *(short*)(mem+2)= k, + *(unsigned short*)(mem+4)= atof(argv[3])*3500/k+0.5, + fwrite(mem+1, 1, 5, fo); + else if( wav ){ + k<<= 1; + j= atof(argv[3])*7056/k+0.5; + while( j-- ) + obgen( k ); + } + else + printf("Error: pilot command not allowed in TAP files\n"), + exit(-1); + nextsilence= 0; + argc-= 2; + argv+= 2; + } + else if( !my_strcasecmp(argv[1], "pulse")){ + k= atoi(argv++[2]); + if( tzx ){ + mem[1]= 0x13; + *(unsigned char*)(mem+2)= k; + for ( j= 0; j>16; + fwrite(mem+1, 1, length+19, fo); + } + else{ + mem[1]= 0x14; + *(short*)(mem+2)= atoi(argv++[2]); + *(short*)(mem+4)= atoi(argv++[2]); + *(char*)(mem+6)= 8; + *(unsigned short*)(mem+7)= atoi(argv++[2]); + if( fi ) + length= fread(mem+12, 1, 0x20000-12, fi); + else + length= parseHex(argv[2], 12); + *(unsigned short*)(mem+9)= length; + *(unsigned char*)(mem+11)= length>>16; + fwrite(mem+1, 1, length+11, fo); + } + ++argv; + } + else if( wav ){ + if( turbo ){ + k= atoi(argv[2]) << 1; + j= atof(argv[7])*7056/k+0.5; + while( j-- ) + obgen( k ); + obgen( atoi(argv[3]) << 1 ); + obgen( atoi(argv[4]) << 1 ); + } + if( fi ) + length= fread(mem, 1, 0x20000, fi); + else + length= parseHex(argv[5+turbo*4], 0); + j= 0; + param= atoi(argv[2+turbo*3]) << 1; + k= atoi(argv[3+turbo*3]) << 1; + while ( length-- ) + for( wav= 0, checksum= mem[j++]; wav<8; wav++, checksum<<= 1 ) + obgen( l= checksum & 0x80 ? k : param ), + obgen( l ); + obgen( l ); + fclose(fi); + argv+= turbo+1<<2; + nextsilence= silence= atof(argv[0]); + } + else + printf("Error: pure or turbo command not allowed in TAP files\n"), + exit(-1); + argc-= turbo+1<<2; + } + else if( strchr(argv[1], '-') + && (*strchr(argv[1], '-')= 0, !my_strcasecmp(argv[1], "plug")) ){ + argv[1]+= 5; + k= atoi(strstr(argv[1], "-")+1); + *strchr(argv[1], '-')= 0; + command= (char *) malloc (0x100); + sprintf(command, "%s %d %s tmp.%s", argv[1], frequency, + channel_type-1 ? (channel_type-2?"stereoinv":"stereo") : "mono", ext+1); + argc-= k; + while( k-- ) + strcat(command, " "), + strcat(command, argv++[2]); + if( system(command) ) + printf("Error: plug error with command: %s\n", command), + exit(-1); + else{ + fwrite( precalc, 1, ind, fo ); + rem= ind= 0; + sprintf(command, "tmp.%s", ext+1); + fi= fopen(command, "rb"); + if( fi ){ + if( tzx ) + fseek(fi, 0, SEEK_END), + i= ftell(fi)-10, + fseek(fi, 10, SEEK_SET); + else + 0!=fread(mem, 1, 44, fi), + i= *(int*)(mem+40); + j= i>>20; + k= i&0xfffff; + for ( int i= 0; i