/*
 *	EMUL-8: a pdp8e emulator.
 *
 *	Author:
 *		Bill Haygood
 *		41832 Ernest Road
 *		Loon Lake, WA 99148-9607
 *		Internet: billh@comtch.iea.com
 *		Voice/AnsMach/FAX \
 *			or	   509-233-2555
 *		  Cellular/Pager  /
 *
 *	Copyright 1992, 1993, 1994 by the author with all rights reserved.
 *
 *	Implementation dependent code.
 */
#ifdef MS_DOS
# include <dos.h>		/* bdos					*/
# include <conio.h>		/* kbhit				*/
# define  SCRNSIZE	80	/* Default screen size (get from DOS!)	*/
  INT	  charcnt = 0 ;		/* Character counter			*/
  INT	  auto_lf, escflag = 1 ;	/* ESCape and LF flags		*/
#endif

#ifdef AMIGA
# include <intuition/intuition.h>
# include <clib/intuition_protos.h>
# include <clib/alib_protos.h>
# include <proto/exec.h>
#endif

#ifndef	TRUE
# define	TRUE	1
#endif
#ifndef FALSE
# define	FALSE	0
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef AMIGA
  extern struct Window *OpenWindow (struct NewWindow *) ;
  struct IntuitionBase *IntuitionBase = NULL ;
  static struct Window *conwindow = NULL ;
  static struct Window *auxwindow = NULL ;
  static struct IOStdReq *conwriteReq = NULL ;
  static struct MsgPort *conwritePort = NULL ;
  static struct IOStdReq *conreadReq = NULL ;
  static struct MsgPort *conreadPort = NULL ;
  static struct IOStdReq *auxwriteReq = NULL ;
  static struct MsgPort *auxwritePort = NULL ;
  static struct IOStdReq *auxreadReq = NULL ;
  static struct MsgPort *auxreadPort = NULL ;
#endif

extern	unsigned char	aux_vdu, run ;

extern unsigned char coninchar, conoutchar ;
extern unsigned char auxinchar, auxoutchar ;
extern FILE  *pdp8conout ;		/* pdp8 console output file	*/
extern FILE  *pdp8auxout ;		/* pdp8 aux console output file	*/

unsigned char	nextconinchar = '\0' ;
unsigned char	nextauxinchar = '\0' ;

#ifdef AMIGA
/* -------------------------------------------------------------------- */
/*
 *	Open a VDU device.
 */
static char OpenConsole (struct IOStdReq *writereq,
			struct IOStdReq *readreq, struct Window *window)
{
    char error ;

    writereq -> io_Data = (APTR) window ;
    writereq -> io_Length = sizeof (struct Window) ;
    error = OpenDevice ((char *) "console.device", 0,
				(struct IORequest *) writereq, 0) ;
    readreq -> io_Device = writereq -> io_Device ;
    readreq -> io_Unit = writereq -> io_Unit ;
    return error ;
}
/* -------------------------------------------------------------------- */
/*
 *	PDP8 console refresh variables (AmigaDOS only).
 *	Used when window resized.
 */
static	unsigned char	freshseq [] = { 0x9b, '0', ' ', 'q' } ;
static	char	conEscSeq [16] = { '\0' } ;
static	char	*conEscSeqP ;
static	int	conheight = 23, conwidth = 77 ;
#define	FRESH_SIZE (1 << 11)
#define	FRESH_MASK (FRESH_SIZE - 1)
static	char	confresh [FRESH_SIZE] = { '\0' } ;
static	int	confresh_i = 0 ;
static	unsigned char	confreshout [FRESH_SIZE] = { '\0' } ;
#define	COUNT	200
static	int	counter = 0 ;
/* -------------------------------------------------------------------- */
/*
 *	PDP8 aux console refresh variables (AmigaDOS only).
 *	Used when window resized.
 */
static	char	auxEscSeq [16] = { '\0' } ;
static	char	*auxEscSeqP ;
static	int	auxheight = 23, auxwidth = 77 ;
static	char	auxfresh [FRESH_SIZE] = { '\0' } ;
static	int	auxfresh_i = 0 ;
static	unsigned char	auxfreshout [FRESH_SIZE] = { '\0' } ;
/* -------------------------------------------------------------------- */
/*
 *	Queue a keyboard read request.
 */
static void QueueRead (struct IOStdReq *readreq, unsigned char *charact)
{
    readreq -> io_Command = CMD_READ ;
    readreq -> io_Data = (APTR) charact ;
    readreq -> io_Length = 1 ;
    SendIO ((struct IORequest *) readreq) ;
}
#endif
/* -------------------------------------------------------------------- */
/*
 *	Check for a keystrike at a keyboard.
 */
unsigned char KeyStrike (int ioaddr)
{
    unsigned char KeyReady = FALSE ;

#ifdef AMIGA
    if (!counter)
    {
/*
 *	Request window size.
 */
	conwriteReq -> io_Data = (APTR) freshseq ;
	conwriteReq -> io_Length = 4 ;
	DoIO ((struct IORequest *) conwriteReq) ;
    }
    if (aux_vdu && counter == (COUNT >> 1))
    {
	auxwriteReq -> io_Data = (APTR) freshseq ;
	auxwriteReq -> io_Length = 4 ;
	DoIO ((struct IORequest *) auxwriteReq) ;
    }
    counter = (counter) ? --counter : COUNT ;
#endif
    switch (ioaddr)
    {
	case 003 :
#ifdef AMIGA
	    if (nextconinchar)
	    {
		coninchar = nextconinchar ;
		nextconinchar = '\0' ;
		QueueRead (conreadReq, &nextconinchar) ;
		if (coninchar == 0x9b)
		{
		    coninchar = '\0' ;
		    conEscSeqP = conEscSeq ;
		    while (!nextconinchar)
			;
		    while ((nextconinchar & 0x7f) != '~'
				&& (nextconinchar & 0x7f) != 'r')
		    {
			*conEscSeqP++ = nextconinchar ;
			nextconinchar = '\0' ;
			QueueRead (conreadReq, &nextconinchar) ;
			while (!nextconinchar)
			    ;
		    }
		    *conEscSeqP++ = nextconinchar ;
		    *conEscSeqP = '\0' ;
		    nextconinchar = '\0' ;
		    QueueRead (conreadReq, &nextconinchar) ;
		    switch (conEscSeq [0])
		    {
			int flag, h, w ;
			case '1' :		/* Window size ESC seq	*/
			    flag = FALSE ;
			    conEscSeqP = conEscSeq + 4 ;
			    sscanf (conEscSeqP, "%d", &h) ;
			    if (h != conheight)
			    {
				conheight = h ;
				flag = TRUE ;
			    }
			    while (*conEscSeqP++ != ';')
				;
			    sscanf (conEscSeqP, "%d", &w) ;
			    if (w != conwidth)
			    {
				conwidth = w ;
				flag = TRUE ;
			    }
			    if (flag)
			    {
				int i, j, k = confresh_i++ ;
				confresh_i &= FRESH_MASK ;
				for (i = 0, j = 0 ;
					confresh [i] && i < FRESH_SIZE ;
						i++)
				    if ((confresh [i] & 0177) == 012)
					j++ ;
				if (j < h)
				    h = j ;
				for (i = h ; i ;
					i--, confresh_i =
						--confresh_i & FRESH_MASK)
				    while (confresh [confresh_i] != '\n')
					confresh_i =
						--confresh_i & FRESH_MASK ;
				j = (h > 1) ? 2 : -1 ;
				confresh_i = (confresh_i + j)
							& FRESH_MASK ;
				strcpy (confreshout, "\x9bH\x9bJ") ;
				i = 4 ;
				while (k != confresh_i)
				{
				    if (w = confresh [confresh_i++])
					confreshout [i++] = w ;
				    confresh_i &= FRESH_MASK ;
				}
				confreshout [i] = '\0' ;
				conwriteReq -> io_Data
						= (APTR) confreshout ;
				conwriteReq -> io_Length = -1 ;
				DoIO ((struct IORequest *) conwriteReq) ;
			    }
			    break ;
			case '9' :
			    run = FALSE ;	/* F10 struck; quit	*/
			    break ;
			default :
			    break ;
		    }
		    coninchar = nextconinchar ;
		    nextconinchar = '\0' ;
		    QueueRead (conreadReq, &nextconinchar) ;
		}
		else
		    KeyReady = TRUE ;
		if (coninchar == 030)		/* Turn ^X into ^C	*/
		    coninchar = 003 ;
		if (coninchar == 010)		/* Turn BS into DEL	*/
		    coninchar = 0177 ;
	    }
#endif
#ifdef MS_DOS
	    if (kbhit ())	/* This is the vital function for KSF	*/
	    {
		coninchar = getch () ;
		if (!coninchar)
		{
		    coninchar = getch () ;	/* Extended code	*/
		    if (coninchar == 1)
			coninchar = 033 ;	/* ESC			*/
		    if (coninchar > 50)
		    {				/* Function key		*/
			coninchar = 0 ;
			run = FALSE ;		/* Quit			*/
		    }
		}
		else
		    KeyReady = TRUE ;
		if (coninchar == 030)		/* Turn ^X into ^C	*/
		    coninchar = 003 ;
		if (coninchar == 010)		/* Turn BS into DEL	*/
		    coninchar = 0177 ;
	    }
#endif
	    break ;
	case 040 :
#ifdef AMIGA
	    if (aux_vdu && nextauxinchar)
	    {
		auxinchar = nextauxinchar ;
		nextauxinchar = '\0' ;
		QueueRead (auxreadReq, &nextauxinchar) ;
		if (auxinchar == 0x9b)
		{
		    auxinchar = '\0' ;
		    auxEscSeqP = auxEscSeq ;
		    while (!nextauxinchar)
			;
		    while ((nextauxinchar & 0x7f) != '~'
					&& (nextauxinchar & 0x7f) != 'r')
		    {
			*auxEscSeqP++ = nextauxinchar ;
			nextauxinchar = '\0' ;
			QueueRead (auxreadReq, &nextauxinchar) ;
			while (!nextauxinchar)
			    ;
		    }
		    *auxEscSeqP++ = nextauxinchar ;
		    *auxEscSeqP = '\0' ;
		    nextauxinchar = '\0' ;
		    QueueRead (auxreadReq, &nextauxinchar) ;
		    switch (auxEscSeq [0])
		    {
			int flag, h, w ;
			case '1' :		/* Window size ESC seq	*/
			    flag = FALSE ;
			    auxEscSeqP = auxEscSeq + 4 ;
			    sscanf (auxEscSeqP, "%d", &h) ;
			    if (h != auxheight)
			    {
				auxheight = h ;
				flag = TRUE ;
			    }
			    while (*auxEscSeqP++ != ';')
				;
			    sscanf (auxEscSeqP, "%d", &w) ;
			    if (w != auxwidth)
			    {
				auxwidth = w ;
				flag = TRUE ;
			    }
			    if (flag)
			    {
				int i, j, k = auxfresh_i++ ;
				auxfresh_i &= FRESH_MASK ;
				for (i = 0, j = 0 ; auxfresh [i]
						&& i < FRESH_SIZE ; i++)
				    if ((auxfresh [i] & 0177) == 012)
					j++ ;
				if (j < h)
				    h = j ;
				for (i = h ; i ; i--, auxfresh_i
					= --auxfresh_i & FRESH_MASK)
				    while (auxfresh [auxfresh_i] != '\n')
					auxfresh_i =
						--auxfresh_i & FRESH_MASK ;
				j = (h > 1) ? 2 : -1 ;
				confresh_i = (confresh_i + j)
							& FRESH_MASK ;
				strcpy (auxfreshout, "\x9bH\x9bJ") ;
				i = 4 ;
				while (k != auxfresh_i)
				{
				    if (w = auxfresh [auxfresh_i++])
					auxfreshout [i++] = w ;
				    auxfresh_i &= FRESH_MASK ;
				}
				auxfreshout [i] = '\0' ;
				auxwriteReq -> io_Data
						= (APTR) auxfreshout ;
				auxwriteReq -> io_Length = -1 ;
				DoIO ((struct IORequest *) auxwriteReq) ;
			    }
			    break ;
			default :
			    break ;
		    }
		    auxinchar = nextauxinchar ;
		    nextauxinchar = '\0' ;
		    QueueRead (auxreadReq, &nextauxinchar) ;
		}
		else
		    KeyReady = TRUE ;
	    }
#endif
#ifdef MS_DOS
	    if (aux_vdu && auxinchar)
		KeyReady = TRUE ;
#endif
	default :
	    break ;
    }
    return KeyReady ;
}

/* -------------------------------------------------------------------- */
/*
 *	Get a character from a keyboard.
 */
int GetChar (int ioaddr, int clear)
{
    int retval = 0 ;

    switch (ioaddr)
    {
	case 003 :
	    if (coninchar)
	    {
		retval = coninchar ;
		if (clear)
		    coninchar = '\0' ;
	    }
	    break ;
	case 040 :
	    if (aux_vdu && auxinchar)
	    {
		retval = auxinchar ;
		if (clear)
		    auxinchar = '\0' ;
	    }
	    break ;
	default :
	    retval = '\0' ;
    }
    return retval ;
}
/* -------------------------------------------------------------------- */
/*
 *	Output a character to a VDU.
 */
void PutChar (int ioaddr, unsigned char charact)
{
    switch (ioaddr)
    {
	case 004 :
	    conoutchar = charact ;
	    if (conoutchar != 0x9b)
		conoutchar &= 0177 ;
	    if (conoutchar && (conoutchar == 014
			|| conoutchar == 025 || conoutchar == 0177))
		conoutchar = 0 ;    /* Do not display ^L, ^U, rubout	*/
#ifdef AMIGA
	    if (conoutchar)
	    {
		conwriteReq -> io_Data = (APTR) &conoutchar ;
		conwriteReq -> io_Length = 1 ;
		DoIO ((struct IORequest *) conwriteReq) ;
		confresh [confresh_i++] = conoutchar ;
		confresh_i &= FRESH_MASK ;
		if (pdp8conout)
		    fputc (conoutchar, pdp8conout) ;
	    }
#endif
#ifdef MS_DOS
	    if (conoutchar > 037)
		charcnt += escflag ;	/* Count printing characters	*/
	    else
		charcnt-- ;		/* Decrement count if backspace	*/
	    else if (conoutchar == 015)
	    {
		if (charcnt)
		    auto_lf = 0 ;	/* Reset switch if cnt != 0	*/
		charcnt = 0 ;		/* Reset counter after a CR	*/
		escflag = 1 ;		/*	and terminate ESC seq	*/
	    }
	    else if (conoutchar == 012 && auto_lf)
		conoutchar = auto_lf = 0 ;
	    if (charcnt >= SCRNSIZE)	/* Did BIOS start new line ?	*/
	    {
		charcnt = 0 ;		/* Mimic automatic CR/LF to	*/
		auto_lf = 1 ;		/*	prevent double spacing	*/
	    }
	    if (conoutchar)
	    {
		bdos (2, conoutchar, 0) ;    /* DOS char out - ANSI.SYS	*/
		if (pdp8conout)
		    fputc (conoutchar, pdp8conout) ;
	    }
#endif
	    break ;
	case 041 :
	    if (aux_vdu)
	    {
#ifdef AMIGA
		if (auxoutchar != 0x9b)
		    auxoutchar = charact & 0177 ;
		auxwriteReq -> io_Command = CMD_WRITE ;
		auxwriteReq -> io_Data = (APTR) &auxoutchar ;
		auxwriteReq -> io_Length = 1 ;
		DoIO ((struct IORequest *) auxwriteReq) ;
		auxfresh [auxfresh_i++] = auxoutchar ;
		auxfresh_i &= FRESH_MASK ;
#endif
#ifdef MS_DOS
	    /* FIX: replace with aux-handling code ; JvZ	*/
#endif
		if (pdp8auxout)
		    fputc (auxoutchar, pdp8auxout) ;
	    }
	    break ;
	default :
	    break ;
    }
}
/* -------------------------------------------------------------------- */
/*
 *
 */
static void Exeunt (char *s, int retValue)
{
    if (*s)
	printf ("%s\n", s) ;
#ifdef AMIGA
    if (aux_vdu && (auxreadReq || auxwriteReq))
    {
	if (auxreadReq)
	{
	    if (!CheckIO ((struct IORequest *) auxreadReq))
		AbortIO ((struct IORequest *) auxreadReq) ;
	    WaitIO ((struct IORequest *) auxreadReq) ;
	}
	CloseDevice ((struct IORequest *) auxwriteReq) ;
	if ((struct IORequest *) auxreadReq)
	    DeleteExtIO ((struct IORequest *) auxreadReq) ;
	auxreadReq = NULL ;
	if (auxreadPort)
	    DeletePort (auxreadPort) ;
	auxreadPort = NULL ;
	if (auxwriteReq)
	    DeleteExtIO ((struct IORequest *) auxwriteReq) ;
	auxwriteReq = NULL ;
	if (auxwritePort)
	    DeletePort (auxwritePort) ;
	auxwritePort = NULL ;
    }
    if (conreadReq || conwriteReq)
    {
	if (conreadReq)
	{
	    if (!CheckIO ((struct IORequest *) conreadReq))
		AbortIO ((struct IORequest *) conreadReq) ;
	    WaitIO ((struct IORequest *) conreadReq) ;
	}
	CloseDevice ((struct IORequest *) conwriteReq) ;
	if (conreadReq)
	    DeleteExtIO ((struct IORequest *) conreadReq) ;
	conreadReq = NULL ;
	if (conreadPort)
	    DeletePort (conreadPort) ;
	conreadPort = NULL ;
	if (conwriteReq)
	    DeleteExtIO ((struct IORequest *) conwriteReq) ;
	conwriteReq = NULL ;
	if (conwritePort)
	    DeletePort (conwritePort) ;
	conwritePort = NULL ;
    }
    if (aux_vdu && auxwindow)
	CloseWindow (auxwindow) ;
    auxwindow = NULL ;
    if (conwindow)
	CloseWindow (conwindow) ;
    conwindow = NULL ;
    if (IntuitionBase)
	CloseLibrary ((struct Library *) IntuitionBase) ;
    IntuitionBase = NULL ;
#endif
/*
 *	MSDOS: Nothing to do.
 */
    exit (retValue) ;
}
/* -------------------------------------------------------------------- */
/*
 *	Open PDP8 console keyboard file.
 */
void local_init (void)
{
#ifdef AMIGA
    struct NewWindow *nw ;

/*
 *	Allocate memory for NewWindow structure.
 */
    if (!(nw = (struct NewWindow *)
				malloc (sizeof (struct NewWindow))))
	exit (4) ;
/*
 *	Open Intuition library.
 */
    if (!(IntuitionBase = (struct IntuitionBase *)
		OpenLibrary ((char *) "intuition.library", 33)))
	Exeunt ((char *) "Could not open 'intuition.library'", 1) ;
/*
 *	Initialize the PDP8 Aux console NewWindow structure.
 */
    nw -> LeftEdge = 0 ;
    nw -> TopEdge = 100 ;
    nw -> Width = 640 ;
    nw -> Height = (aux_vdu) ? 100 : 200 ;
    nw -> DetailPen = (unsigned char) -1 ;
    nw -> BlockPen = (unsigned char) -1 ;
    nw -> IDCMPFlags = 0 ;
    nw -> Flags = WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | WINDOWSIZING
		| SMART_REFRESH | RMBTRAP ;
    nw -> FirstGadget = NULL ;
    nw -> CheckMark = NULL ;
    nw -> Title = (char *) "PDP8 Aux VDU" ;
    nw -> Screen = NULL ;
    nw -> BitMap = NULL ;
    nw -> MinWidth = 320 ;
    nw -> MinHeight = 80 ;
    nw -> MaxWidth = (unsigned char) ~0 ;
    nw -> MaxHeight = (unsigned char) ~0 ;
    nw -> Type = WBENCHSCREEN ;
/*
 *	Open the PDP8 Aux VDU window.
 */
    if (aux_vdu)
	if (!(auxwindow = OpenWindow (nw)))
	    Exeunt ((char *) "Could not open Aux window", 2) ;
/*
 *	Initialize the PDP8 Console NewWindow structure.
 */
	nw -> Title = (char *) "PDP8 Console" ;
/*
 *	Open the PDP8 Console window.
 */
    nw -> TopEdge = 0 ;
    if (!(conwindow = OpenWindow (nw)))
	Exeunt ((char *) "Could not open Console window", 2) ;
    free (nw) ;
/*
 *	Open the PDP8 Aux VDU device.
 */
    if (aux_vdu)
    {
	if (!(auxwritePort = CreatePort ((char *) "RKM.console.write",0)))
	    Exeunt ((char *) "Could not create Aux VDU write port", 5) ;
	if (auxwriteReq = (struct IOStdReq *) CreateExtIO (auxwritePort,
			(long) sizeof (struct IOStdReq)))
	    auxwriteReq -> io_Command = CMD_WRITE ;
	else
	    Exeunt ((char *) "Could not create Aux VDU write request", 6) ;
	if (!(auxreadPort = CreatePort ((char *) "RKM.console.read",0)))
	    Exeunt ((char *) "Could not create Aux VDU read port", 5) ;
	if (!(auxreadReq = (struct IOStdReq *) CreateExtIO (auxreadPort,
			(long) sizeof (struct IOStdReq))))
	    Exeunt ((char *) "Could not create Aux VDU write request", 6) ;
	if (OpenConsole (auxwriteReq, auxreadReq, auxwindow))
	    Exeunt ((char *) "Could not open PDP8 Aux VDU device", 9) ;
/*
 *	Queue Aux VDU read.
 */
	QueueRead (auxreadReq, &nextauxinchar) ;
    }
/*
 *	Open the PDP8 Console device.
 */
    if (!(conwritePort = CreatePort ((char *) "RKM.console.write",0)))
	Exeunt ((char *) "Could not create Console write port", 5) ;
    if (conwriteReq = (struct IOStdReq *) CreateExtIO (conwritePort,
		(long) sizeof (struct IOStdReq)))
	conwriteReq -> io_Command = CMD_WRITE ;
    else
	Exeunt ((char *) "Could not create Console write request", 6) ;
    if (!(conreadPort = CreatePort ((char *) "RKM.console.read",0)))
	Exeunt ((char *) "Could not create Console read port", 5) ;
    if (!(conreadReq = (struct IOStdReq *) CreateExtIO (conreadPort,
		(long) sizeof (struct IOStdReq))))
	Exeunt ((char *) "Could not create Console write request", 6) ;
    if (OpenConsole (conwriteReq, conreadReq, conwindow))
	Exeunt ((char *) "Could not open PDP8 Console device", 9) ;
/*
 *	Queue Console read.
 */
    QueueRead (conreadReq, &nextconinchar) ;
#endif
/*
 *	MSDOS: Nothing to do.
 */
}

/* -------------------------------------------------------------------- */
/*
 *	Close PDP8 VDU devices.
 */
void local_uninit (void)
{
    Exeunt ((char *) "", 0) ;
}
/* -------------------------------------------------------------------- */
