/*
 *  Modifications to existing pc.c module are --
 *
 *  Copyright © 1987, 1989, 1990
 *  Louis A. Mamakos, WA3YMH
 *
 *  This work, or any derivations thereof, may be used for non-commercial
 *  purposes only.  So there.
 */

/* OS- and machine-dependent stuff for the Commodore-Amiga 500/1000/2000 */

#define	AMIGAVERSION	"$Revision: 1.3 $"

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/errors.h>
#include <devices/timer.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <workbench/startup.h>

#ifdef	LATTICE
#include <proto/exec.h>
#include <proto/dos.h>
#endif

#include <stdio.h>
#ifndef AZTEC_C
#include <dos.h>
#endif

#include "global.h"
#include "config.h"
#include "files.h"
#include "hardware.h"
#include "mbuf.h"
#include "socket.h"
#include "internet.h"
#include "iface.h"
#include "cmdparse.h"
#include "commands.h"
#ifdef	ASY
#include "asy.h"
#include "amiga/serdev.h"
#endif
#include "slip.h"
#include "timer.h"
#include "proc.h"
#include "session.h"
#include "amiga/windows.h"
#include "stat.h"

/* define 0 to use WAIT_CHAR */
#define	USE_READ	1

const static char *copyright_notice = {
   "AMIGA port of KA9Q TCP/IP\r\n"
   "(C) Copyright 1987,1989,1990 Louis A. Mamakos WA3YMH\r\n"
   "for non-commercial, non-profit use only.  All other rights reserved\r\n"};

#define ITALICS "\033[3m"
#define PLAIN "\033[0m"

static char RCSHEADER[] = "$Header: DUA1:Projects/ka9q/amiga.new/amiga/amiga.c,v 1.2 90/01/13 09:40:55 louie Exp Locker: louie $";

static char *Banner = {
	ITALICS 
	"[Amiga port (using Lattice C), version " 
	PLAIN "\337" AMIGAVERSION ITALICS "\n"
	"Copyright \251 1989,1990 by Louis A. Mamakos, WA3YMH]\n" PLAIN
};


struct Keyboard Keyboard;
static void check_time(void), doit(register ULONG what);


#ifdef COMMENT
ULONG __saveds __asm
except_handler(register __d0 ULONG mask,
	       register __a1 void *dataptr);
#endif /*COMMENT*/

/*
 *  This is the exception handler.  It is invoked whenever a signal bit is
 *  set among those in except_mask.  We're kinda limited in what can be
 *  done here..
 */
extern except_handler();
#asm
	xref	_geta4

_except_handler:
	move.l a4,-(sp)
	jsr	_geta4
	move.l	d0,_pollmask
	move.l	(sp)+,a4
	rts
#endasm

#ifdef COMMENT
ULONG __saveds __asm
except_handler(register __d0 ULONG mask,
	       register __a1 void *dataptr)
{
	pollmask = mask;
	return mask;
}
#endif /* COMMENT */

static APTR oldwindowptr;
static BPTR current_dir;
static struct Task *mytask;
static struct MsgPort  *timerp;
static struct timerequest tr;
static char timeropen, windowsopen;
static ULONG pollmask, waitmask, timerbit, intuitionbit, except_mask;

/* in alloc.c */
extern int Memwait;
/* ---------- */

extern struct WBStartup *WBenchMsg;

void
clean(why)
	char *why;
{
	(void) SetExcept(0L, except_mask);
	mytask->tc_ExceptCode = (APTR) NULL;
	mytask->tc_ExceptData = (APTR) NULL;

	if (windowsopen)
		term_windows();
	if (timeropen)
		CloseDevice((struct IORequest *) &tr);
	if (timerp)
		DeletePort(timerp);

	((struct Process *)mytask)->pr_WindowPtr = oldwindowptr;

	if (why) {
		fputs(why, stderr);
		fputs("\n", stderr);
	}
	exit(0);
}

/* Called at startup time to set up console I/O, memory heap */
void
ioinit(mem)
int32 mem;
{
	char *path;
	extern char *Fdir, *pathname(char *, char *);

	/*
	 *  Get a new lock on the current directory, and save the original
	 *  away for when we exit this program.  We should "always" be able
	 *  to get a lock on the current directory.
	 */
	current_dir = CurrentDir(Lock("", SHARED_LOCK));

	mytask = FindTask((char *) NULL);
	mytask->tc_ExceptCode = (APTR) except_handler;
	mytask->tc_ExceptData = (APTR) "KA9Q NOS";

	/* disable DOS requestors */
	oldwindowptr = ((struct Process *)mytask)->pr_WindowPtr;
	((struct Process *)mytask)->pr_WindowPtr = (APTR) -1;

	if ((timerp = CreatePort("net:timer", 0L)) == NULL)
		clean("Can't create timer port");

	if ((windowsopen = init_windows()) == 0)
		clean("Can't initialize windows");

	Keyboard.wp = Keyboard.rp = &Keyboard.buf[0];
	Keyboard.cnt = 0;

	/* create and start up timer */
	tr.tr_node.io_Message.mn_ReplyPort = timerp;
	if (OpenDevice("timer.device", UNIT_VBLANK,	/* UNIT_MICROHZ ?? */
				(struct IORequest *) &tr, 0L) != 0L)
		clean("Can't open timer");
	timeropen++;
	tr.tr_node.io_Command = TR_GETSYSTIME;
	DoIO((struct IORequest *)&tr);
	tr.tr_node.io_Command = TR_ADDREQUEST;
	tr.tr_time.tv_secs = 0;
	tr.tr_time.tv_micro = MSPTICK*1000L;	/* convert to microseconds */
	buildwaitmask();

	except_mask |= timerbit;
	(void) SetExcept(except_mask, except_mask);
	SendIO((struct IORequest *) &tr);
	
	if (WBenchMsg != (struct WBStartup *)NULL && WBenchMsg->sm_NumArgs) {
		/* we were started from workbench! */
		struct WBArg *arg = WBenchMsg->sm_ArgList;
		
		if (WBenchMsg->sm_NumArgs > 1) {
			/* if he specified a Project icon when we were invoked,
			   then its the name of the startup file to be run */
			arg++;
			Startup = arg->wa_Name;
		}
		UnLock(CurrentDir(arg->wa_Lock));
		/*
		 * I suppose that at this point, I could grovel through the
		 * tooltypes and stuff in the icon, but I don't quite know 
		 * why I would yet.
		 */
	}
	/*
	 *  Compute absolute paths of various files.
	 */
	if (path = pathname("", Fdir)) {
		Fdir = path;
	} else
		printf("\033[1mWARNING:\033[0m Can't find path for %s\n", Fdir);

	puts(Banner);
}

/* Called just before exiting to restore console state */
void
iostop(void)
{
	while(Ifaces != NULLIF){
		if(Ifaces->stop != NULLFP)
			(*Ifaces->stop)(Ifaces);
		Ifaces = Ifaces->next;
	}

	/*
	 *  Trash the current directory and restore it to the one that we
	 *  originally started with.
	 */
	UnLock(CurrentDir(current_dir));
	clean((char *)0);
}

	
void
buildwaitmask(void) {
#ifdef	ASY
	register struct asy *asy;
#endif
	timerbit = 1L << timerp->mp_SigBit;
	intuitionbit = 1L << WND_msgport->mp_SigBit;
	waitmask = timerbit | intuitionbit;
#ifdef	ASY	
	for (asy = &Asy[0]; asy < &Asy[Nasy]; asy++)
		waitmask |= asy->in_sigbit | asy->out_sigbit;
#endif
}


/*
 *  This function is invoked whenever something "Amiga" has happened.
 */
static void
doit(register ULONG what)
{
#ifdef	ASY
	register struct asy *asy;
#endif
	if (what & timerbit)
		check_time();

	if (what & intuitionbit)
		check_WND_event();

#ifdef	ASY
	for (asy = &Asy[0]; asy < &Asy[Nasy]; asy++) {
		if (what & asy->in_sigbit)
			check_serin(asy);
		if (what & asy->out_sigbit)
			check_serout(asy);
	}
#endif
}


/*
 *  take a quick peek to see if something happened.
 */
void
poll(void)
{
	register ULONG what;

	what = SetSignal(0L, 0L) | pollmask;
	pollmask = 0;
	if ((what & waitmask) != 0) {
		SetSignal(0L, what);
		doit(what);
	}
}

/*
 * wait for something to happen
 */
void
giveup(void)
{
	register ULONG what;

	if (pollmask) {
		what = pollmask;
		pollmask = 0;
	} else
		what = Wait(waitmask);

	if (what & waitmask)
		doit(what);
}

/* checks the time then ticks and updates ISS */
int Tick;

static void
check_time(void)
{
	if (CheckIO((struct IORequest *) &tr)) {
		WaitIO((struct IORequest *) &tr);
		(void) GetMsg(timerp);
		Tick++;
		psignal(&Tick, 1);
		tr.tr_time.tv_secs = 0;
		tr.tr_time.tv_micro = MSPTICK*1000L;
					/* convert to microseconds */
		SendIO((struct IORequest *) &tr);
	}
}

void
systick(void) {
	static int wait = 0;
	static short int waitidx = 0;
	static int waitinterval[] =
		{0, 50/MSPTICK, 100/MSPTICK, 250/MSPTICK, 500/MSPTICK,
		 1000/MSPTICK, 1250/MSPTICK, 2000/MSPTICK, 4000/MSPTICK};
	/*
	 *  This is sort of a crock, but on the Amiga many other processes
	 *  are contending for memory on the system.  If we've got processes
	 *  waiting for memory, poke 'em every so often to see if maybe some
	 *  had freed up.
	 *
	 *  Now, checking for available memory is likely a relatively 
	 *  expensive operation since interrupts need to be disabled.
	 *  Taking a cue from TCP, we'll do an exponential
	 *  back-off polling for more memory.  The longer we're out of
	 *  memory, the longer we'll likely stay out of memory.
	 */
	if (Memwait) {
		if (waitinterval[waitidx] <= wait) {
			/* if current wait interval has expired, poke */
			psignal(&Memwait, 0);
			wait = 0;
			waitidx++;
			if (waitidx >= sizeof(waitinterval)/sizeof(int))
				waitidx = sizeof(waitinterval)/sizeof(int) - 1;
		}
		wait += MSPTICK;	/* bump wait interval */
	} else {
		if (waitidx) {
			/*
			 *  We had been waiting, but memory has freed up.  Reset
			 *  all the state variables for the next time.
			 */
			wait = 0;
			waitidx = 0;
		}
	}
	conflsh();
}

int
kbread(void) {
	int c;

	while (Keyboard.cnt == 0)
		pwait(&Keyboard);

	c = *Keyboard.rp++;
	if (Keyboard.rp == &Keyboard.buf[KBUFSIZE])
		Keyboard.rp = Keyboard.buf;
	Keyboard.cnt--;
	return c;
}
	
static short int Virtual_Interrupt_Enable = 1;

int
istate(void) {
	return (int)Virtual_Interrupt_Enable;
}

int
dirps(void) {
	int prior = Virtual_Interrupt_Enable;

	if (Virtual_Interrupt_Enable) {
		/*
		 * when the virtual interrupts are disabled, turn off
		 * exception processing.
		 */
		(void) SetExcept(0L, except_mask);

		/* disable virtual interrupt enable */
		Virtual_Interrupt_Enable = 0;

	}
	return prior;
}

void
restore(int foo)
{
	if (foo && !Virtual_Interrupt_Enable) {
		Virtual_Interrupt_Enable = 1;
		/* check to see if we've got pending Amiga events */
		poll();

		SetExcept(except_mask, except_mask);
	}
}

long
ptol(void *ptr)
{
	return (long) ptr;
}

void *
ltop(long l)
{
	return (void *)l;
}


/*
 *  This is the exception handler.  It is invoked whenever a signal bit is
 *  set among those in except_mask.  We're kinda limited in what can be
 *  done here..
 */
#ifdef COMMENT
ULONG __saveds __asm
except_handler(register __d0 ULONG mask,
	       register __a1 void *dataptr)
{
	pollmask = mask;
	return mask;
}
#endif /* COMMENT */

int stat(name,buf)
char	*name;
struct stat *buf;
{
	long l;
	struct FileInfoBlock *fp;

	if ((l=Lock(name, ACCESS_READ)) == 0)
		return(-1);
	fp = malloc(sizeof(struct FileInfoBlock));
	if (fp == NULL)
		return -1;
	Examine(l, fp);
	buf->st_attr = fp->fib_Protection;
	buf->st_size = fp->fib_Size;
	buf->st_type = fp->fib_DirEntryType;
	UnLock(l);
	free(fp);
	buf->st_ctime = buf->st_mtime = 0; /*getft(name);*/
	return 0;
}

extern long amiga_command_key_code;
extern short amiga_winsizes[];
extern short amiga_winmode;
static int do_command_key __ARGS((int argc, char *argv[], void *p));
static int do_windim __ARGS((int argc, char *argv[], void *p));

struct cmds AmigaCmds[] = {
	"command_key",	do_command_key,	0, 0, "command_key <scancode>",
	"window_dimentions", do_windim,	0, 0,
		"window <left> <top> <width> <height>",
	NULLCHAR,	NULLFP,		0, 0, "Unknown command",
};

int
doamiga(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(AmigaCmds, argc, argv, p);
}

static int
do_command_key(argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setlong(&amiga_command_key_code, "Command keyboard scan code",
		argc, argv);
}

static int
do_windim(argc, argv, p)
int argc;
char *argv[];
void *p;
{
	if (argc != 5) {
		tprintf("Session window left edge: %d  top edge: %d\n",
			amiga_winsizes[0], amiga_winsizes[1]);
		tprintf("               width: %d  height: %d\n",
			amiga_winsizes[2], amiga_winsizes[3]);
	}
	return 0;
}
