/*
    Freq.C - frequency counter for AURA-equipped Amigas.
*/

#include <math.h>
#include <dos.h>

#include <exec/types.h>
#include <exec/memory.h>

#include <exec/execbase.h>
#include <hardware/cia.h>
#include <intuition/intuition.h>

#include <clib/exec_protos.h>
#include <clib/cia_protos.h>

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


#include "freq_gui.c"
#include "freq_gui_temp.c"


/* AURA DEFINES */

#define AURA_L 0xA20000
#define AURA_R 0xA20002

/* UWORD *Aura_L, *Aura_R; defined as internal on each effect */

#define AURA_READ_R() ((*Aura_R) ^ 0x8000)	/* AURA returns a left aligned 12 bit offset binary number */
#define AURA_READ_L() ((*Aura_L) ^ 0x8000)	/* invert MSB so offset binary turns to 2's complement */
#define AURA_WRITE_R(x) (*Aura_R)=(x)		/* AURA needs a 2's complement number to be written to */
#define AURA_WRITE_L(x) (*Aura_L)=(x)		/* an internal inverter on MSB converts to offset binary */

/* END OF AURA DEFINES */



#define STOPA_AND  CIACRAF_TODIN |CIACRAF_PBON | CIACRAF_OUTMODE | CIACRAF_SPMODE

        /*
        ;
        ; AND mask for use with control register A
        ; (interval timer A on either CIA)
        ;
        ; STOP -
        ;       START bit 0 == 0 (STOP IMMEDIATELY)
        ;       PBON  bit 1 == same
        ;       OUT   bit 2 == same
        ;       RUN   bit 3 == 0 (SET CONTINUOUS MODE)
        ;       LOAD  bit 4 == 0 (NO FORCE LOAD)
        ;       IN    bit 5 == 0 (COUNTS 02 PULSES)
        ;       SP    bit 6 == same
        ;       TODIN bit 7 == same (unused on ciacra)

        */


#define STARTA_OR  CIACRAF_START

        /*
        ;
        ; OR mask for use with control register A
        ; (interval timer A on either CIA)
        ;
        ; START -
        ;
        ;       START bit 0 == 1 (START TIMER)
        ;
        ;       All other bits unaffected.
        ;

        */



#define check_mouse()	{					\
			if(((ciaa->ciapra)&CIAF_GAMEPORT0)==0)  \
				break;                          \
			}




#ifndef TRUE
#define TRUE        1
#define FALSE       0
#endif


/* protos */

    
void init_timing(void);
void Measure(WORD *);


#define SAMPLERATE 40000

/* globals */
unsigned int SampleRate=SAMPLERATE;	/* sample rate */
UWORD TimerRate;		/* number to divide E clock for in order to get sample rate */
UWORD SpS;			/* REAL sample rate in samples per second, can be lower or higher */

int quit=0, doit=0;

struct CIA *ciaa= (struct CIA *)0xbfe001;

extern struct ExecBase *SysBase;


/*
                                main
*/

main()
{

    register i, j;
    register UWORD *Aura_L, *buf;

    
    UWORD *buffer;                    

    buffer=(UWORD *)halloc(2*SAMPLERATE*sizeof(UWORD));	/* we really need SpS words */

    if(!buffer)	
    	exit(1);
    	
    if(SetupScreen()) 
    	exit(1);
    if(OpenFreqWindow()) {
	CloseDownScreen();
	exit(1);
    }    

/* INIT AURA POINTERS */
	
	Aura_L= (UWORD *) AURA_L;
        

  while(!quit) {

            while(doit) {
		    /* init hardware */
		    init_timing();

		    j=SpS;
		    buf=buffer;

	    	    /* disable interrupts, go to it */
		    Disable();
		    ciaa->ciacra|=STARTA_OR;
		    i=0;

	       	    while((i++) < j) {

			    while(((ciaa->ciaicr)&CIAICRF_TA) == 0);       /* wait for timer */
			    ciaa->ciacra|=STARTA_OR;

	                    (*(buf++))=AURA_READ_L();
                    
			    check_mouse()
		    }

		    Enable();
		    check_mouse()
		    Measure((WORD *)buffer);
	    }

	    doit=0;
	    
	    Wait(1L << FreqWnd->UserPort->mp_SigBit);
    	    HandleFreqIDCMP();

  }
  CloseFreqWindow();
  CloseDownScreen();
}

        
/*
                                init_timing
    
    Sets up spec'd sample rate

*/

void init_timing(void)
{   
    TimerRate = (UWORD)( (SysBase->ex_EClockFrequency)/SampleRate);	/* get timer constant */
    SpS= (UWORD)( (SysBase->ex_EClockFrequency) / TimerRate);		/* get REAL sample rate */

    Disable();
    ciaa->ciacra&= STOPA_AND;
    ciaa->ciaicr|=CIAICRF_TA;	/* disable timer A interrupts */
    ciaa->ciatalo=(UBYTE)TimerRate&0xff;
    ciaa->ciatahi=(UBYTE)TimerRate>>8;
    Enable();
    
}

void Measure(WORD *buffer)		/* ported from a ADSP2181 zero-crossing counter... */
{

#define sgn(i)	(((i>0)? 1:0))


int  AF=0, AX0, AR, i, AY0 = 128;                       /* zero cross noise threshold */

	AX0 = *(buffer++);
	AR  = sgn(AX0);            
		
		
		for(i=0;i<SpS;i++) {

		   if(AR) 			/* if positive, sgn is 1 for positive, 0 for negative */
			goto last_was_pos;
last_was_neg:      AR  = AX0 - AY0;
		   AX0 = *(buffer++);
		   if (AR>=0) 
			AF = AF+1;
		   goto calc_zcr;
last_was_pos:      AR  = AX0 + AY0;
		   AX0 = *(buffer++);
		   if (AR<0)			
			AF = AF+1;
calc_zcr:          AR  = sgn(AR);
		}
	
       	GT_SetGadgetAttrs(FreqGadgets[GD_G],FreqWnd,NULL,GTNM_Number, AF/2,TAG_END);

}
