/*
 *	poly_play - Play a polyphonic tune.
 */
#include <stdio.h>

#define TRUE	(1)
#define FALSE	(0)

int tune[5000];
char name[80];
FILE *tfp;

int *tunep;
int tempo;
int duration;

main( argc, argv )
int argc;
char *argv[];
{
	int tindex;

	if( argc != 2 )
	{
		tfprintf( stderr, "\nUsage: poly_play tunefile\n\n");
		exit( 1 );
	}

	if( !(tfp = fopen(argv[1], "r")) )
	{
		tfprintf( stderr, "\n*** Unable to open tune file ***\n" );
		exit( 2 );
	}

	fgets( name, 80, tfp );
	tindex = 0;

	while( tfscanf(tfp, "%d", &tune[tindex++]) != -1)
		;

	fclose( tfp );

	tunep = tune;						/* Initialize tune pointer */
	tempo = 0x1fff;						/* Set up default tempo */
/*
 *  Now attempt to play the tune...
 */
	asm( "push ax" );					/* Save registers */
	asm( "push bx" );
	asm( "push cx" );
	asm( "push dx" );
	asm( "push si" );
	asm( "push di" );
	asm( "push bp" );
	asm( "push es" );
	asm( "push <tempo>" );				/* Initialize tempo counter */
	asm( "xor ax,ax" );					/* Must write 0 into voice count and */
	asm( "mov bx,ax" );					/*  data registers */
	asm( "mov dx,ax" );
	asm( "mov si,ax" );
	asm( "mov bp,ax" );
	asm( "mov di,ax" );
	asm( "mov es,ax" );
	asm( "mov cx,#12h" );				/* Initialize shift value */
/*
 *  This section sorts the tune data into the appropriate registers
 *  Voice period data is stored in ES for voice 1, DI for voice 2,
 *  and SI for voice 3.  The voice period count is stored in BX for
 *  voice 1, DX for voice 2, and BP for voice 3.
 */
	asm( "sort:" );
		asm( "push bx" );
		asm( "push dx" );
	asm( "sort_1:" );
		asm( "add <tunep>,#2" );
		asm( "mov bx,<tunep>" );		/* Get tune pointer */
		asm( "mov ax,[bx]" );			/* Get tune data in AX */
		asm( "mov dx,ax" );				/* Make a copy in DX */
		asm( "and dx,#1FFFh" );			/* Strip off 3 data type bits */
		asm( "shl ax" );				/* Move msb into carry bit */
		asm( "jnc sort_4" );			/* Jump if end, duration, or tempo data */
		asm( "shl ax" );				/* Move 2nd msb into carry bit */
		asm( "jc sort_3" );				/* Jump if voice 3 data */
		asm( "shl ax" );				/* Move 3rd msb into carry bit */
		asm( "jc sort_2" );				/* Jump if voice 2 data */
		asm( "mov es,dx" );				/* Store voice 1 data in ES */
		asm( "jmp sort_1" );
	asm( "sort_2:" );
		asm( "mov di,dx" );				/* Store voice 2 data in DI */
		asm( "jmp sort_1" );
	asm( "sort_3:" );
		asm( "mov si,dx" );				/* Store voice 3 data in SI */
		asm( "jmp sort_1" );			/* Ignore */
	asm( "sort_4:" );
		asm( "shl ax" );				/* Move 2nd msb into carry bit */
		asm( "jnc sort_6" );			/* Jump if end or duration data */
		asm( "shl ax" );
		asm( "jnc sort_5" );			/* Jump if tempo data */
		asm( "jmp sort_1" );			/* Ignore */
	asm( "sort_5:" );
		asm( "mov <tempo>,dx" );		/* Store tempo */
		asm( "pop dx" );				/* Restore registers */
		asm( "pop bx" );
		asm( "pop ax" );				/* Burn the current tempo counter */
		asm( "push <tempo>" );			/*  and put in the new one */
		asm( "push bx" );				/* Save registers again */
		asm( "push dx" );
		asm( "jmp sort_1" );
	asm( "sort_6:" );
		asm( "shl ax" );				/* Move 3rd msb into carry */
		asm( "jc sort_7" );				/* Jump if duration */
		asm( "pop dx" );
		asm( "pop bx" );
		asm( "pop ax" );				/* Get rid or tempo counter */
		asm( "jmp theend" );
	asm( "sort_7:" );
		asm( "mov <duration>,dx" );		/* Store duration data */
		asm( "pop dx" );
		asm( "pop bx" );
	asm( "theloop:" );
		asm( "cli" );					/* Turn off interrupts during note */
		asm( "pop ax" );				/* Get tempo counter */
		asm( "dec ax" );				/* Decrement it */
		asm( "push ax" );				/* Put it back */
		asm( "jnz play" );				/* Jump if some tempo remains */
		asm( "pop ax" );				/* Get tempo counter */
		asm( "mov ax,<tempo>" );		/* Reset tempo counter */
		asm( "push ax" );				/*  and put it back */
		asm( "dec <duration>" );		/* Decrement duration counter */
		asm( "jnz play" );				/* Continue if not finished */
		asm( "sti" );					/* Turn interrupts back on */
		asm( "jmp sort" );				/* Get new tune data if duration up */
/*
 *  This routine plays the notes until the duration counter
 *  reaches zero.  At that time, the new data is sorted.
 */
	asm( "play:" );
		asm( "add bx,si" );				/* Add voice 3 data to count */
		asm( "rol bx" );				/* Get msb of voice 3 count */
		asm( "mov ax,cx" );				/* Keeps keyboard clk on and */
		asm( "rcl al" );				/*  casette motor relay off */
		asm( "rcl al" );
		asm( "b out 061h" );
		asm( "ror bx" );				/* Restore bx to original state */
		asm( "add dx,di" );				/* Add voice 2 data to count */
		asm( "rol dx" );
		asm( "mov ax,cx" );
		asm( "rcl al" );
		asm( "rcl al" );
		asm( "b out 061h" );
		asm( "ror dx" );
		asm( "mov ax,es" );
		asm( "add bp,ax" );				/* Add voice 1 data to count */
		asm( "rol bp" );
		asm( "mov ax,cx" );
		asm( "rcl al" );
		asm( "rcl al" );
		asm( "b out 061h" );
		asm( "ror bp" );
		asm( "jmp theloop" );
	asm( "theend:" );
		asm( "pop es" );				/* Restore registers */
		asm( "pop bp" );
		asm( "pop di" );
		asm( "pop si" );
		asm( "pop dx" );
		asm( "pop cx" );
		asm( "pop bx" );
		asm( "pop ax" );
}
