/*
 *   Tape Ripper 1.0
 *
 *   Lettore cassette del C64
 *
 */

#include<dos.h>
#include<stdlib.h>
#include<stdio.h>

#define PORT 0x379	/* indirizzo della parallela (in lettura) */
#define C64_PAL_FREQ 985248	/* Frequenza in Hz della CPU del C64 (versione PAL) */
#define CTC_BASEFREQ 1193182	/* Frequenza base in Hz del CTC */
#define BUFSIZE 0x200000	/* 4MBytes di buffer */

unsigned long int i,cont;

void record(unsigned int *buffer)     
{
	unsigned char lsb,msb,flag;
	
	cont = 0;
	disable();	/* interrupt disabilitati... adesso si ha una risoluzione temporale continua dell'ordine di 10^-6 secondi */
	
	while((inportb(PORT)&0x40)==0x40);	/* attesa per il fronte discendente del segnale... */
	
	do	/* ...siamo sincronizzati */
	{
		flag = 0;
		outportb(0x43,0x34);	/* setto il CTC in modo 2 */
		outportb(0x40,0x00);	/* conteggio in binario, canale zero */
		outportb(0x40,0x00);	/* dividi per 65536 */
		
		while((inportb(PORT)&0x60)==0x00)	/* loop sul segnale basso */
		{
			outportb(0x43,0x00);	/* latch! ovvero: uscita del CTC congelata */
			lsb = inportb(0x40);	/* leggo l'lsb della parola */
			inportb(0x61);		/* istruzione dummy */ 
			msb = inportb(0x40);	/* leggo l'msb della parola */
			if ((msb==0x00) && (lsb!=0x00)) flag = 1;	/* se siamo scesi sotto 256 setta la flag di controllo */
		}
		while((inportb(PORT)&0x60)==0x40)	/* loop sul segnale alto... corpo identico al precedente */
		{
			outportb(0x43,0x00);
			lsb = inportb(0x40);
			inportb(0x61);
			msb = inportb(0x40);
			if ((msb==0x00) && (lsb!=0x00)) flag = 1;
		}
		outportb(0x43,0x00);	/* latch! */
		lsb = inportb(0x40);
		inportb(0x61);
		msb = inportb(0x40);
		if (flag==0) buffer[cont] = lsb + (msb << 8);	/* flag bassa, quindi nessun overflow e salvo in memoria la parola */
		else buffer[cont] = 0x00;	/* ho un overflow e quindi salvo zero, come da specifica del file TAP versione 0 */
		cont++;	/* prossima locazione di memoria */
	}
	while((!(inportb(PORT)&0x20))&&(cont<BUFSIZE));	/* cicla oppure esci se il tasto <STOP> e' stato premuto o la memoria si e' esaurita */
	enable();	/* interrupt riabilitati */
}

void buildTAP(FILE *out,unsigned int *buffer)
{
	unsigned long int data,delta;
	unsigned char header[12] = {0x43,0x36,0x34,0x2D,0x54,0x41,0x50,0x45,0x2D,0x52,0x41,0x57};	/* header del file TAP, ovvero:"C64-TAPE-RAW" */
	for(i=0;i<12;i++) fputc(header[i],out);	/* scrivo l'header */
	fputc(0x00,out);	/* $0C versione file TAP (versione 0) */
	for(i=0;i<3;i++) fputc(0x00,out);	/* $0D-0F non usati (pongo a zero) */         
	for(i=0;i<4;i++)	/* $10-13 dimensione del file (big-endian) */
	{
		fputc(data&0xFF,out);
		data = data >> 8;
	}
	for(i=0;i<cont;i++)	/* $13-EOF dati effettivi */
	{
		delta = 0x10000 - buffer[i];	/* inversione della durata */
		data = (unsigned long int)(((double)delta)*(C64_PAL_FREQ/8)/CTC_BASEFREQ);	/* calcolo del valore */
		if (data<0x100) fputc((data&0xFF),out);	/* byte inferiore a 256, lo scrivo su file normalmente */
		else fputc(0x00,out);	/* altrimenti inserisco un byte flag di overflow come da specifica */
	}
}

int main()
{
	FILE *out;
	unsigned int *buffer;
	
	if ((buffer=(unsigned int*)calloc(BUFSIZE,sizeof(unsigned int)))==0)	/* allocazione memoria per il buffer */
	{
		printf("\nerror:calloc()");
		exit(3);
	}
	if ((out=fopen("out.tap","wb"))==0)	/* apertura file */
	{
		printf("\nerror:fopen()");
		exit(3);
	}
	printf("\nTape Ripper\nLettore cassette del C64 - by Gianluca Ghettini");
	if (!(inportb(PORT)&0x20))
	{
		printf("\nPRESS <STOP>");
		while(!(inportb(PORT)&0x20));
	}
	delay(500);	/* attesa per lasciar esaurire il transitorio generato dal registratore */
	printf("\nPRESS <PLAY> ON TAPE");
	while(inportb(PORT)&0x20);
	printf("\nLOADING...");
	delay(1000);
	record(buffer);
	buildTAP(out,buffer);
	free(buffer);
	fclose(out);
	printf("\nOK");
	exit(0);
}
