/*************************************************************/ /* AUTHOR .... : Bert van Oortmarssen */ /* NAME ...... : zx81tape.c */ /* PROJECTS .. : zx81 */ /* DATE ...... : Sat 10-25-1997 */ /* TASK ...... : */ /* */ /*************************************************************/ #include #include #include #include #include typedef unsigned char BYTE; #ifdef _WIN32 # define VEEL 2000000 #else # define VEEL 30000 #endif static int nfill=0; static int n0=0; static int n1=0; char pathbuf [200]; char namestem [200]; char OptFileNameS [200]; char ZxName [40]; int OptUnknownZero = 0; int OptUnknownOne = 0; int OptSavedNameP = 1; int OptFileNameP = 0; int OptRestart = 0; int OptList = 0; int OptCleanTmp = 0; #ifdef _DEBUG int OptMoreInfo = 1; #else int OptMoreInfo = 0; #endif int OptTreshhold = 30; int OptKsideDiv = 3; int OptStep = 0; int OptRemakePfile = 0; int OptRemakeBitfile = 0; #define MOREINFO(X) if (OptMoreInfo) printf X #define NSTATISTICS 512 static int statistics[NSTATISTICS]; static char ZxChar [65] = "____________________________0123456789abcdefghijklmnopqrstuvwxyz"; void DoError (char *pszError) { fputs (pszError, stderr); fputc ('\n', stderr); exit(1); } void MakeFileName (char *stem, char *extra, char *extn, char *outbuf) { strcpy (outbuf, stem); if (extra) strcat (outbuf, extra); if (extn) strcat (outbuf, extn ); } int SkipExistingFile (char *pszFileName) { struct stat mstat; if (OptRestart) return (0); return (0== stat(pszFileName, &mstat)); } void Format (BYTE *bptr, size_t bread) { int j, n, k, m, nlength, nplat, ncnt, nkwart; for (n=0; n < (int) bread; n++) { k = bptr[n]; /* printf ("%02X ", k); */ /* normalize */ if (k < 0x80) k = 0xFF - k; k -= 0x80; k <<= 1; /* printf ("=%02X\n", k); */ k = (k < OptTreshhold) ? 0 : 0xF0; bptr [n] = k; } if (nfill == 0) { n = bread / 2; k = n + (n / 2); ncnt = 0; /* find the longest sequence of zero's */ for (m=n; m < k; m++) { if (bptr[m]) { if (ncnt > nfill) nfill = ncnt; ncnt = 0; } else ncnt++; } MOREINFO(("treshhold=%d bread=%u nfill=%d\n", OptTreshhold, bread, nfill)); } if (nfill) { nkwart= nfill >> 2; ncnt = 0; for (n=0; n < bread; n++) { if (bptr[n]) { if (ncnt && n > nkwart && ncnt < nkwart) { for (j=1; j <= ncnt; j++) bptr[n-j] = 0xF0; } ncnt = 0; } else ncnt++; } if (n0==0 || n1==0) { n = bread / 2; k = n + (n / 2); memset (statistics, 0, sizeof(statistics)); while (n < k && bptr[n]) n++; while (n < k && bptr[n]==0) n++; /* now we are positioned at a nonzero signal */ /* start walking until we have seen at least */ /* nplat zero signals & the current signal */ /* is nonzero .... */ ncnt = 0; nlength = 0; nplat = (2 * nfill) / 3; for (; n < k; n++) { if (bptr[n]) { if (ncnt >= nplat) { if (nlength < NSTATISTICS) statistics [nlength]++; nlength = 0; } ncnt = 0; } else ncnt++; nlength++; } #if(0) for (m=0; m < NSTATISTICS; m++) { printf ("%cm=%3d %5d ", (m%4)?' ':'\n' , m, statistics[m]); } #endif printf ("\nnplat = %d\n", nplat); for (m=0; m < NSTATISTICS; m++) { if (statistics[m] > statistics[n0]) n0 = m; if (statistics[m] < (statistics[n0]>>2)) break; } for (m=NSTATISTICS-1; m >= 0; m--) { if (statistics[m] > statistics[n1]) n1 = m; if (statistics[m] < (statistics[n1]>>2)) break; } printf ("\nnplat = %d n0=%d n1=%d\n", nplat, n0, n1); } } } void Work (char *szfi, char *szfo) { FILE *fp; FILE *fo; BYTE *bptr; size_t bread; if ((fp=fopen (szfi, "rb"))==NULL) { fprintf (stderr," *** error *** Can't read file %s \n", szfi); exit(1); } if ((fo=fopen (szfo, "wb"))==NULL) { fprintf (stderr," *** error *** Can't open output file %s \n", szfo); fclose (fp); exit(1); } bptr = (BYTE *) malloc (VEEL); if (bptr==NULL) { fprintf (stderr," *** error *** Not enough memory \n"); exit(1); } do { bread = fread (bptr, 1, VEEL, fp); Format (bptr, bread); fwrite (bptr, 1, bread, fo); } while (bread==VEEL); bptr[0] = 0xFF; fwrite (bptr, 1, 1, fo); fclose (fo); fclose (fp); free (bptr); } void Outpoep (FILE *fo, BYTE b) { static int nc=0; if (nc==64) { fputc ('\n', fo); nc = 0; } fputc (b, fo); nc++; } void War2Bitfile (char *szfi, char *szfo) { FILE *fp; FILE *fo; int ncnt; int nlen; int nplat; int nstarted; int nsample; int k; int l0,h0,l1,h1; BYTE b; if ((fp=fopen (szfi, "rb"))==NULL) { fprintf (stderr," *** error *** Can't read file %s \n", szfi); exit(1); } if ((fo=fopen (szfo, "wt"))==NULL) { fprintf (stderr," *** error *** Can't open output file %s \n", szfo); fclose (fp); exit(1); } ncnt = 0; nlen = 0; nstarted = 0; nplat = (2 * nfill) / 3; nsample = 0; k = (n1 - n0) / OptKsideDiv; l0= n0 - k; h0= n0 + k; l1= n1 - k; h1= n1 + k; while (fread(&b, 1, 1, fp)) { if (b) { if (ncnt >= nplat) { if (nstarted) { /* correct for the last bit ... followed by lot */ /* of zero signals */ nlen += nfill - ncnt; if (nlen >= l0 && nlen <= h0) Outpoep (fo, '0'); else if (nlen >= l1 && nlen <= h1) Outpoep (fo, '1'); else { printf ("unknown bit, nsample=%d in war file\n", nsample); Outpoep (fo, '?'); } } nlen = 0; } ncnt = 0; nstarted = 1; } else ncnt++; nlen++; nsample++; } fclose (fo); fclose (fp); } void Bit2Pfile (char *szfi, char *szfo, int ntoskip) { FILE *fp; FILE *fo; int nbitcnt, nerror, cnt; int k; int j; BYTE b; BYTE *bptr = (BYTE *) malloc(40000u); if (bptr==NULL || (fp=fopen (szfi, "rb"))==NULL) { fprintf (stderr," *** error *** Can't read file %s \n", szfi); exit(1); } if ((fo=fopen (szfo, "wb"))==NULL) { fprintf (stderr," *** error *** Can't open output file %s \n", szfo); fclose (fp); exit(1); } k = 0; nerror = 0; nbitcnt = 0; cnt = 0; j = 0; while (fread(&b, 1, 1, fp)) { if (b=='1') j = 1; else if (b=='0') j = 0; else if (b=='?') { if (OptUnknownZero) j = 0; else if (OptUnknownOne) j = 1; else { nerror++; j = -2; } } else j = -1; if (j < -1) printf ("error at cnt=%d (0x%04X)\n", cnt, cnt); if (j>=0) { k <<= 1; k |= j; nbitcnt++; if (nbitcnt==8) { if (ntoskip > 0) ntoskip--; else bptr[cnt++] = k; k = 0; nbitcnt = 0; } } } if (nerror < 0) fprintf (stderr, "** aborted because of %d error(s) !! (use -i or -o to try again)\n", nerror); fwrite (bptr, 1, cnt, fo); fclose (fo); fclose (fp); free (bptr); } void Bit2Name (char *szfi) { FILE *fp; int nbitcnt, cnt; int k; int j; BYTE b; if ((fp=fopen (szfi, "rb"))==NULL) { fprintf (stderr," *** error *** Can't read name from file %s \n", szfi); exit(1); } k = 0; nbitcnt = 0; cnt = 0; while (cnt >= 0 && (cnt < sizeof(ZxName) - 1) && fread(&b, 1, 1, fp)) { if (b=='1') j = 1; else if (b=='0' || b=='?') j = 0; else j = -1; if (j>=0) { k <<= 1; k |= j; nbitcnt++; if (nbitcnt==8) { ZxName[cnt++] = ZxChar [k & 0x3F]; ZxName[cnt] = 0; if (k & 0x80) cnt = -1; k = 0; nbitcnt = 0; } } } fclose (fp); } void Uitleg (char *pszProgname) { printf (" %s filename.raw [n=NN] \n", pszProgname); printf (" (c) berto@wirehub.nl [Bert van Oortmarssen] %s %s \n\n", __DATE__, __TIME__); printf (" use to sample your old zx81 tape\n"); printf (" [8-bit mono and preferably 22KhZ (although other sample \n"); printf (" frequencies can be used)]. Remove all leading and trailing \n"); printf (" noise and silence (the first signal should represent the \n"); printf (" name of the program). Make sure recording level is > 50% of\n"); printf (" the maximum. The data MUST be save in RAW format (that is\n"); printf (" just a long sequence of 8-bit samples).\n\n"); printf (" command line arguments and options:\n"); printf (" filename.raw // the file with the raw data \n"); printf (" n=NN // threshold (0..239) default = 30 \n"); printf (" k=NN // default is 3 (higher, smaller tolerance)\n"); printf (" -e // stop on unknown bits (default)\n"); printf (" -i // set undeterminable bits to 0 (default is stop)\n"); printf (" -o // set undeterminable bits to 1\n"); printf (" -r // restart (overwrite intermediate files)\n"); printf (" -p // overwrite P-file (default is use existing)\n"); printf (" -b // overwrite Bit-file (default is use existing)\n"); printf (" -z // use 'saved' name for p.file (default)\n"); printf (" -ffilename.p // use 'filename.p' for p.file\n"); printf (" -l // generate listing 'name'.lst\n"); printf (" -c // clean up intermediate files\n"); printf (" -m // more information when processing\n"); printf (" intermediate files:\n"); printf (" filename.war // normalized raw file\n"); printf (" filename.bit // converted war file ('0','1','?')\n"); printf (" 'name'.p // bit file => p file\n"); } void MainOptions (int argc, char **argv) { int n, k, val; for (n=2; n < argc; n++) { if (argv[n][0]=='-') { k = toupper(argv[n][1]); switch (k) { case 'P' : OptRemakePfile = 1; break; case 'B' : OptRemakeBitfile = 1; break; case 'I' : OptUnknownZero = 1; break; case 'O' : OptUnknownOne = 1; break; case 'Z' : OptSavedNameP = 1; break; case 'F' : OptFileNameP = 1; OptSavedNameP = 0; strcpy (OptFileNameS, &argv[n][2]); if (OptFileNameS[0]==0) fprintf (stderr,"missing p-filename %s\n", argv[n]); break; case 'R' : OptRestart = 1; break; case 'L' : OptList = 1; break; case 'C' : OptCleanTmp = 1; break; case 'M' : OptMoreInfo = 1; break; default : fprintf (stderr,"unknown option %s\n", argv[n]); } } else if (argv[n][1]=='=') { k = toupper(argv[n][0]); val = atoi(&argv[n][2]); switch (k) { case 'N' : OptTreshhold = val; break; case 'K' : OptKsideDiv = val; break; default : fprintf (stderr,"unknown option %s\n", argv[n]); } } } } int main (int argc, char **argv) { char warbuf [220]; char bitbuf [220]; char pbuf [220]; char listbuf [220]; int j, k; if (argc>1) { MainOptions (argc, argv); memset (ZxName, 0, sizeof(ZxName)); strcpy (namestem, argv[1]); for (j=0, k=strlen(namestem)-1; j < 4 && k>0; j++, k--) if (namestem[k]=='.') { namestem[k] = 0; j = toupper (namestem[k+1]); switch (j) { case 'B' : OptStep = 2; break; case 'P' : OptStep = 3; break; } break; } strcpy (pathbuf, namestem); for (k=strlen(pathbuf)-1; k>0; k--) if (pathbuf[k]=='\\' || pathbuf[k]==':') { pathbuf[k+1] = 0; break; } if (k==0) pathbuf[0] = 0; MakeFileName (namestem, NULL, ".war", warbuf); MakeFileName (namestem, NULL, ".bit", bitbuf); if (OptStep < 1) { printf ("... creating war file %s\n", warbuf); Work (argv[1], warbuf); if (n0==0 || n1==0) DoError ("*** error *** Could not compute 0,1 bit length in samples"); } if (OptStep < 2) { if (OptRemakeBitfile || !SkipExistingFile (bitbuf)) { printf ("... creating bit file %s\n", bitbuf); War2Bitfile (warbuf, bitbuf); } else printf ("... using existing file %s\n", bitbuf); } if (OptStep < 3) { Bit2Name (bitbuf); if (ZxName[0]==0) { if (OptSavedNameP) DoError ("*** error *** Could not get the saved name "); else printf ("*** warning *** Could not get the saved name"); } } if (OptStep < 3 && OptSavedNameP) { printf ("pathbuf=[%s] ZxName=[%s]\n", pathbuf, ZxName); MakeFileName (pathbuf, ZxName, ".p", pbuf); MakeFileName (pathbuf, ZxName, ".lst", listbuf); } else { if (OptFileNameP && OptFileNameS) { strcpy (pbuf, OptFileNameS); MakeFileName (namestem, NULL, ".lst", listbuf); } else { MakeFileName (namestem, NULL, ".p", pbuf); MakeFileName (namestem, NULL, ".lst", listbuf); } } if (OptStep < 3) { if (OptRemakePfile || !SkipExistingFile (pbuf)) { printf ("... creating P file %s\n", pbuf); Bit2Pfile (bitbuf, pbuf, strlen(ZxName)); } else printf ("... using existing file %s\n", pbuf); if (OptCleanTmp) { printf ("... cleaning up intermediate file: %s\n", warbuf); remove (warbuf); printf ("... cleaning up intermediate file: %s\n", bitbuf); remove (bitbuf); } } } else Uitleg (argv[0]); return(0); }