/****************************************************************************
 *  midiex.c   2/20/86	  Copyright (C) 1986 John Bailin, Cantus Corportion
 *
 *  Patch exchange software for the IBM-PC using MPU-401 in dumb mode.
 *
 *  Modified 08/24/86 Jim Bergsten to run under Microsoft "C"
 *  Changes copyright (C) 1986 Jim Bergsten
 *     "creat" call changed to "open" calls.
 *     Symbol "string" defined in routine "edit_space"
 *     Missing open bracket in first "for" stmt. in routine "edit_space"
 *     Some logic changed; some extensions, for example, created files
 *     are now only as long as the system exclusive data received.
 *     Error recovery added so operation can be retried without
 *     restarting the program.
 *     Other minor editorial changes...
 *
 *  Updated by Michael Geary, 11/14/86
 *  These changes are not copyrighted!
 *  (How many silly copyrights do you want to see on this thing?)
 *	Fixed bugs:
 *	    Files were not closed on error.
 *	    You could ^C out and leave the IRQ2 (INT 0Ah) vector hooked -
 *	       now forces BREAK OFF and does all console I/O through BIOS.
 *	    Interrupt routine in .ASM file failed to chain to previous
 *		interrupt handler when it wasn't ours.
 *	Function templates and type checking added.
 *	Streamlined the user interface
 *	More and more editorial changes...
 *
 ***************************************************************************/

/*
 *  The external data and code declarations are all publics in the 8086
 *  assembler module MIDIINT.ASM.  This program was compiled with the Microsoft
 *  C compiler.
 */

/* Force library argument checking */
#define LINT_ARGS

#include <ctype.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <dos.h>
#include <process.h>

/* Don't include stdio.h - so inadvertant printf's will be caught */
int cdecl sprintf( char *, char *, ... );

typedef unsigned char	BYTE;
typedef unsigned int	WORD;
typedef unsigned long	DWORD;
typedef int		BOOL;

#define TRUE		1
#define FALSE		0

#define BUFMAX		8192
#define CR		13
#define ESC		27
#define LINE		80

/*
 * MIDI equates
 */

#define SYSEX		0xF0
#define EOX		0xF7


/*
 * MPU-401 equates
 */

#define UART		0x3F
#define RESET		0xFF

/*
 * global declarations
 */

extern int recv_data_count;
extern BYTE recv_buf[ ];
extern int  recv_buf_ptr;

extern BYTE wait_char;

BYTE dump_buf[BUFMAX];
BOOL breakstate;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*
 * Functions in this C file
 */

void check_keys( void );
BOOL file_exists( char * );
BOOL get_filename( char *, char * );
char *getstring( char * );
void getword( char * *, char * );
BOOL initialize( void );
void instruct( void );
void main( void );
void menu( void );
void print( char * );
void quit( int );
int  read_dump( char * );
int  receive_dump( void );
BOOL send_dump( int );
BOOL setbreak( BOOL );
BOOL write_dump( char *, int );

/*
 * Functions in MIDIINT.ASM
 */

WORD fetch_mpu_data( void );
int  get_char( int );
void int10( WORD, WORD, WORD, WORD );
void reset_mpu_vector( void );
BOOL set_mpu_vector( void );
BOOL write_mpu_command( int );
BOOL write_mpu_data( int );

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void main()
{
    print( "\n\nMIDIEX patch exchange utility.\n" );
    print( "Copyright (C) 1986 by Cantus Corportaion.\n" );
    print( "Modifications (C) 1986 by Jim Bergsten.\n" );
    print( "Version 1.2, updated by Michael Geary.\n" );
    if( ! initialize() ) {
	print( "\nYour MPU isn't responding within MIDIEX's timeout period.\n" );
	print( "Try rebooting your system and starting over.\n" );
	quit( 1 );
    }
    print( "\nThis program allows you to send and receive MIDI System Exclusive\n" );
    print( "data dumps between your PC and a synthesizer or other MIDI device.\n" );
    instruct();

    while( TRUE ) menu();

    quit( 0 );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL file_exists( name )
    char *name;
{
    register int f;

    if( ( f = open( name, (int)(O_RDONLY|O_BINARY) ) ) < 0 )  return FALSE;

    close( f );
    return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL get_filename( prompt, name )
    char *prompt, *name;
{
    char response[LINE];
    char *resp;

    if( name[0] )  return TRUE;

    print( "\nEnter filename to " );
    print( prompt );
    print( ": " );
    resp = getstring( response );
    getword( &resp, name );

    return name[0] != 0;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL initialize()
{
    breakstate = setbreak( 0 );
    if( ! set_mpu_vector() )  return FALSE;
    write_mpu_command( RESET );
    write_mpu_command( UART );
    return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void instruct()
{
    print( "\nCommands are:\n" );
    print( "  S pathname    to Send a data dump to your synthesizer.\n" );
    print( "  R pathname    to Receive a data dump from your synthesizer.\n\n" );
    print( "Press the Esc key at any time to exit.\n" );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void menu()
{
    char response[LINE];
    char *resp;
    char name[LINE];
    int  size;

prompt:
    print( "\nCommand (S, R, Esc): " );
    resp = getstring( response );
    getword( &resp, name );

    switch( name[0] ) {

	case 'S':
	    getword( &resp, name );
	    if( ! get_filename( "send", name ) )  break;
	    if( size = read_dump( name ) )  send_dump( size );
	    print( "\n" );
	    break;

	case 'R':
	    getword( &resp, name );
	    if( ! get_filename( "receive", name ) )  break;
	    if( file_exists( name ) ) {
		print( "File already exists, OK to overwrite (Y/N)? " );
		getstring( response );
		if( response[0] != 'Y' )  break;
	    }
	    if( size = receive_dump() )  write_dump( name, size );
	    print( "\n" );
	    break;

	default:
	    instruct();
	    goto prompt;
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void quit( exitvalue )
    int exitvalue;
{
    print( "\n\n" );
    write_mpu_command( RESET );
    reset_mpu_vector();
    setbreak( breakstate );
    _exit( exitvalue );
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

int read_dump( name )
    char *name;
{
    register int f, size;

    if( ( f = open( name, (int)(O_RDONLY|O_BINARY) ) ) < 0 ) {
	print( "\nUnable to open input file " );
	print( name );
	return FALSE;
    }

    if( ( size = read( f, dump_buf, BUFMAX ) ) <= 0 ) {
	close( f );
	print( "\nTrouble reading input file " );
	print( name );
	return FALSE;
    }

    close( f );
    return size;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

int receive_dump()
{
    register int i, j, k;
    char printbuf[40];

    /* Set character to wait for */
    wait_char = EOX;

    print( "\nSend the MIDI data now...\n" );
    while( wait_char )	check_keys();

/*  The following dumps the received data in hexadecimal.
    remove the comments around it if you want this display. */

/*  print( "MIDI data received...\n" );
    j = 0;
    for( i = 0;  i < recv_buf_ptr;  ++i ) {
	sprintf( printbuf, " %2.2X", recv_buf[i] );
	print( printbuf );
	if( j++ == 15 ) {
	    print( "\n" );
	    j = 0;
	 }
    }
    if( j )  print( "\n" );
*/

    /* search backwards for start of dump */
    i = recv_buf_ptr;

    for( k = 1;  k <= BUFMAX;  k++ ) {
	if( --i < 0 )  i = BUFMAX;
	if ( recv_buf[i] == SYSEX )  break ;
    }

    if( k >= BUFMAX ) {
	print( "Start of MIDI dump not found\n" );
	return 0;
    }

    sprintf( printbuf, "Size of dump is %d\n", k );
    print( printbuf );

    for ( j = 0;  j < k;  j++ ) {
	if( i == BUFMAX )  i = 0;
	dump_buf[ j ] = recv_buf[ i++ ];
    }

    return k;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL send_dump( size )
    int size;
{
    register int i;

    print( "\nStrike any key when ready to send MIDI data...\n" );
    get_char( 0 );
    print( "\nSending MIDI data...\n" );
    for (i =  0; i <= size; i++) {
	if( ! write_mpu_data( dump_buf[i] ) ) {
	    print( "MIDI interface timed out\n" );
	    return FALSE;
	}
	check_keys();
	if( dump_buf[i] == EOX )  break;
    }
    print( "\nDone!" );
    return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL setbreak( newstate )
    BOOL newstate;
/*
 *  Sets DOS Ctrl-Break checking to newstate and returns previous state.
 */
{
    BOOL oldstate;
    union REGS regs;

    regs.x.ax = 0x3300;
    intdos( &regs, &regs );
    oldstate = (BOOL)regs.h.dl;

    regs.x.ax = 0x3301;
    regs.h.dl = (unsigned char)newstate;
    intdos( &regs, &regs );

    return oldstate;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL write_dump( name, size )
    register char *name;
    int size;
{
    register int f;

    if( ( f = open( name, (int)(O_CREAT|O_TRUNC|O_WRONLY|O_BINARY), S_IWRITE ) ) < 0 ) {
	print( "\nUnable to open output file " );
	print( name );
	return FALSE;
    }

    if( write( f, dump_buf, size ) != size ) {
	close( f );
	print( "\nTrouble writing to output file " );
	print( name );
	return FALSE;
    }

    close( f );
    print( "\nDone!" );
    return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*
 *
 * lower level editing routines
 *
 */

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void check_keys()
{
    register char c;

    if( get_char( 1 ) == 1 ) {
	c = (char)( get_char( 0 ) & 0xFF );
	if( c == ESC )	quit( 0 );
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

char *getstring( string )
    char *string;
{
    register int x = 0;
    register char c;

    while( TRUE ) {
	c = (char)( get_char( 0 ) & 0xFF );
	if( c == ESC )	quit( 0 );
	if( c == CR ) {
	    print( "\n" );
	    string[x] = '\0';
	    return string;
	}
	if( ( c >= 32 && c < 127 ) || c == CR ) {
	    string[ x++ ] = toupper( c );
	    int10( 0x0E00 | c, 0, 0, 0 );
	} else if( c == 8  &&  x > 0 ) {
	    int10( 0x0E00 | c, 0, 0, 0 );
	    int10( 0x0A00 | ' ', 7, 1, 0 );
	    --x;
	}
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void getword( strpp, word )
    char **strpp;
    char *word;
{
    char *wordp;

    wordp = word;
    while( isspace( **strpp ) ) (*strpp)++;
    while( **strpp && ! isspace( **strpp ) )  *wordp++ = *(*strpp)++;
    *wordp = 0;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

void print( string )
    register char *string;
{
    register char c;

    while( c = *string++ ) {
	if( c == '\n' )  int10( 0x0E00 | '\r', 0, 0, 0 );
	int10( 0x0E00 | c, 0, 0, 0 );
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
