/****************************************************************************
 *  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...
 *
 *
 *  Updated by Mike W. Smith, 12/5/89
 *  (No changes in the copyright either)
 *  Complete rewrite of C and ASM code,
 *  Now compiles in Turbo C.
 *  Goes back to using standard C I/O and handles Control-Breaks and other
 *   error conditions correctly,
 *  Allows the MPU's port address and IRQ level and the size of the buffer to
 *   be set with command line arguments,
 *  Now handles the interrupt controller properly on AT's and 386's,
 *
 ***************************************************************************/

#if defined(__TURBOC__)
#include <dir.h>        /* Turbo C */
#else
#include <direct.h>     /* Microsoft C/QuickC */
#endif

#include <conio.h>
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "midiex.h"


/* Function prototypes for this file    */
int     _Cdecl  init_mpu(void);
void    _Cdecl  instruct(void);
void    _Cdecl  listfiles(void);
void    _Cdecl  menu(void);
void    _Cdecl  receivefile(void);
void    _Cdecl  set_irq(int level);
void    _Cdecl  set_address(long address);
int     _Cdecl  set_buffer(int size);
void    _Cdecl  transmitfile (void);


/* Global data  */
int buffer_size;
int error;

/*--------------------------------------------------------------------------*/

/* Start of code */

void main(int argc,char **argv)
{
    char *stop_at;
       int i;

    clrscr();
    printf("MIDIEX patch exchange utility.\n");
    printf("Copyright (C) 1986 by Cantus Corportaion.\n");
    printf("Modifications (C) 1986 by Jim Bergsten.\n");
    printf("Version 1.2, updated 1986 by Michael Geary.\n");
    printf("Version 1.3, updated 1988 by David Hayes. \n");
    printf("Version 1.8, updated 1989 by Mike W. Smith. \n");
    printf("\nThis program allows you to send and receive MIDI System Exclusive\n" );
    printf("data dumps between your PC and a synthesizer or other MIDI device.\n" );

    /* Get command line arguments and set the IRQ level and MPU address */
    if(argc>1) {
        if(!strcmp(argv[1],"?")) {
            printf("\n\nTo set the size of the buffer, the IRQ level, and the MPU's address, use:\n");
            printf("\n    MIDIEX /B:nnnn /I:n /A:nnn \n");
            printf("\nwhere /B:nnnn is the size of the buffer,\n");
            printf("      /I:n is the IRQ level,\n");
            printf("      /A:nnn is the base address of the MPU-401\n");
            printf("      (use 0xnnn to indicate hexidecimal numbering).\n\n");
            printf("Defaults are /B:1024 /I:2 /A:0x330.\n");
            return;
        }
        for(i=1;i<=argc;i++) {
            if(!strnicmp(argv[i],"/B:",3)) if(!set_buffer(atoi(argv[i]+3))) {
                printf("\n\n        ERROR - not enough memory for buffer.\n\n");
                return;
            }
            if(!strnicmp(argv[i],"/I:",3)) set_irq(atoi(argv[i]+3));
            if(!strnicmp(argv[i],"/A:",3)) set_address(strtol(argv[2]+3,&stop_at,0));
        }
    }

    if(!buffer_ptr) if(!set_buffer(1024)) {
        printf("\n\n        ERROR - not enough memory for buffer.\n\n");
        return;
    }

    /* Set Ctrl-Break vector to avoid the possibility of exiting in the  */
    /* middle of the program without resetting the MPU vectors.          */
    set_ctrl_brk();

    /* Initialize the MPU   */
    if(!init_mpu()) {
        printf("\n\nThe MPU failed to initialize.  Turn off your computer to reset your MPU.\n\n" );
        free((void *)buffer_ptr);
        reset_ctrl_brk();
        return;
    }

    /* Display instructions */
    instruct();

    menu();

    /* All done.  Reset everything. */
    send_command(RESET);
    reset_mpu_vector();
    reset_ctrl_brk();
    free((void *)buffer_ptr);

    /* If no error forced the exit, then clear screen.  */
    if(!error) clrscr();
}

/*---------------------------------------------------------------------------*/

/* Initializes the MPU and sets it to UART mode                              */

int init_mpu(void)
{
    set_mpu_vector();
    /* Try reseting at least twice in case the MPU was in UART mode */
    if(!send_command(RESET)) if(!send_command(RESET)) {
        reset_mpu_vector();
        return(FAIL);
    }
    if(!send_command(0xAC)) {   /* Does it return a version number? */
        reset_mpu_vector();
        return(FAIL);
    }
    if(!send_command(UART)) {
        reset_mpu_vector();
        return(FAIL);
    }
    return(SUCCESS);
}

/*---------------------------------------------------------------------------*/

/* Display instructions for using MIDIEX                                     */

void instruct(void)
{
    printf("\nCommands are:\n" );
    printf("  T (path\\)filename    to Transmit a data dump to your synthesizer.\n");
    printf("  R (path\\)filename    to Receive a data dump from your synthesizer.\n");
    printf("  F (path\\)filespec    to list files in the directory.\n\n");
    printf("Press the Esc key at any time to exit.\n");
}

/*---------------------------------------------------------------------------*/

/* Lists the specified files on screen                                       */

void list_files(void)
{
    struct ffblk fileinfo;
    char filename[81];

    printf("\n\nFiles to display: ");
    scanf("%s",filename);
    if(!findfirst(filename,&fileinfo,0)) printf("\n%-16s",fileinfo.ff_name);
    else {
        printf("\n\nNo matching files.\n\n");
        return;
    }
    while(!findnext(&fileinfo)) printf("%-16s",fileinfo.ff_name);
    printf("\n");
}

/*---------------------------------------------------------------------------*/

/* Displays the menu for MIDIEX                                              */

void menu(void)
{
    int i=0;

    while(i!=ESC) {
        printf("\nCommand (T, R, F, Esc): " );

        switch(i=getch()) {

        case 'F':
        case 'f':
            list_files();
            break;

        case 'T':
        case 't':
            error=0;
            set_handler(no_op);
            transmitfile();
            break;

        case 'R':
        case 'r':
            error=0;
            receivefile();
            set_handler(no_op);
            break;

        case ESC:
            break;

        default:
            error=0;
            printf("\n");
            instruct();
            break;
        }
    }
}

/*----------------------------------------------------------------------------*/

/* Writes received SYSEX data to a file                                       */

void receivefile(void)
{
    FILE *fptr;
    struct ffblk fileinfo;
    char filename[81];
    int i;

start:
    printf("\n\nFilename to save to: ");
    scanf("%s",filename);
    if(!findfirst(filename,&fileinfo,0)) {
        printf("\nFile already exists.  Overwrite? ");
        while(1) {
            switch(getch()) {

            case 'Y':
            case 'y':
                printf("\n");
                goto open_file;

            case 'N':
            case 'n':
                goto start;

            default:
                continue;
            }
        }
    }

open_file:
    fptr=fopen(filename,"wb");
    printf("\nSend your SYSEX data now.  (Hit ESC to abort).");
    buffer_end=sysex_ended=0;
    set_handler(receive_sysex);
    while(!sysex_ended) {
        if(kbhit()) {
            i=getch();
            if(((char)i==ESC)||((char)i==3)) {
                printf("\n\nUser break.  Action aborted.\n\n");
                fclose(fptr);
                remove(filename);
                error=1;
                return;
            }
        }
    }
    set_handler(no_op);
    if(!buffer_end) {
        printf("\n\nError receiving data.  Action aborted.\n\n");
        fclose(fptr);
        remove(filename);
        error=1;
        return;
    }
    for(i=0;i<buffer_end;i++) fputc(*(buffer_ptr+i),fptr);
    printf("\n\nReceived %u bytes.  Writing to file %s.\n",buffer_end,filename);
    fclose(fptr);
}

/*----------------------------------------------------------------------------*/

/* Sets the data port and the command and status port addresses               */

void set_address(long address)
{
    if(((int)address>0x200)&&((int)address<0x400)) {
        mpu_data_port   =   (int)address;
        mpu_status_port =   (int)address+1;
    }
    else {
        mpu_data_port   =   0x330;
        mpu_status_port =   0x331;
    }
}

/*----------------------------------------------------------------------------*/

/* Sets the queue buffer for holding the incoming SYSEX data                  */

int set_buffer(int size)
{
    if(!size) size=1024;
    if((buffer_ptr=(char *)malloc(buffer_size=size))==NULL) return(FAIL);
    return(SUCCESS);
}

/*---------------------------------------------------------------------------*/

/* Sets the IRQ variables   */

void set_irq(int level)
{
    /* If an IRQ level of 2 is declared on an AT or 386 computer, then  */
    /* convert it to a 9.                                               */
    if(second_8259()) if(level==2) level=9;

    switch(level) {
    case 3:
        mpu_irq =   0xB;
        irq_mask=   8;
        eoi     =   0x63;
        break;

    case 4:
        mpu_irq =   0xC;
        irq_mask=   16;
        eoi     =   0x64;
        break;

    case 5:
        mpu_irq =   0xD;
        irq_mask=   32;
        eoi     =   0x65;
        break;

    case 6:
        mpu_irq =   0xE;
        irq_mask=   64;
        eoi     =   0x66;
        break;

    case 7:
        mpu_irq =   0xF;
        irq_mask=   128;
        eoi     =   0x67;
        break;

    case 9:
        mpu_irq =   0x71;
        irq_mask=   2;
        eoi     =   0x6162;
        break;

    default:
        mpu_irq =   0xA;
        irq_mask=   4;
        eoi     =   0x62;
    }
}

/*----------------------------------------------------------------------------*/

/* Sends a SYSEX file                                                         */

void transmitfile (void)
{
    FILE *fptr;
    char filename[81];
    int i,j=0;

    printf("\n\nName of file to transmit: ");
    scanf("%s",filename);
    if((fptr=fopen(filename,"rb"))==NULL) {
        printf("\n\nCouldn't find the file %s.\n\n",filename);
        fclose(fptr);
        error=1;
        return;
    }
    printf("\nHit any key to transmit.");
    i=getch();
    if((i==BRK)||(i==ESC)) {
        printf("\n\nTransmission aborted.\n");
        fclose(fptr);
        error=1;
        return;
    }
    while((i=fgetc(fptr))!=EOF) {
        if(!send_data(i)) {
            printf("\n\nError sending the file.  Transmission aborted.\n\n");
            fclose(fptr);
            error=1;
            return;
        }
        j++;
    }

    printf("\n\nTransmission complete, %u bytes sent.\n",j);
    fclose(fptr);
}
