/* $Id: pc-mach.c,v 1.6 89/05/06 17:13:39 lee Exp $
 *
 * Glib - Generic LIBrarian and editor
 *
 * Machine dependent stuff for MIDI programs.
 * Time Thompson
 *
 * This is for MS-DOS machines.  The screen operations should
 * work okay, but the MIDI I/O needs to be worked on.  It may
 * or may not be fast enough on some machines. 
 * $Log:	pc-mach.c,v $
 * Revision 1.6  89/05/06  17:13:39  lee
 * rel. to comp.sources.misc
 * 
 */

#include "glib.h"

int Rows = 24;
int Cols = 80;

char *
alloc(n)
{
	char *p;

	if ( (p=malloc((unsigned)n)) == (char *)NULL ) {
		printf("*** Whoops *** alloc has failed?!?  No more memory!\n");
		fflush(stdout);
		bye();
	}
	return(p);
}

flushmidi()
{
	while ( STATMIDI )
		getmidi();
}

/* getmousepos - get currect row and column of mouse */
getmousepos(amr,amc)
int *amr;
int *amc;
{
	*amr = -1;
	*amc = -1;
}

/* statmouse - return mouse button state (0=nothing pressed,1=left,2=right) */
statmouse()
{
	return(-1);
}

/* Return when either a console key or mouse button is pressed. */
mouseorkey()
{
	return(getconsole());
}

#ifdef C86
getch()
{
	struct regval sreg, rreg;
	sreg.ax = 0x0000;
	sysint(0x16, &sreg, &rreg);
	return(rreg.ax & 0x7f);
}
#endif

windinit()
{
}

windgoto(r,c)
int r,c;
{
#ifdef C86
	struct regval sreg, rreg;
	sreg.ax = 0x200;
	sreg.bx = 0;
	sreg.dx = (r<<8) | c ;
	sysint(0x10, &sreg, &rreg);
#endif
#ifdef TURBOC
	gotoxy(1+c,1+r);
#endif
}

winderaserow(r)
{
#ifdef C86
	struct regval sreg, rreg;
	sreg.ax = 0x0600;
	sreg.bx = 0;
	sreg.cx = (r<<8);	/* urow, lcol */
	sreg.dx = (r<<8) | 79;
	sysint(0x10, &sreg, &rreg);
#endif
#ifdef TURBOC
	gotoxy(1,r+1);
	clreol();
#endif
}

windexit()
{
	/* windgoto(23,0);
	windrefresh();
	nocbreak();
	nl();
	echo();
	endwin(); */
}

windclear()
{
#ifdef C86
	struct regval sreg, rreg;
	sreg.ax = 0x0600;
	sreg.bx = 0;
	sreg.cx = 0;	/* urow, lcol */
	sreg.dx = (24<<8) | 79;
	sysint(0x10, &sreg, &rreg);
#endif
#ifdef TURBOC
	clrscr();
#endif
}

/* windgets - get a line of input from the console, handling backspaces */
windgets(s)
char *s;
{
	char *origs = s;
	int c;

	while ( (c=getconsole()) != '\n' && c!='\r' && c!= EOF ) {
		if ( c == '\b' ) {
			if ( s > origs ) {
				windstr("\b \b");
				s--;
			}
		}
		else {
			windputc(c);
			*s++ = c;
		}
		windrefresh();
	}
	*s = '\0';
}

windstr(s)
char *s;
{
	int c;

	while ( (c=(*s++)) != '\0' )
		windputc(c);
}

windputc(c)
int c;
{
	putchar(c);
}

windrefresh()
{
}

beep()
{
	putchar('\007');
}

windhigh()
{
}

windnorm()
{
}

/****************
 * openls(), nextls(), and closels() are used to scan the current directory.
 ***************/

int first = 1;
openls()
{
	first = 1;
}
char *
nextls()
{
	static struct ffblk ffblk;
	int n;

	if ( first ) {
		n = findfirst("*.*",&ffblk,0);
		first = 0;
	}
	else
		n = findnext(&ffblk);

	if ( n == 0 )
		return(ffblk.ff_name);
	else
		return((char *)NULL);
}
closels()
{
}

#ifdef OLDSTUFF
/*
 * The following MPU code has been provided by Steve Frysinger (moss!spf).
 */

#define	STATUS_PORT	0x0331
#define	COMMAND_PORT	0x0331
#define	DATA_PORT	0x0330
#define	DATA_READY_MASK	0x40
#define	DATA_AVAIL_MASK	0x80

int send_command_4001(val)	/* Patterned after Voyetra's reset_4001() */
unsigned val;
{
	unsigned x = 0;
	int flag;
	int ack_count,time_count;
	int retval=1; /* Assume success */
	inportb(DATA_PORT);
	for (time_count=5000,flag=1;time_count&&flag;time_count--)
	{
		if (!(inportb(STATUS_PORT)&DATA_READY_MASK)) flag=0;
	}
	if (flag)
	{
		fprintf(stderr,"Command timeout waiting for port!\n");
		retval = -1;
	}
	else
	{
		outportb(COMMAND_PORT,val);
		for (time_count=10000,ack_count=5,flag=0;!flag;)
		{
			if ((inportb(STATUS_PORT)&DATA_AVAIL_MASK))
			{
				time_count--;
				if (!time_count)
				{
					flag++;
					fprintf(stderr,"Command timeout waiting for ACK.\n");
					retval = -1;
				}
			}
			else
			{
				x = (unsigned)inportb(DATA_PORT);
				if (x == 0xfe)
				{
					flag++;
					fprintf(stderr,"Got command acknowledgement\n");
				}
				else
				{
					ack_count--;
					if (!ack_count)
					{
						printf("Too many data bytes without ACK\n");
						retval = -1;
					}
				}
			}
		}
	}
	return(retval);
} /* send_command_4001 */
#endif

/*
 * Acknowledgements to John Helton for additions to support ATT PC6300 w/
 * MPU401 and OP4000 MIDI Interface Controllers.
 * PC6300 support uses Borland TurboC 'C' compiler, large model
 *
 */
#define QUIT 1
#define NOQUIT 0

void interrupt (*oldint0)();
void interrupt (*oldint2)();
void interrupt (*oldint1b)();
void interrupt (*oldint23)();
void interrupt (*oldint24)();
void interrupt ctr_brk_handler();
void interrupt fatal_err_hndlr();

void interrupt far tick_isr();
void interrupt far midi_isr();
extern unsigned long hzcount;
extern char midi_buffer[];
extern int wrt_index, rd_index;

extern unsigned _stklen;	/* Turbo C specific */ 

int Nextpcchar = EOF;
int Fakemidi = 0;
int (*Intfunc)() = NULL;

hello()
{
    _stklen = 50000;
    hzcount = 0L;			/* init timer & buffer globals */
    wrt_index = rd_index = 0;
    if (!setup_MIC())			/* Set up the Midi Interface Ctrl */
    {
	if ( fisatty(stdout) )
	    printf("Can't initialize midi interface\n");
	Fakemidi = 1;
	reset_ints(NOQUIT);		/* restore interrupts to a suitable
					   condition to keep running without
					   the Midi Interface (for testing) */
	return;
    }
    send_msg(UART, COMMAND_MSG);	/* run the MIC in dumb mode */
    return;
}

rtend()
{
    reset_MIC();
}

bye()
{
	system_exit();
}

flushconsole()
{ return; }

getconsole()
{
    int c;

    if ( Nextpcchar != EOF ) {
    	c = Nextpcchar;
    	Nextpcchar = EOF;
    }
    else {
    	c = getch();
    }
    return(c);
}

putconsole(c)
{
	putch(c);
}

/* getmidi reads data out of the circular midi buffer */
getmidi()
{
	static char mbuff[1];
	unsigned char *p;

	while ( wrt_index == rd_index)
		;
	rd_index++;
	if (rd_index > 511) rd_index = 0;
	mbuff[0] = midi_buffer[rd_index];
	return (mbuff[0] & 0xff);
}

sendmidi(val)
int val;
{
	unsigned char p[1];
	p[0] = val;
	putnmidi(1,p);
}

putnmidi(n,p)
char *p;
{
    while ( n-- > 0 ) {
	    int c = (*p++) & 0xff;
	    if ( Fakemidi )
		printf("putmidi(d=%d x=%x o=%o)\n",c,c,c);
	    else
		write_data_MPU(c);
    }
}

resetclock()
{
	hzcount = 0L;
}

long
milliclock()
{
	return (hzcount*5L);
}

/* filetime - Return the modification time of a file in seconds. */
long
filetime(fn)
char *fn;	/* file name */
{
	struct stat s;

	if ( stat(fn,&s) == -1 )
		return(-1);
	return(s.st_mtime);
}

/* currtime - Return current time in seconds (consistent with filetime()) */
long
currtime()
{
	long time();
	return ( time((long *)0) );
}

fisatty(f)
FILE* f;
{
	return(isatty(fileno(f)));
}

/*
 * send_msg(COM, TYPE, PTR, LENGTH)
 * will send command to Midi Interface Controller (MIC) Command port, 
 * then either return, wait for data or send data
 */

send_msg(com, type, ptr, length)
int com, type, length;
char *ptr;
{
    int acked;
    int x;

    while ( NOT_READY_FOR_DATA_MIC());
    disable_ints();
    COMMAND_OUT_MIC((char) com);	/* send the command to the MIC */
    for(acked=0; !acked; ) {		/* wait until our command is acked */
        x = read_MPU();			/* get a byte back from the MIC */
        if (x == ACK) acked++;		/* done if ACK received */
        else write_rcv_buff(x);		/* deal with this input stream first */
    }
    if (type == WRITE_MSG)		/* if write request... */
        for (; length; ptr++, length--) write_data_MPU(*ptr);
					/* write all the bytes to the MIC */
    enable_ints();			/* ints restored to where they were
					   before we shut them off */
}


/* WRITE_RCV_BUFF(INPUT)
 * part of the command handshake sequence.  Input read from the
 * midi data port is saved in wraparound FIFO
*/

write_rcv_buff(val)
int val;
{
    wrt_index++;
    if (wrt_index > 511) wrt_index = 0;
    midi_buffer[wrt_index] = (char) val;
}

/*
 * SETUP_MIC()
 * resets MIC, sets up int vectors and enables int system
 * Returns - true if MIC set ok
 */

static setup_MIC()
{
    int retval;

    set_ints();			/* set the new interrupt vectors */
    init_enable_MPU();		 /* turn on interrupt system for first time */
    retval = reset_MIC();		/* try to reset MIC */
    if (!retval) retval = reset_MIC(); /* if no good, try again */
    init_enable_TIMER();	/* turn on timer interrupt if reset ok*/
    return(retval);
}

/*
 * RESET_MIC()
 * attempts to do a low level software reset on the MIC
 * returns - true if success
 */

reset_MIC()
{
    unsigned x = 0;
    int flag;
    int ack_count, time_count;
    int retval=1;		/* assume success */
    
    disable_ints();		/* disable interrupts */
    READ_DATA_MIC();		/* Clear out any data in receiver */
    /* Here we will loop until ready for data, or we give up 	*/
    for (time_count=5000, flag=1; time_count && flag; time_count--) {
	/* if MIC ready for data, we can quit loop */
        if (!(NOT_READY_FOR_DATA_MIC())) flag=0;
    }
    /* if timed out before we got ready for data, return flag couldn't reset */
    if (flag){	
	retval = 0;
    }
    else {
        COMMAND_OUT_MIC(RESET); /* Send the reset command */
	/* loop here till time out, or ack recvd */
        for (time_count=10000, ack_count=5, flag=0; !flag;) {
	    if ((NOT_DATA_AVAILABLE_MIC())) { /* if no data from MIC */
	        time_count--;		/* count one more wait */
	        if (!time_count) {
BAD_RESET:
		    flag++;		/* if time out, note it */
		    retval=0;		/* and note failure */
		}
	    }
	    else {		/* If there is data coming back form MIC */
	        x = (unsigned) READ_DATA_MIC(); /* ...read it */
	        if (x == ACK) { /* If data is the ACK message */
		    flag++;		/* note we are done, and no failure */
		}
	        else {		/* if data back from MIC wasn't ack */
		    ack_count--;/* note that we got something that isn't ack */
		    if (!ack_count) goto BAD_RESET;
				/* If we get too many data bytes back from
				   MIC with no ack, give up */
		}
	    }
	}
    }
    enable_ints();			 /* turn ints back on */
    return(retval);
}

/*
 * SYSTEM_EXIT()
 * called to leave program.  resets ints and exits
 */
system_exit()
{
    reset_MIC();
    disable_ints();
    reset_ints(QUIT);	/* put all the int vectors back where they were */
    reset_TIMER();
    enable_ints();
    windexit(0);
    exit();
}

/*
 * hi level interrupt routines
 */

void interrupt
ctr_brk_handler()
{
    if ( Intfunc != NULL )
	(*Intfunc)();
    printf("interrupts reset; BYE!\n");
    system_exit();
}

void interrupt
fatal_err_hndlr()
{
    fatal_dos_handler();
}

fatal_dos_handler()	/* called by fatal error handler ISR */
{
    printf("got fatal error; BYE!\n");
    system_exit();
}

set_ints()
{
    oldint0 = getvect(0x08);	/* timer interrupt on irq0 */
    setvect(0x08, tick_isr);
    oldint2 = getvect(0x0a);	/* midi interrupt on irq2 */
    setvect(0x0a, midi_isr);
    oldint1b = getvect(0x1b);	/* the rest of these are dos & bios ints */
    setvect(0x1b, ctr_brk_handler);
    oldint23 = getvect(0x23);
    setvect(0x23, ctr_brk_handler);
    oldint24 = getvect(0x24);
    setvect(0x24, fatal_err_hndlr);
}

reset_ints(reset_type)
{
    if (reset_type == QUIT) {
        setvect(0x08, oldint0);
        setvect(0x1b, oldint1b);
        setvect(0x23, oldint23);
        setvect(0x24, oldint24);
    }
    setvect(0x0a, oldint2);	/* only need to reset one hardware interrupt
				   if we're not going to quit */
}

signal(type,func)
int (*func)();
{
	if ( type == SIGINT ) {
		if ( func == SIG_IGN )
			Intfunc = NULL;
		else
			Intfunc = func;
	}
}
