/*****************************************************************************/
/*									     */
/*									     */
/*	CP/M emulator version 0.1					     */
/*									     */
/*	written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de)		     */
/*	June-1994							     */
/*									     */
/*	This file is distributed under the GNU COPYRIGHT		     */
/*	see COPYRIGHT.GNU for Copyright details				     */
/*									     */
/*									     */
/*****************************************************************************/
#include "cpmemu.h"

#define HAVE_RAW_IO

/* magic for character I/O */
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>

#ifdef HAVE_RAW_IO
#include <sys/ioctl.h>
#include <linux/kd.h>
static int rawmode = 0;
#endif

#define SWITCHMODES	/* switch between polled & sleeping */
#define POLLING  1	/* conin() loops */
#define SLEEPING 2	/* conin() sleeps until character found */

static int currentmode = POLLING;
static int ifd, ofd;

static struct termios iold, iraw;
static struct termios oold, oraw;

static void restore_modes(void) {
    tcsetattr(ifd,TCSAFLUSH,&iold);
    if (ifd != ofd)
	tcsetattr(ofd,TCSAFLUSH,&oold);
}

int setup_io(const char *tty) {
    if (tty) {
	ofd = ifd = open(tty, O_RDWR);
	if (ofd < 0) {
	    fprintf(stderr, "cpm: cannot open tty %s\n", tty);
	    exit(1);
	}
    } else {
	ifd = STDIN_FILENO;
	ofd = STDOUT_FILENO;
    }
    if (tcgetattr(ifd, &iold))
	return 0;		/* no chance */
    iraw = iold;
    iraw.c_lflag &= ~ECHO & ~ECHONL & ~ICANON & ~ISIG;
    iraw.c_iflag &= ~(ICRNL | IGNCR | IXON | IXOFF);
    iraw.c_cc[VMIN] = 0;	/* input: return without waiting */
    iraw.c_cc[VTIME] = 0;

    if (ifd != ofd) {
	if (tcgetattr(ofd, &oold))
	    return 0;		/* no chance */
	oraw = oold;
	oraw.c_oflag &= ~OPOST;
	if (tcsetattr(ofd,TCSADRAIN,&oraw))
	    return 0;
    } else
	iraw.c_oflag &= ~OPOST;
    /* now, register atexit function */
    atexit(restore_modes);
    if (tcsetattr(ifd,TCSADRAIN,&iraw))
	return 0;
    return 1;
}

int waiting = -1;

static void poll(void) {
    waiting = 0;
#ifdef HAVE_RAW_IO
    /* nasty undocumented stuff to prepare for tandy I memory mapped
       keyboard emulation (need key release events) */
    if (z80mem[4] == 0x5f && !rawmode) {
	ioctl(ifd, KDSKBMODE, K_RAW);
	rawmode = 1;
    } else if (z80mem[4] != 0x5f && rawmode) {
	ioctl(ifd, KDSKBMODE, K_XLATE);
	rawmode = 0;
    }
#endif
    if (read(ifd, &waiting, 1) < 1)
	waiting = -1;
}

void putch(int c) {	/* output character without postprocessing */
    write(ofd, &c, 1);
}

void putmes(const char *s) {
    write(ofd, s, strlen(s));
}
void vt52(int c) {	/* simple vt52,adm3a => ANSI conversion */
    static int state = 0, x, y;
    char buff[32];
#if 0
    static FILE *log = NULL;
    if (!log)
	log = fopen("cpm.out", "w");
    fputc(c, log);
#endif
    switch (state) {
    case 0:
	switch (c) {
	case 0x7f:		/* DEL: echo BS, space, BS */
	    putmes("\b \b");
	    break;
	case 0x1a:		/* adm3a clear screen */
	case 0x0c:		/* vt52 clear screen */
	    putmes("\033[H\033[2J");
	    break;
	case 0x1e:		/* adm3a cursor home */
	    putmes("\033[H");
	    break;
	case 0x1b:
	    state = 1;	/* esc-prefix */
	    break;
	case 1:
	    state = 2;	/* cursor motion prefix */
	    break;
	case 2:		/* insert line */
	    putmes("\033[L");
	    break;
	case 3:		/* delete line */
	    putmes("\033[M");
	    break;
	case 5:		/* clear to eol */
	    putmes("\033[K");
	    break;
	case 18: case 19:
	    break;
	default:
	    putch(c);
	}
	break;
    case 1:	/* esc was sent */
	switch (c) {
	case '=':
	case 'Y':
	    state = 2;
	    break;
	default:		/* some true ANSI sequence? */
	    state = 0;
	    putch(0x1b);
	    putch(c);
	}
	break;
    case 2:
	y = c - ' '+1;
	state = 3;
	break;
    case 3:
	x = c - ' '+1;
	state = 0;
	sprintf(buff, "\033[%d;%dH", y, x);
	putmes(buff);
	break;
    } 
}
	
int getch(void) {		/* return a character, or -1 if none ready */
    if (waiting < 0) {
	/* must read character from keyboard */
	/* this part switches to sleeping mode */
	if (currentmode == POLLING) {
	    iraw.c_cc[VMIN] = 1;
	    currentmode = SLEEPING;
	    tcsetattr(ifd,TCSADRAIN,&iraw);
	}
	poll();
    }
    if (waiting >= 0) {
	int c;
	c = waiting & 0xff;
	waiting = -1;
	return c;
    }
    return -1;
}

int kbhit(void) {
    if (waiting < 0) {
	/* really ask keyboard */
	/* this part switches to polling mode */
	if (currentmode != POLLING) {
	    iraw.c_cc[VMIN] = 0;
	    currentmode = POLLING;
	    tcsetattr(ifd,TCSADRAIN,&iraw);
	}
	poll();
    }
    return waiting >= 0;
}

