/* MIDInterface 1.11
     Copyright (C) 1985, 1986   By:
			Jay Kubicky
			934 N. Orange St.
			Media, PA 19063
			215-565-7761

   This is a VERY recent version of this program.
   It has not been 'tested' extensively, though all testing that
   has been done shows favorable results.  
   All users are free, of course, to test (and debug) this software as
   desired.

   This program was developed under the DeSmet C compiler, and utilizes
   DeSmet's vastly useful in-line assembly language capability.
   Compiling it on other compilers (such as Lattice) may require 
   EXTENSIVE modification.

   2/14/86
*/

#define MIDID 0xffa0
#define MIDIS 0xffa2
#define PIC0  0x20
#define PIC1  0x21
#define CSTAT 0xFFA7
#define COUNTER1 0xFFA4
#define COUNTER2 0xFFA5
#define Tsize 25600	/* track size in bytes (24K) */
#define Tk 25		/* track size in K */
#define seg_per_trk 1600	/* track size in paragraphs */
/* the following structures hold data for each buffer */

char prog_id[]={"MIDInterface 1.11 (c) 1985, 1986  Jay Kubicky"};

struct rec      {     /* 63 bytes long    63*16=1008 bytes total */
        char chan;
        unsigned ssize;
        char name[40];
	char transpose;	/* transpose amount */
        char extra[19];  /* for later use */
                } parts[16];

char in_filt;		/* This is the MIDI input filter mask:
				bit:	message filtered out:
				---	--------------------
				 0	Note ON
				 1	Note OFF
				 2	Program Change
				 3	Channel Pres. (After-touch)
				 4	Pitch Wheel
				 5	Control Change
				 6	Poly. Key pres. (After-touch)
			*/
char on[4]="ON ";	/* 'ON' */
char off[4]="OFF";	/* 'OFF' */
char yes[4]="YES";
char no[4]="NO ";
char sin_line[80];	/* A 79 char. single line */
unsigned r_segment;
char *ptr, *end;
int fd;  char filename[30];

int r_tempo;		/* tempo val (bpm) */
unsigned tempo;		/* real tempo val. */
char destbyte, stop;
char clsb, cmsb, dsync, MIDIsync, audmet;
char rbuff;             /* receive buffer */
char beat, mbeat,abeat, s_t_b, m_t_b, a_t_b; /* beats & time bases */

char buffon[16];        /* true means given buff is active */
unsigned pointers[16];  /* an array of pointers into present buffers */
unsigned startp[16];    /* an array of starts of present buffer */
unsigned endp[16];      /* pointers to end of songs */
unsigned segment[16];   /* array of segments of tracks */

int pfd;                /* fd for printer */
char printer;           /* printer enable */

extern unsigned _rax, _rbx, _rdx, _res, _rds;

char LAST_STAT;
char C_O_F;
char clk_type;		/* 0=internal,  1=external */
char counter_dec;
char vir_buff[1024];
char buff1[Tsize];	/* track 0 - 3070 notes */
char buff2[Tsize];	/* track 1 - 3070 notes */
	/* first two tracks eat up  50K */
	/* last fourteen will take 350K combined */
	/* total note storage=
			3200 notes per track
		       51200 notes in all!!!     */

char *_showsp(),*_showss(),*_showds();
main()
{
	unsigned a;
        char clkval,sel,b;    int c;
        unsigned base_seg, cont_mem;

    /* the following code stores the DS at 0x4FA  */
    /*     THIS IS VERY IMPORTANT!!!!!!!!         */
    /* this allows the DS to be transffered to the RXINT function */
# asm
   MOV CX,DS  ; store DS in CX
   PUSH DS    ; save DS
   MOV AX,0   ; this will be new value for DS
   MOV DS,AX  ; put 0 in DS
   MOV WORD [04FAH], CX   ; store DS
   POP DS     ; retrieve DS
#

        /*       MIDInterface 1.11 16-track digital sequencer

                         Data storage format:
                         --------------------

                        (MIDI ORIENTED COMMANDS)
   byte num:     first      second     third      fourth     fifth
                +--------+----------+----------+----------+--------+
   Note ON      00vvvvvv | LLLLLLLL | MMMMMMMM | 1nnnnnnn |   --
                         |          |          |          |
   Note OFF     00vvvvvv | LLLLLLLL | MMMMMMMM | 0nnnnnnn |   --
                         |          |          |          |
   Prog Chan    01000000 | LLLLLLLL | MMMMMMMM | 0ppppppp |   --
                         |          |          |          |
   Chan Pres    01000001 | LLLLLLLL | MMMMMMMM | 0aaaaaaa |   --
   (after-touch)         |          |          |          |
   Pitch Wheel  10wwwwww | LLLLLLLL | MMMMMMMM |    --    |   --
                         |          |          |          |
   Cntrl Chan   11000000 | LLLLLLLL | MMMMMMMM | 0nnnnnnn | 0ccccccc
			 |	    |	       |	  |
   Key Pres.    011vvvvv | LLLLLLLL | MMMMMMMM | 0nnnnnnn |   --
   (after-touch)

                        (INTERNAL ORIENTED COMMANDS)
                +--------+----------+----------+----------+--------+
   Change Tempo 11000001 | LLLLLLLL | MMMMMMMM | tttttttt |   --

   End          11111111 |    --    |    --    |    --    |   --


-------------------------------------------------------------------------
        vvvvvv  - top 6 bits of velocity (NOTE ON or OFF)
      LLLLLLLL  - LSB of clk
      MMMMMMMM  - MSB of clk
       nnnnnnn  - note for either NOTE ON or NOTE OFF
       ppppppp  - new program
       aaaaaaa  - chan pressure (after-touch)
        wwwwww  - top six of bender
       nnnnnnn  - control number
       ccccccc  - control value

      tttttttt  - new tempo value (40-200)
*/
	c=0;
	while(c<79)
		sin_line[c++]='Ä';
	sin_line[c]='\0';

	in_filt=0xff;	/* Don't filter out anything */
	clk_type=0;	/* internal */
	counter_dec=2;
        rbuff=255;
	segment[0]=segment[1]=_showds();	/* 1st two buffs in DS */
	startp[0]=buff1;	endp[0]=buff1+Tsize-8;
	startp[1]=buff2;	endp[1]=buff2+Tsize-8;
	parts[0].ssize=0;	parts[1].ssize=0;

	/* This is the memory allocation section.
	   This is really a sort of pseudo-allocation scheme
	   allowing the use of the entire system memory (up to 640K).
	   Memory is allocated in blocks of 25k (1600 paragraphs).
	   All tracks are allocated sequentially until you run out
	   of system RAM.  The first two tracks are taken care of
	   in the C memory, so there should always be room for these.
	*/

        base_seg=_showss()+1;	/* start just after stack */
	base_seg += (_showsp()/16);	/* This is the segment boundary of
					   the bottom of un-initialized mem */
	++base_seg;	/* one for a safety */

	cont_mem=get_av_mem();
	cont_mem *= 64;	/* Find total # of system para. */

	/* Here's where we "allocate our memory" */

	_setmem(segment+4,28,0);
        c=2;
        for (a=base_seg; a < cont_mem && c < 16; a+=seg_per_trk)     {
		segment[c]=a;
		startp[c]=0;
		endp[c]=Tsize-8;
		parts[c].ssize=0;
		parts[c].transpose=0;
		++c;
        }
        m_t_b=2;  s_t_b=1;  a_t_b=48;   /* default time bases */
        printer=0;
        scr_cla();
        printf("Do you wish to use the printer as an audio meternome?");
        if(toupper(getchar())=='Y')     {
                printf("\nReady the printer and hit any key");
                getchar();
                if((pfd=open("PRN",2))==-1)     {
                        printf("Error opening printer ... aborting\n");
                        getchar();
                        exit(1);
                                                }
                beepp();
                printer=1;
                                        }
start:
        setdart();
        intoff();   /* turn interrupts off */
        scr_cla();
printf("             ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»\n");
printf("             º  MIDInterface 1.11  16 Track Digital Recorder  º\n");
printf("             ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼\n");
printf("\n");
printf("                               Main Menu:\n");
printf("                               ÄÄÄÄÄÄÄÄÄÄ\n");
printf("                       1..........Erase a track\n");
printf("                       2..........Record to a track\n");
printf("                       3..........Play from a track\n");
printf("                       4..........Track information\n");
printf("                       5..........Save a track\n");
printf("                       6..........Load a track\n");
printf("                       7..........Set MIDI modes\n");
printf("                       8..........Edit input filter\n");
printf("                       0..........Quit\n");
	scr_rowcol(24,0);
printf("                  Copyright (C) 1985, 1986 by Jay Kubicky");
	scr_rowcol(18,0);
printf("                             Enter Selection:");
        sel=getchar();
        switch (sel)    {
                case '1':
                        erbuff();
                        break;
                case '2':
                        recbuff();
                        break;
                case '3':
                        pbuffs();
                        break;
                case '4':
                        dbuffs();
                        break;
                case '5':
                        strack();
                        break;
                case '6':
                        ltrack();
                       break;
                case '7':
                        mmode();
                        break;
		case '8':
			ed_in_filt();
			break;
                case '0':
                        scr_cla();
                        b=0;
                        for(a=0; a<16; ++a)     {
                                if(parts[a].ssize)      {
                                        b=1; /* toggle high */
                                        printf("   Data still left in track %d\n",a);
                                                        }
                                                }
                        if(!b)                          {
                                close(pfd);
                                exit(0);
                                                        }
                        printf("Still want to exit? ");
                        if(toupper(getchar()) == 'Y')   {
                                close(pfd);
                                exit(0);
                                                        }
                default:
                        goto start;
                        }
        goto start;
}

/*	DBUFFS
	This is the general buffer display routine.
	It displays all pertinent data for all avaiable buffers.
*/
dbuffs()
{
        char a,b; unsigned tn,nf,nu;
s_:     scr_cla();
        printf("                   Track Assignment Screen:\n");
        printf("                   ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\n");
        printf("Track Num:   Transmit Chan:    Notes Used:   Transpose:      Enabeled:\n");
        printf("%s\n",sin_line);
        for(a=0; a<16; ++a)   {
		if(segment[a])	{
	                tn=(endp[a]-startp[a])/8;
	                nf=tn-(parts[a].ssize/8);
	                nu=tn-nf;
			b=parts[a].transpose;
	                printf("%d\t\t%d\t\t%d\t\t%d\t\t%s\n",a,parts[a].chan,nu,b < 128 ? b : -256+b, buffon[a] || (a==rbuff) ? yes : no);
		}
		else
			printf("--\t\t--\t\t--\t\t--\t\t--\n");
        }
        printf("%s\n",sin_line);
ttag:	scr_rowcol(21,0);
        printf("Chng one trans. channel (1), change all (2), transpose (3), enable status (4)\n");
	printf("or (0) to quit/continue: ");
        a=getchar();
        scr_rowcol(22,17);
        scr_cls();  /* clear rest of current line */
        switch(a)	{
		case '0':
	                return;
			break;
		case '1':
	                printf("   Enter track number:");
	                b=getint();
	                scr_rowcol(23,0);
	                printf("   Enter new transmit channel:");
	                parts[b].chan=getint();
        		break;
		case '2':
        	        printf("   Enter new transmit channel for all tracks:");
        	        a=getint();
        	        for(b=0; b<16; ++b)
        	                parts[b].chan=a;
			break;
		case '3':
			printf("   Enter track number:");
	                b=getint();
	                scr_rowcol(23,0);
	                printf("   Enter new transpose amount:");
	                parts[b].transpose=getint();
        		break;
		case '4':
			printf("   Enter track number:");
			b=getint();
			if(parts[b].ssize && b != rbuff)
				buffon[b]= buffon[b] ? 0 : 1;
			break;
		default:
			goto ttag;
	}
        goto s_;
}

/*	ERBUFF
	This funct. erase a buffer (set the size to 0)
*/
erbuff()
{
        scr_cla();
        char b;
	printf("                          Erase a Track\n");
	printf("%s\n",sin_line);
        printf("\nWhich buffer? ");
        b=getint();
        printf("Are you sure you want to erase buffer %d?",b);
        if(toupper(getchar()) != 'Y')
                return;
        parts[b].ssize=0;
        buffon[b]=0;
}

/*	PBUFFS
	This is the top-level play-buffer routine.
*/
pbuffs()
{
        int m;
        copyptrs();

        scr_cla();
	printf("                           Play Track Mode\n");
        printf("%s\n\n\n",sin_line);
	printf("   Hit any key to continue\n");
        getchar();
        dbuffs();

        scr_cla();
	printf("                           Play Track Mode\n");
        printf("%s\n\n\n",sin_line);

	get_options();

        printf("\nHit space bar to continue, any other key to quit\n");
        if(getchar()!=32)
                return;
        setrec();
        destbyte=0xff;
        play();
        intoff();
        _outb(5,MIDIS);    /* send off to drum mach. */
        _outb(104,MIDIS);
        _outb(0xfc,MIDID);  /* MIDI seq off */
        for(m=0; m<16; ++m)     {
                if(parts[m].ssize)
                        buffon[m]=1;
                                }
}

/*
	RECBUFF:
	This is the track record routine.
	All it really does is set up & enable the interrupt routine -
	RxInt
*/
recbuff()
{
        char b;  int m;
        copyptrs();
        scr_cla();
	printf("                        Record a Track\n");
        printf("%s\n\n",sin_line);
	printf("        Which buffer:");
        b=getint();
        if(parts[b].ssize)      {
                printf("That buffer already has music, still want to record (Y/N)?");
                if(toupper(getchar()) != 'Y')
                        return;
                                }
	if(!segment[b])	{
		printf("Sorry, that track is not available to record in.\n");
		hak();
		return;
	}
        rbuff=b;
	parts[b].ssize=0;
        dbuffs();  /* display buff enable board w/ option to change */
        scr_cla();

	get_options();

        printf("\nHit any key to begin recording\n");
        getchar();
        scr_cla();
        printf("Hit any key to stop recording\n");

        setrec();
        play();

        _poke(255,ptr,segment[rbuff]);    /* EOS (end-of-song) */
        parts[b].ssize=ptr-startp[rbuff];
        printf("\nSong is %d notes long\n",parts[b].ssize / 8);
        rbuff=255;              /* no more record buffer */
        pointers[b]=startp[b];  /* copy start pointer */
        buffon[b]=1;            /* enable present buffer */

        _outb(5,MIDIS);    /* send off to drum mach. */
        _outb(104,MIDIS);
        _outb(0xfc,MIDID);  /* MIDI seq off */

	rbuff=0xff;
	hak();
}

/*	SETREC
	This sets up almost everything for Play & Record.
*/
setrec()
{
	int a;

        stop=0;
        destbyte=0;
        setint();
        ptr=startp[rbuff];
	end=endp[rbuff];
	r_segment=segment[rbuff];
        end=endp[rbuff];
        C_O_F=0x90;
        beat=abeat=mbeat=0xfe;
        if(dsync)      {
                _outb(5,MIDIS);  /* send start to drums */
                _outb(104+128,MIDIS);
        }
        if(MIDIsync)
                _outb(0xfa,MIDID);      /* start ext seq */
	pointers[0]=startp[0];
	pointers[1]=startp[1];
        setcounter(tempo & 0xff,tempo >> 8,255,255);
}

copyptrs()  /* copy start pstns of buffers into pointer areas  */
{
        char a;
        for(a=0; a<15; ++a)
                pointers[a]=startp[a];
}

/*	LTRACK
	Load a track into memory.
*/
ltrack()
{
        char tr;
        scr_cla();
	printf("                        Load a Track\n");
        printf("%s\n",sin_line);
	dir("????????","???");
	scr_rowcol(2,0);
	printf("   Load which track:");
        tr=getint();
        if(parts[tr].ssize)             {
                printf("Data already in that track ... aborting.\n");
                getchar();  return;     }
        printf("  Load from which file:");
        scanf("%s",filename);
        if((fd=open(filename,2)) == -1)  {
                printf("Can't open %s ... aborting\n",filename);
                getchar();
                return;
                                        }
        if(read(fd,parts[tr],63) == -1)        {
                printf("Error encountered during reading.\n");
                getchar();
                return;
                                                }
        if(seg_read(fd,tr,parts[tr].ssize+1) == -1)  {
                printf("Error encountered while reading.\n");
                getchar();  return;                     }
        close(fd);
        printf("Total bytes loaded from disk:%d\n",parts[tr].ssize+63);
        buffon[tr]=1;
        getchar();
}

/*	STRACK
	Save a track to disk.
*/
strack()  /* save a track */
{
        char tr;
        scr_cla();
	printf("                        Save a Track\n");
        printf("%s\n",sin_line);
	dir("????????","???");
	scr_rowcol(2,0);
	printf("   Save which track:");
	tr=getint();
        if(!parts[tr].ssize)             {
                printf("Nothing in that track ... aborting.\n");
                getchar();  return;     }
        printf("  Save to what file:");
        scanf("%s",filename);
        if((fd=creat(filename)) == -1)  {
                printf("Can't creat %s ... aborting\n",filename);
                getchar();
                return;
                                        }
        if(write(fd,parts[tr],63) == -1)        {
                printf("Error encountered during writing.\n");
                getchar();
                goto _end;
                                                }
        if(seg_write(fd,tr,parts[tr].ssize+1) == -1)  {
                printf("Error encountered while writing.\n");
                getchar();   goto _end;                 }
        close(fd);
        printf("Total bytes written to disk:%d\n",parts[tr].ssize+63);
        hak();     
        return;
_end:
        close(fd);
}

/*	SEG_READ, SEG_WRITE
	These functions carry out intra-segmentary disk I/O functions.
*/
seg_read(fd,tr,size)
int fd, tr;  unsigned size;
{
        unsigned a;
	a=startp[tr];	/* this won't be 0 for tr. 0 & 1 : it's a pointer */

        if(size > 1024) {
		size+=a;	/* make size look like a pointer */
                for(;a < (size-1024); a+=1024)      {
                        if(read(fd,vir_buff,1024) == -1)
                                return(-1);
                        _lmove(1024,vir_buff,_showds(),a,segment[tr]);
                }
        }
        else
                size+=a;

        if(!(size-a))
                return(1);
        if(read(fd,vir_buff,size-a) == -1)
                return(-1);
        _lmove(size-a,vir_buff,_showds(),a,segment[tr]);
        return(1);
}

seg_write(fd,tr,size)
int fd, tr;  unsigned size;
{
	unsigned a;
	a=startp[tr];	/* this won't always be 0 because of tracks 0 & 1 */

	if(size > 1024) {
		size+=a;	/* make size look like a pointer */
		for(;a < (size-1024); a+=1024)      {
			_lmove(1024,a,segment[tr],vir_buff,_showds());
			if(write(fd,vir_buff,1024) == -1)
				return(-1);
		}
	}
	else
                size+=a;

        if(!(size-a))	/* Is the buff length 0 || have we written all data? */
                return(1);
        _lmove(size-a,a,segment[tr],vir_buff,_showds());
        if(write(fd,vir_buff,size-a) == -1)
                return(-1);
        return(1);
}

/* play() -
	This function plays through the 16 tracks until track 0 ends
	or a key is struck.
*/
play()
{
	int playing;
        char a,ch,b;
	unsigned n;
	scr_rowcol(2,0);
	printf("Tempo:    %d",r_tempo);
sp:
        for (a=0; a<15; ++a)    {
                if(buffon[a] && a!=rbuff)       {
			if(playfrom(a))	{	/* Track or song end */
				if(a)		/* if not track 0 */
					buffon[a]=0;	/* turn off buff */
				else
					return;
			}
		}
		if(dsync)
			bt();
		if(MIDIsync)
			bt2();
		if(audmet)
			bt3();
	}
        if(!(ch=scr_csts()))
		goto sp;
	else {
		switch(ch)	{
			case '+':
				++r_tempo;
				break;
			case '-':
				--r_tempo;
				break;
			default:
				return;
		}
		scr_rowcol(2,10);
		printf("%d",r_tempo);
		tempo=2000000/((r_tempo*48)/60);
		set_cnt_1(tempo,tempo >> 8);
	}
	goto sp;
}
playfrom(a)
char a;
{
        char *p,d,*p1;
        p=vir_buff;
        _lmove(8,pointers[a],segment[a],vir_buff,_showds());

        if(*p==0xff)     /* check for EOS */
		return(1);

        readclk();
	p1=p+2;
        if(*p1 > cmsb)
                goto pnow;
        if(*p1 <  cmsb)
                return(0);
        if(*(p+1) <  clsb)
                return(0);
pnow:   /* now we know that it's time to play [notes]... */
        d=*p & 0xc0;
        switch(d)       {
                case 00:        /* note on or off */
                        pnt(p,parts[a].chan,parts[a].transpose);
                        pointers[a]+=4;
                        break;
                case 0x40:      /* prog chan / chan pres. / key pres. */
                        pchan(p,parts[a].chan);
                        pointers[a]+=4;
                        break;
                case 0x80:      /* pitch bender */
                        w_f_e();        /* let FIFO empty */
                        _outb(0xE0+parts[a].chan,MIDID); /* send P. B. code */
                        w_f_e();        /* let FIFO empty */
                        _outb(0,MIDID);  /* we didn't store LSB */
                        w_f_e();
                        _outb(*p << 1,MIDID); /* send MSB */
                        pointers[a]+=3;
                        break;
                case 0xC0:      /* cntrl chan | temp chan */
			cchan(p,parts[a].chan);
			if(!(*p&1)); /* make up for control change */
				++pointers[a];
			pointers[a]+=4;
			break;
	}
        return(0);
}

pnt(p,tc,trans)
char *p,tc,trans;
{
# asm
        MOV SI,WORD [BP+4]      ; get p
        MOV AL,BYTE [SI+3]      ; get *(p+3)
        AND AL,128              ; strip off top bit
        JZ znoff                ; play a note off
        MOV AL,BYTE [BP+6]      ; get tchan
        ADD AL,090H             ; add a basic note on
zcheat1:                        ; here's where we'll cheat on a Note OFF
        MOV DX,0FFA0H           ; MIDI Data port
        CALL w_f_e_             ; wait for DART FIFO to empty
        OUT DX,AL               ; send byte
        MOV AL,BYTE [SI+3]      ; get note to turn on
	ADD AL,BYTE [BP+8]	; add transpose val.
        AND AL,07FH             ; strip off top bit
        CALL w_f_e_             ; wait for DART FIFO to empty
        OUT DX,AL               ; send out note
        MOV AL,BYTE [SI]        ; get velocity
        SHL AL,1                ; shift up into range
        CALL w_f_e_             ; wait for DART FIFO to empty out
        OUT DX,AL               ; send velocity
        JMP zalldone            ; finished

znoff:                          ; send Note OFF command
        MOV AL,BYTE [BP+6]      ; get transmit channel
        ADD AL,080H             ; add basic Note OFF
        JMP zcheat1             ; let's take a short cut

zalldone:                       ; all done

#
}

pchan(p,tc)	/* Program change, channel pressure, or after-touch */
char *p,tc;
{
# asm
	MOV DX,0FFA0H		; DART port
        MOV SI,WORD [BP+4]      ; get p
        MOV AL,BYTE [SI]        ; get I.D. byte
        CMP AL,040H             ; I.D. for Prog change
        JZ zpchan               ; branch if Prog change
	CMP AL,041H		; I.D. for Chan pressure, after-touch
	JZ zchanp
				; else, Poly. key press. (after-touch)
	MOV AL,BYTE [BP+6]      ; get transmit chan
        ADD AL,0A0H		; poly. key pressure
	CALL w_f_e_
	OUT DX,AL		; send 1st byte
	MOV AL,BYTE [SI+3]	; get key #
	CALL w_f_e_
	OUT DX,AL
	MOV AL,BYTE [SI]	; get pressure
	AND AL,01FH		; strip off bottom 5
	SHL AL,1
	SHL AL,1		; bumb up two bits
	CALL w_f_e_
	OUT DX,AL
	JMP zalldone2		; all done

zpchan:                         ; program change
        MOV AL,BYTE [BP+6]      ; get transmit channel
        ADD AL,0C0H             ; basic prog chan
        JMP zcheat2             ; use code from above routine
zchanp:				; channel pressure
	MOV AL,BYTE [BP+6]	; get transmit channel
	ADD AL,0D0H		; add basic chan pres
	JMP zcheat2
zcheat2:                        ; entry point from zpchan
        CALL w_f_e_             ; wait for DART
        OUT DX,AL               ; send byte
        MOV AL,BYTE [SI+3]      ; get new vel
        OUT DX,AL               ; send

zalldone2:                      ; FINE
#
}

cchan(p,tc)
char *p,tc;
{
# asm
        MOV SI,WORD [BP+4]      ; get p
        MOV AL,BYTE [SI]        ; get I.D. byte
        AND AL,1                ; bit 0
        JNZ ztchan              ; tempo change
                                ; now we know it's a cntrl chan
        MOV AL,0B0H             ; cntrl chan
        ADD AL,BYTE [BP+6]      ; add transmit channel
        MOV DX,0FFA0H           ; MIDID port
        CALL w_f_e_             ; wait for DART to empty
        OUT DX,AL
        MOV AL,BYTE [SI+3]      ; get cntrl #
        CALL w_f_e_             ; wait for DART FIFO to empty
        OUT DX,AL
        MOV AL,BYTE [SI+4]      ; get cntrl val
        CALL w_f_e_
        OUT DX,AL
        JMP _fine2              ; done

ztchan:                         ; tempo change
    ;   MOV AL,extsync_
    ;   OR AL,AL                ; set flags
    ;   JNZ _fine2              ; if extsync mode, then this has no purpose
#
    /*  tempo(*(p+3)+30);   */
# asm

_fine2:

#
}

w_f_e()  /* this function "sleeps" until the FIFO in the DART is empty */
{
# asm
        PUSH DX
        PUSH AX
        MOV DX,0FFA2H           ; DART status register
try_again:
        IN AL,DX
        AND AL,4                ; Tx buff empty?
        JZ try_again            ; no?
        POP AX
        POP DX
#
}
/*
	BT, BT2, BT3
	These are the meternome counting routines.
*/
bt()
{
        readclk();
        if(beat==clsb)  {
                _outb(5,MIDIS+1);  /* send sync high */
                _outb(128,MIDIS+1);
                _outb(5,MIDIS+1);  /* send sync low */
                _outb(0,MIDIS+1);
                beat-=s_t_b;
        }
}

bt2()
{
        readclk();
        if(mbeat==clsb) {
                _outb(0xf8,MIDID);  /* MIDI timing clk */
                mbeat-=m_t_b;
                        }
}

bt3()
{
        readclk();
        if(abeat==clsb) {
                if(printer)
                        beepp();
                abeat-=a_t_b;
                        }
}
beepp()
{
        _rax=0x0500;
        _rdx=7;
        _doint(0x21);
}
/*	SETINT
	This routine sets up the interrupt.
*/
setint()
{


        int rxint(),a;
        int seg_num, (*fun_add)();

        fun_add = rxint;
        seg_num=_showcs();
        _poke(fun_add & 0xff,0x28,0);  _poke(fun_add >> 8,0x29,0);
        _poke(seg_num & 0xff,0x2a,0);  _poke(seg_num >> 8,0x2b,0);
        setdart();
        setpic();
}

setdart()
{
        _outb(24,MIDIS);   /* reset channel A */
        _outb(1,MIDIS);    _outb(24,MIDIS);  /* int on all Rx + Ext Stat */
        _outb(3,MIDIS);    _outb(193,MIDIS);
        _outb(4,MIDIS);    _outb(196,MIDIS);
        _outb(5,MIDIS);    _outb(104,MIDIS);
}
setpic()
{
        char a;
        a=_inb(PIC1);  /* read interrupt mask */
        if(a&4)         /* if DART is masked off */
                a-=4;
        _outb(a,PIC1);   /* re-enable ints */
}

setcounter(l1,m1,l2,m2)
char l1,m1,l2,m2;
{
        char e,d;
        _outb(0x34,CSTAT);   /* counter 1=mode 2 - read/load both  */
        e=d; d=e;
        _outb(0x70,CSTAT);  /* counter 2= mode 0 - read/load both */
        e=d; d=e;
        _outb(l2,COUNTER2);  /* set LSB of counter 2 */
        e=d; d=e;
        _outb(m2,COUNTER2);  /* set MSB of counter 2 */
        e=d; d=e;
        _outb(l1,COUNTER1);    /* set LSB of counter 1 */
        e=d; d=e;
        _outb(m1,COUNTER1);  /* set MSB of counter 1 */
        readclk();
        while(cmsb!=255 && clsb!=255)
                readclk();
        return;
}
set_cnt_1(l1,m1)
char l1,m1;
{
        _outb(l1,COUNTER1);  /* set LSB of counter 1 */
        _outb(m1,COUNTER1);  /* set MSB of counter 1 */
}

intoff()
{
        _outb(01,MIDIS);    /* turn off my interrupt */
        _outb(00,MIDIS);    /* (at the DART) */
}
inton()
{
        _outb(01,MIDIS);    /* turn on my interrupt */
        _outb(24,MIDIS);    /* (at the DART) */
}
readclk()
{
# asm
        MOV AL,64
        MOV DX,0FFA7H
        CLI            ; an interrupt right now from Rxint would be bad
        OUT DX,AL
        NOP
        NOP
        NOP
        SUB DX,2
        IN AL,DX
        MOV clsb_,AL
        NOP
        NOP
        NOP
        IN AL,DX
        MOV cmsb_,AL
        STI             ; ints back on
#
}

scr_cla()  /* clear screen & pstn cursor at top */
{
        scr_clr();
        scr_rowcol(0,0);
}

/*	MMODE
	This is a no-frills routine for setting up various
	MIDI system modes
*/
mmode()
{
        char a,mo,v;
st:
        scr_cla();
	printf("                        MIDI mode assignments:\n");
        printf("%s\n",sin_line);
ss:
        putchar('\n');
        printf("        1 - Omni ON  / Poly\n");
        printf("        2 - Omni OFF / Poly\n");
        printf("        3 - Omni ON  / Mono\n");
        printf("        4 - Omni OFF / Mono\n");
        printf("        5 - Program change\n");
        printf("        6 - All notes off (all channels)\n");
        printf("        7 - All notes off (one channel)\n");
        printf("        0 - Exit\n");
        printf("Enter new mode:");
        mo=getint();
        if(!mo)
                return;
        if(mo>7)
                goto st;
        if(mo==6)
                goto skip;
        printf("Send over which channel:");
        a=getint();        if(a > 15)
                goto ss;
        printf("\nChannel %d:\n",a);
skip:
        switch(mo)      {
                case 1:
                        printf("  Poly / Omni on\n");
                        poly(a);
                        o_on(a);
                        break;
                case 2:
                        printf("  Poly / Omni off\n");
                        poly(a);
                        omni_off(a);
                        break;
                case 3:
                        printf("  Mono / Omni on\n");
                        mono(a);
                        o_on(a);
                        break;
                case 4:
                        printf("  Mono / Omni off\n");
                        mono(a);
                        omni_off(a);
                        break;
                case 5:
                        printf("  Program change\n");
                        printf("   Enter new program: ");
                        v=getint();
                        _outb(192+a,MIDID);
                        _outb(v,MIDID);
                        break;
                case 6:
                        for(a=0; a<16; ++a)    {
                                _outb(0xb0+a,MIDID);
                                _outb(123,MIDID);
                                w_f_e();
                                _outb(0,MIDID);
                                                }
                        printf(" All notes off - all channels\n");
                        getchar();
                        break;
                case 7:
                        _outb(0xb0+a,MIDID);
                        _outb(123,MIDID);
                        w_f_e();
                        _outb(0,MIDID);
                        printf(" All notes off\n");
                        getchar();
                        break;
                        }
        goto st;
}

o_on(ch)
char ch;
{
        _outb(176+ch,MIDID);
        _outb(125,MIDID);
        w_f_e();
        _outb(0,MIDID);
        getchar();
}

omni_off(ch)
char ch;
{
        _outb(176+ch,MIDID);
        _outb(124,MIDID);
        w_f_e();
        _outb(0,MIDID);
        getchar();
}

poly(ch)
char ch;
{
        _outb(176+ch,MIDID);
        _outb(127,MIDID);
        w_f_e();
        _outb(0,MIDID);
        getchar();
}

mono(ch)
char ch;
{
        char v;
        printf("How many channels? (0 = # in receiver, or other) ");
        v=getint();
        _outb(176+ch,MIDID);
        _outb(126,MIDID);
        w_f_e();
        _outb(v,MIDID);
        getchar();
}

get_av_mem()  /* returns total system RAM - in K */
{
        _doint(0x012);
        return(_rax);
}

char *_showss()
{
# asm
	MOV AX,SS
#
}

hak()	/* display "hit any key to continue" & wait */
{
	puts("\n\n");
	scr_cls();
	puts("                            Hit any key to continue.");
	while(!scr_csts());
}

getint()	/* error check an input line & return int */
{
	char inln[20],*b,flag;
stg:
	_gets(inln,20);
	if(strlen(inln))	{
		for(b=inln;*b!='\0';++b)	{
			if(!isdigit(*b) && *b != '-')	{
				putchar(7);
				goto stg;
			}
		}
		return(atoi(inln));
	}
	else
		putchar(7);
	goto stg;
}

/*	ED_IN_FILT
	This routine allows the user to edit the input filter
*/
ed_in_filt()
{
	int n;
	scr_cla();
	scr_rowcol(0,26);
        printf("Edit Input Filter\n");
        printf("%s\n\n",sin_line);
st_filt:
	scr_rowcol(3,0);
	printf("             Mess #:    Message type:           Status:\n");
	printf("            ÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ\n");
	printf("               1      ³  Note ON               ³   %s\n",(in_filt & 1) ? yes : no);
	printf("               2      ³  Note OFF              ³   %s\n",(in_filt & 2) ? yes : no);
	printf("               3      ³  Program Change        ³   %s\n",(in_filt & 4) ? yes : no);
	printf("               4      ³  Channel Pressure      ³   %s\n",(in_filt & 8) ? yes : no);
	printf("               5      ³  Pitch Wheel           ³   %s\n",(in_filt & 16) ? yes : no);
	printf("               6      ³  Control Change        ³   %s\n",(in_filt & 32) ? yes : no);
	printf("               7      ³  Poly. Key Pressure    ³   %s\n",(in_filt & 64) ? yes : no);
	printf("            ÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄ\n");
	printf("\n\n\n\n\n");
re_try:
	scr_rowcol(18,12);
	printf("Enter Message Number to toggle, 0 to end: ");
	n=getint();
	if(n<0 || n>7)
		goto re_try;
	if(!n)
		return;
	n=1 << (n-1);	/* make n into bit to complement */
	in_filt = in_filt & n ? in_filt-n : in_filt+n;
	goto st_filt;

}

get_options()	/* enter various user options */
{

em:     printf("Enter metrenome speed:");
        r_tempo=getint();
        if(r_tempo<40 || r_tempo>200)
                goto em;
        tempo=2000000/((r_tempo*48)/60);
        printf("Audio Sync (Y/N):");
        audmet=(toupper(getchar())=='Y') ? 1:0;
        putchar('\n');
        printf("Drum Sync (Y/N):");
        dsync=(toupper(getchar())=='Y') ? 1:0;
        putchar('\n');
        printf("MIDI Sync (Y/N):");
        MIDIsync=(toupper(getchar())=='Y') ? 1:0;
}

/* dir(fname,ext)
	char *fname,*ext;

	This function shows a directory of the currently logged directory
	and disk and print it on the screen in "five-across" format.
*/

dir(fname,ext)   /* function to show directory of currently logged disk  & dir */
char *fname,*ext;	/* pointers to filename & extension */
{

        struct fcb {
                char drive_num;
                char filename[8];
                char extension[3];
                char rest[25];  /* the rest of the fcb */
        } fcb;

        struct fcb fcb2;
	unsigned dta_add, dta_seg;
        char filename[21];
	char flag=4;
	char cnt=80;

        fcb.drive_num=0;
	strcpy(filename,"                    ");
        strncpy(fcb.filename,fname,8);
        strncpy(fcb.extension,ext,3);
        _setmem(fcb.rest,25,0);

	_rax=0x2f00;	/* get current DTA */
	_doint(0x21);
	dta_add=_rbx;
	dta_seg=_res;	/* this is the offset & segment of the current DTA */

        _rdx=fcb;
        _rds=_showds();
        _rax=0x1100;

	scr_rowcol(6,0);
	printf("%s\n",sin_line);
	scr_rowcol(7,0);
	scr_cls();
        for(;;)	{
                _doint(0x21);
		if((_rax & 0xff))
			break;	/* last file has been listed */
                _lmove(20,dta_add,dta_seg,fcb2,_showds());
                strncpy(filename,fcb2.filename,8);
                filename[8]='.';
		filename[9]='\0';
                strcat(filename,fcb2.extension);
		filename[12]='\0';
                puts(filename);
		puts("    ");
		if(!flag)
			flag=5;
		if(!--cnt)	{
			printf("                    Hit any key to continue.");
			getchar();
			scr_rowcol(7,0);
			scr_cls();
			cnt=80;
			flag=4;
		}
		--flag;
                _rax=0x1200;
        }
}
