#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern extern
#include "mips.h"

#define	REGSP	29
#define	REGRET	1

#define	ODIRLEN	116	/* compatibility; used in _stat etc. */
#define	OERRLEN	64	/* compatibility; used in _stat etc. */

char 	errbuf[ERRMAX];
ulong	nofunc;

#include "/sys/src/libc/9syscall/sys.h"

char *sysctab[]={
	[SYSR1]		"SYSR1",
	[_ERRSTR]	"_errstr",
	[BIND]		"Bind",
	[CHDIR]		"Chdir",
	[CLOSE]		"Close",
	[DUP]		"Dup",
	[ALARM]		"Alarm",
	[EXEC]		"Exec",
	[EXITS]		"Exits",
	[_FSESSION]	"_Fsession",
	[FAUTH]		"Fauth",
	[_FSTAT]	"_fstat",
	[SEGBRK]	"Segbrk",
	[_MOUNT]	"_Mount",
	[OPEN]		"Open",
	[_READ]		"_Read",
	[OSEEK]		"Oseek",
	[SLEEP]		"Sleep",
	[_STAT]		"_Stat",
	[RFORK]		"Rfork",
	[_WRITE]	"_Write",
	[PIPE]		"Pipe",
	[CREATE]	"Create",
	[FD2PATH]	"Fd2path",
	[BRK_]		"Brk_",
	[REMOVE]	"Remove",
	[_WSTAT]	"_Wstat",
	[_FWSTAT]	"_Fwstat",
	[NOTIFY]	"Notify",
	[NOTED]		"Noted",
	[SEGATTACH]	"Segattach",
	[SEGDETACH]	"Segdetach",
	[SEGFREE]	"Segfree",
	[SEGFLUSH]	"Segflush",
	[RENDEZVOUS]	"Rendezvous",
	[UNMOUNT]	"Unmount",
	[_WAIT]		"Wait",
	[SEEK]		"Seek",
	[FVERSION]	"Fversion",
	[ERRSTR]	"Errstr",
	[STAT]		"Stat",
	[FSTAT]		"Fstat",
	[WSTAT]		"Wstat",
	[FWSTAT]	"Fwstat",
	[MOUNT]		"Mount",
	[AWAIT]		"Await",
	[PREAD]		"Pread",
	[PWRITE]	"Pwrite",
};

void sys1(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0); }

void
sys_errstr(void)
{
	ulong str;

	str = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("errstr(0x%lux)", str);

	memio(errbuf, str, OERRLEN, MemWrite);
	strcpy(errbuf, "no error");
	reg.r[REGRET] = 0;
	
}

void
syserrstr(void)
{
	ulong str;
	uint n;

	str = getmem_w(reg.r[REGSP]+4);
	n = getmem_w(reg.r[REGSP]+8);
	if(sysdbg)
		itrace("errstr(0x%lux, 0x%lux)", str, n);

	if(n > strlen(errbuf)+1)
		n = strlen(errbuf)+1;
	memio(errbuf, str, n, MemWrite);
	strcpy(errbuf, "no error");
	reg.r[REGRET] = n;
	
}

void
sysfd2path(void)
{
	int n;
	uint fd;
	ulong str;
	char buf[1024];

	fd = getmem_w(reg.r[REGSP]+4);
	str = getmem_w(reg.r[REGSP]+8);
	n = getmem_w(reg.r[REGSP]+12);
	if(sysdbg)
		itrace("fd2path(0x%lux, 0x%lux, 0x%lux)", fd, str, n);
	reg.r[REGRET] = -1;
	if(n > sizeof buf){
		strcpy(errbuf, "buffer too big");
		return;
	}
	n = fd2path(fd, buf, sizeof buf);
	if(n < 0)
		errstr(buf, sizeof buf);
	else
		memio(errbuf, str, n, MemWrite);
	reg.r[REGRET] = n;
	
}

void
sysbind(void)
{
	ulong pname, pold, flags;
	char name[1024], old[1024];
	int n;

	pname = getmem_w(reg.r[REGSP]+4);
	pold = getmem_w(reg.r[REGSP]+8);
	flags = getmem_w(reg.r[REGSP]+12);
	memio(name, pname, sizeof(name), MemReadstring);
	memio(old, pold, sizeof(old), MemReadstring);
	if(sysdbg)
		itrace("bind(0x%lux='%s', 0x%lux='%s', 0x%lux)", name, name, old, old, flags);

	n = bind(name, old, flags);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);

	reg.r[REGRET] = n;
}

void
syschdir(void)
{ 
	char file[1024];
	int n;
	ulong name;

	name = getmem_w(reg.r[REGSP]+4);
	memio(file, name, sizeof(file), MemReadstring);
	if(sysdbg)
		itrace("chdir(0x%lux='%s', 0x%lux)", name, file);
	
	n = chdir(file);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);

	reg.r[REGRET] = n;
}

void
sysclose(void)
{
	int n;
	ulong fd;

	fd = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("close(%d)", fd);

	n = close(fd);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	reg.r[REGRET] = n;
}

void
sysdup(void)
{
	int oldfd, newfd;
	int n;

	oldfd = getmem_w(reg.r[REGSP]+4);
	newfd = getmem_w(reg.r[REGSP]+8);
	if(sysdbg)
		itrace("dup(%d, %d)", oldfd, newfd);

	n = dup(oldfd, newfd);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	reg.r[REGRET] = n;
}

void
sysexits(void)
{
	char buf[OERRLEN];
	ulong str;

	str = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("exits(0x%lux)", str);

	count = 1;
	if(str != 0) {
		memio(buf, str, sizeof buf, MemRead);
		Bprint(bioout, "exits(%s)\n", buf);
	}
	else
		Bprint(bioout, "exits(0)\n");
}

void
sysopen(void)
{
	char file[1024];
	int n;
	ulong mode, name;

	name = getmem_w(reg.r[REGSP]+4);
	mode = getmem_w(reg.r[REGSP]+8);
	memio(file, name, sizeof(file), MemReadstring);
	if(sysdbg)
		itrace("open(0x%lux='%s', 0x%lux)", name, file, mode);
	
	n = open(file, mode);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);

	reg.r[REGRET] = n;
};

void
sysread(vlong offset)
{
	int fd;
	ulong size, a;
	char *buf, *p;
	int n, cnt, c;

	fd = getmem_w(reg.r[REGSP]+4);
	a = getmem_w(reg.r[REGSP]+8);
	size = getmem_w(reg.r[REGSP]+12);

	buf = emalloc(size);
	if(fd == 0) {
		print("\nstdin>>");
		p = buf;
		n = 0;
		cnt = size;
		while(cnt) {
			c = Bgetc(bin);
			if(c <= 0)
				break;
			*p++ = c;
			n++;
			cnt--;
			if(c == '\n')
				break;
		}
	}
	else
		n = pread(fd, buf, size, offset);

	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	else
		memio(buf, a, n, MemWrite);

	if(sysdbg)
		itrace("read(%d, 0x%lux, %d, 0x%llx) = %d", fd, a, size, offset, n);

	free(buf);
	reg.r[REGRET] = n;
}

void
sys_read(void)
{
	sysread(-1LL);
}

void
syspread(void)
{
	union {
		vlong v;
		ulong u[2];
	} o;

	o.u[0] = getmem_w(reg.r[REGSP]+16);
	o.u[1] = getmem_w(reg.r[REGSP]+20);
	sysread(o.v);
}

void
sysseek(void)
{
	int fd;
	ulong mode;
	ulong retp;
	union {
		vlong v;
		ulong u[2];
	} o;

	retp = getmem_w(reg.r[REGSP]+4);
	fd = getmem_w(reg.r[REGSP]+8);
	o.u[0] = getmem_w(reg.r[REGSP]+12);
	o.u[1] = getmem_w(reg.r[REGSP]+16);
	mode = getmem_w(reg.r[REGSP]+20);
	if(sysdbg)
		itrace("seek(%d, %lld, %d)", fd, o.v, mode);

	o.v = seek(fd, o.v, mode);
	if(o.v < 0)
		errstr(errbuf, sizeof errbuf);	

	memio((char*)o.u, retp, sizeof(vlong), MemWrite);
}

void
sysoseek(void)
{
	int fd, n;
	ulong off, mode;

	fd = getmem_w(reg.r[REGSP]+4);
	off = getmem_w(reg.r[REGSP]+8);
	mode = getmem_w(reg.r[REGSP]+12);
	if(sysdbg)
		itrace("seek(%d, %lud, %d)", fd, off, mode);

	n = seek(fd, off, mode);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);	

	reg.r[REGRET] = n;
}

void
sysrfork(void)
{
	int flag;

	flag = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("rfork(%d)", flag);
	if(flag & RFPROC) {
		Bprint(bioout, "rfork: cannot create process, rfork(0x%.8ux)\n", flag);
		exits(0);
	}
	reg.r[REGRET] = rfork(flag);
}

void
syssleep(void)
{
	ulong len;
	int n;

	len = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("sleep(%d)", len);

	n = sleep(len);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);	

	reg.r[REGRET] = n;
}

void
sys_stat(void)
{
	char nambuf[1024];
	char buf[ODIRLEN];
	ulong edir, name;
	extern int _stat(char*, char*);	/* old system call */
	int n;

	name = getmem_w(reg.r[REGSP]+4);
	edir = getmem_w(reg.r[REGSP]+8);
	memio(nambuf, name, sizeof(nambuf), MemReadstring);
	if(sysdbg)
		itrace("stat(0x%lux='%s', 0x%lux)", name, nambuf, edir);

	n = _stat(nambuf, buf);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	else
		memio(buf, edir, ODIRLEN, MemWrite);

	reg.r[REGRET] = n;
}

void
sysstat(void)
{
	char nambuf[1024];
	uchar buf[STATMAX];
	ulong edir, name;
	int n;

	name = getmem_w(reg.r[REGSP]+4);
	edir = getmem_w(reg.r[REGSP]+8);
	n = getmem_w(reg.r[REGSP]+12);
	memio(nambuf, name, sizeof(nambuf), MemReadstring);
	if(sysdbg)
		itrace("stat(0x%lux='%s', 0x%lux, 0x%lux)", name, nambuf, edir, n);
	if(n > sizeof buf)
		errstr(errbuf, sizeof errbuf);
	else{	
		n = stat(nambuf, buf, n);
		if(n < 0)
			errstr(errbuf, sizeof errbuf);
		else
			memio((char*)buf, edir, n, MemWrite);
	}
	reg.r[REGRET] = n;
}

void
sys_fstat(void)
{
	char buf[ODIRLEN];
	ulong edir;
	extern int _fstat(int, char*);	/* old system call */
	int n, fd;

	fd = getmem_w(reg.r[REGSP]+4);
	edir = getmem_w(reg.r[REGSP]+8);
	if(sysdbg)
		itrace("fstat(%d, 0x%lux)", fd, edir);

	n = _fstat(fd, buf);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	else
		memio(buf, edir, ODIRLEN, MemWrite);

	reg.r[REGRET] = n;
}

void
sysfstat(void)
{
	uchar buf[STATMAX];
	ulong edir;
	int n, fd;

	fd = getmem_w(reg.r[REGSP]+4);
	edir = getmem_w(reg.r[REGSP]+8);
	n = getmem_w(reg.r[REGSP]+12);
	if(sysdbg)
		itrace("fstat(%d, 0x%lux, 0x%lux)", fd, edir, n);

	reg.r[REGRET] = -1;
	if(n > sizeof buf){
		strcpy(errbuf, "stat buffer too big");
		return;
	}
	n = fstat(fd, buf, n);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	else
		memio((char*)buf, edir, n, MemWrite);
	reg.r[REGRET] = n;
}

void
syswrite(vlong offset)
{
	int fd;
	ulong size, a;
	char *buf;
	int n;

	fd = getmem_w(reg.r[REGSP]+4);
	a = getmem_w(reg.r[REGSP]+8);
	size = getmem_w(reg.r[REGSP]+12);

	Bflush(bioout);
	buf = memio(0, a, size, MemRead);
	n = pwrite(fd, buf, size, offset);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);	
	if(sysdbg)
		itrace("write(%d, %lux, %d, 0xllx) = %d", fd, a, size, offset, n);
	free(buf);

	reg.r[REGRET] = n;
}

void
sys_write(void)
{
	syswrite(-1LL);
}

void
syspwrite(void)
{
	union {
		vlong v;
		ulong u[2];
	} o;

	o.u[0] = getmem_w(reg.r[REGSP]+16);
	o.u[1] = getmem_w(reg.r[REGSP]+20);
	syswrite(o.v);
}

void
syspipe(void)
{
	int n, p[2];
	ulong fd;

	fd = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("pipe(%lux)", fd);

	n = pipe(p);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	else {
		putmem_w(fd, p[0]);
		putmem_w(fd+4, p[1]);
	}
	reg.r[REGRET] = n;
}

void
syscreate(void)
{
	char file[1024];
	int n;
	ulong mode, name, perm;

	name = getmem_w(reg.r[REGSP]+4);
	mode = getmem_w(reg.r[REGSP]+8);
	perm = getmem_w(reg.r[REGSP]+12);
	memio(file, name, sizeof(file), MemReadstring);
	if(sysdbg)
		itrace("create(0x%lux='%s', 0x%lux, 0x%lux)", name, file, mode, perm);
	
	n = create(file, mode, perm);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);

	reg.r[REGRET] = n;
}

void
sysbrk_(void)
{
	ulong addr, osize, nsize;
	Segment *s;

	addr = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("brk_(0x%lux)", addr);

	reg.r[REGRET] = -1;
	if(addr < memory.seg[Data].base+datasize) {
		strcpy(errbuf, "address below segment");
		return;
	}
	if(addr > memory.seg[Stack].base) {
		strcpy(errbuf, "segment too big");
		return;
	}
	s = &memory.seg[Bss];
	if(addr > s->end) {
		osize = ((s->end-s->base)/BY2PG)*sizeof(uchar*);
		addr = ((addr)+(BY2PG-1))&~(BY2PG-1);
		s->end = addr;
		nsize = ((s->end-s->base)/BY2PG)*sizeof(uchar*);
		s->table = erealloc(s->table, osize, nsize);
	}	

	reg.r[REGRET] = 0;	
}

void
sysremove(void)
{
	char nambuf[1024];
	ulong name;
	int n;

	name = getmem_w(reg.r[REGSP]+4);
	memio(nambuf, name, sizeof(nambuf), MemReadstring);
	if(sysdbg)
		itrace("remove(0x%lux='%s')", name, nambuf);

	n = remove(nambuf);
	if(n < 0)
		errstr(errbuf, sizeof errbuf);
	reg.r[REGRET] = n;
}

void
sysnotify(void)
{
	nofunc = getmem_w(reg.r[REGSP]+4);
	if(sysdbg)
		itrace("notify(0x%lux)\n", nofunc);

	reg.r[REGRET] = 0;
}

void
syssegflush(void)
{
	int n;
	ulong va;

	va = getmem_w(reg.r[REGSP]+4);
	n = getmem_w(reg.r[REGSP]+8);
	if(sysdbg)
		itrace("segflush(va=0x%lux, n=%d)\n", va, n);

	reg.r[REGRET] = 0;
}

void sysfversion(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0); }
void sysfsession(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0); }
void sysfauth(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0); }
void syswait(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0); }
void syswstat(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sys_wstat(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysfwstat(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sys_fwstat(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysnoted(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void syssegattach(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void syssegdetach(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void syssegfree(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysrendezvous(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysunmount(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysfork(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysforkpgrp(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void syssegbrk(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void _sysmount(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysalarm(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysexec(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysmount(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}
void sysawait(void) { Bprint(bioout, "No system call %s\n", sysctab[reg.r[REGRET]]); exits(0);}

void (*systab[])(void)	={
	[SYSR1]		sys1,
	[_ERRSTR]	sys_errstr,
	[BIND]		sysbind,
	[CHDIR]		syschdir,
	[CLOSE]		sysclose,
	[DUP]		sysdup,
	[ALARM]		sysalarm,
	[EXEC]		sysexec,
	[EXITS]		sysexits,
	[_FSESSION]	sysfsession,
	[FAUTH]		sysfauth,
	[_FSTAT]	sys_fstat,
	[SEGBRK]	syssegbrk,
	[_MOUNT]	_sysmount,
	[OPEN]		sysopen,
	[_READ]		sys_read,
	[OSEEK]		sysoseek,
	[SLEEP]		syssleep,
	[_STAT]		sys_stat,
	[RFORK]		sysrfork,
	[_WRITE]	sys_write,
	[PIPE]		syspipe,
	[CREATE]	syscreate,
	[FD2PATH]	sysfd2path,
	[BRK_]		sysbrk_,
	[REMOVE]	sysremove,
	[_WSTAT]	sys_wstat,
	[_FWSTAT]	sys_fwstat,
	[NOTIFY]	sysnotify,
	[NOTED]		sysnoted,
	[SEGATTACH]	syssegattach,
	[SEGDETACH]	syssegdetach,
	[SEGFREE]	syssegfree,
	[SEGFLUSH]	syssegflush,
	[RENDEZVOUS]	sysrendezvous,
	[UNMOUNT]	sysunmount,
	[_WAIT]		syswait,
	[SEEK]		sysseek,
	[FVERSION]	sysfversion,
	[ERRSTR]	syserrstr,
	[STAT]		sysstat,
	[FSTAT]		sysfstat,
	[WSTAT]		syswstat,
	[FWSTAT]	sysfwstat,
	[MOUNT]		sysmount,
	[AWAIT]		sysawait,
	[PREAD]		syspread,
	[PWRITE]	syspwrite,
};

void
Ssyscall(ulong inst)
{
	int call;

	USED(inst);
	call = reg.r[REGRET];
	if(call < 0 || call > PWRITE || systab[call] == nil) {
		Bprint(bioout, "Bad system call\n");
		dumpreg();
	}
	if(trace)
		itrace("sysc\t%s", sysctab[call]);

	(*systab[call])();
	Bflush(bioout);
}
