/*****************************************************************************
 * FILE: rsx.c								     *
 *									     *
 * DESC:								     *
 *	- get rsx options						     *
 *	- switch protected mode (16bit) 				     *
 *	- init protected mode						     *
 *	- check copro, install emu					     *
 *	- load first a.out prg						     *
 *									     *
 * Copyright (C) 1993,1994						     *
 *	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld		     *
 *	email: rainer@mathematik.uni-bielefeld.de			     *
 *									     *
 *****************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "DPMI.H"
#include "PRINTF.H"
#include "PROCESS.H"
#include "START32.H"
#include "LOADPRG.H"
#include "FPU.H"
#include "RSX.H"
#include "DOSERRNO.H"
#include "RMLIB.H"
#include "COPY32.H"
#include "SYSDEP.H"
#include "DJIO.H"
#include "VERSION.H"

/* command-line options */
char copro = 1; 		/* prg need 387 : 0=no 1=yes 3=emulate */
char opt_memaccess = 0; 	/* allows memaccess */
char opt_stack = 0;		/* stack: expand down or data segment */
char rsx387_in_dosmem = 1;	/* rsx387 in dos memory (not rsx32) */
char opt_force_dpmi09 = 1;	/* force only DPMI 0.9 calls */
char opt_version = 0;		/* print rsx version */
char opt_force_copro;		/* force copro status */
int  opt_stackval = 0;		/* stack size in KB */
int  opt_max_dos_handles;	/* DOS handles for RSX */
char opt_nocore;		/* don't write core file */
char opt_schedule;		/* enable schedule() */
int  opt_memalloc = 0;		/* pre-alloc heap */
char opt_zero = 0;		/* zero heap */
char opt_os2 = 0;		/* set os/2 bit */

/* special test options */
char opt_kdeb;			/* Kernel debug mode */
char opt_printall = 0;		/* show all information (testing RSX) */
char opt_print_syscalls = 0;	/* show every sys_call (testing RSX) */
char opt_redir; 		/* redirect handle 1 (testing RSX) */
char opt_debugrsx;		/* debug rsx; don't set int3 */

/* other globals */
char npx_present;		/* npx present 1/0 */
char kdeb_program[80];		/* program for kernel debug mode */

int rsx_stdout = 1;
int kread;			/* keyboard read */
int kready;			/* keyboard check */
unsigned bios_selector; 	/* selector bios area */

char **org_env; 		/* org. environment to rsx */
int org_envc;			/* org. env items */

static int emxl_psp = 0;	/* exe-stub emxl.exe psp */

int hexstr2int(char *s)
{
    int i, res=0;

    for (i = 0; i < 4; i++) {
	char c = s[i];
	if (c >= 'a')
	    c -= ('a' - 10);
	else if (c >= 'A')
	    c -= ('A' - 10);
	else
	    c -= '0';
	res <<= 4;
	res |= c;
    }
    return res;
}

/* return first non-digit */
static char * asc2int(char *s, int *retv)
{
    char *str = s;
    *retv = 0;

    while (*str != 0) {
	if ((*str < '0') || (*str > '9'))
	    break;
	*retv *= 10;
	*retv += *str - '0';
	str++;
    }
    return (str);
}

/*
** get one options for rsx
** return last char pos, if successful
*/
char *scan_for_option(char *s, NEWPROCESS *p)
{
    int temp;
    char *t;

    switch (*s) {

    /*
    **	emx options with emxbind:
    **	-a*, -c, -f#, -h#, -p, -s#, -C#, -L
    */

    case 'a':                   /* DOS features */
	for (++s; *s > ' '; ++s) {
	    if (*s == 'm') {
		if (p)
		    p->options |= OPT_MEMACCESS;
		else
		    opt_memaccess = 1;
	    }
	    else if (*s == 'c' || *s == 'i' || *s == 'w')
		continue;
	    else
		return NULL;
	}
	if (*(--s) == 'a')
	    return NULL;
	break;

    case 'c':                   /* core */
	opt_nocore = 1;
	break;

    case 'e':   /* changed !! */
	puts("-e option for copro has changed to -Re");
	return NULL;

    case 'f':                   /* frame size (ignore) */
    case 's':                   /* stack size (ignore) */
    case 'C':                   /* commit */
	while (isdigit(*++s))
	    ;
	--s;
	break;

    case 'h':                   /* max handles */
	++s;
	if (!isdigit(*s))
	    return NULL;
	t = asc2int(s, &temp);
	if (p && temp > opt_max_dos_handles)
	    rm_sethandles(temp);
	opt_max_dos_handles = temp;
	return t;

    case 'p':                   /* don't use lower DOS mem */
	rsx387_in_dosmem = 0;
	break;

    case 'R':       /* special RSX options */
	for (++s; *s > ' '; ++s) {
	    if (*s == ',')
		++s;
	    switch (*s) {
		case 'a':                   /* ss = ds */
		    opt_stack = 1;
		    break;
		case 'e':                   /* copro */
		    copro = 0;
		    if (isdigit(*(s+1)))
			opt_force_copro = *(++s) - '0' + 1;
		    break;
		case 'm':                   /* malloc for memaccess */
		    if (!isdigit(*(s+1)))
			break;
		    s = asc2int(++s, &opt_memalloc) - 1;
		    break;
		case 's':                   /* stack frame */
		    ++s;
		    if (!isdigit(*s))
			return NULL;
		    return asc2int(s, &opt_stackval);
		case 'x':                   /* enable schedule */
		    opt_schedule = 1;
		    break;
		case 'z':                   /* zero heap */
		    opt_zero = 1;
		    break;

		case '1':                   /* no dpmi10 calls */
		    opt_force_dpmi09 = 0;
		    break;
		case '9':                   /* no dpmi10 calls */
		    opt_force_dpmi09 = 1;
		    break;

		case 'D':                   /* don't touch int3 */
		    opt_debugrsx = 1;
		    break;
		case 'F':                   /* redirect output */
		    opt_redir = 1;
		    break;
		case 'I':                   /* print syscalls */
		    opt_print_syscalls = 1;
		    break;
		case 'K':                   /* kernel debug */
		    opt_kdeb = 1;
		    break;
		case 'O':                   /* set OS/2 bit */
		    opt_os2 = 1;
		    break;
		case 'P':                   /* print syscalls */
		    opt_printall = 1;
		    break;
		default:
		    puts("unknown -Rxx option");
		    return NULL;
	    }
	}
	if (*(--s) == 'R')
	    return NULL;
	break;

    case 'V':                   /* version print */
	opt_version = 1;
	break;

    default:
	return NULL;

    } /* switch (*s) */

    return s;
}

static void init_bios_keyboard(void)
{
    /* get DPMI selector for BIOS area, on error try 0x40 */
    if (SegToSel(0x40, &bios_selector))
	bios_selector = 0x40;

    /* services for enhanced keyboards, otherwise older types */
    if ((BYTE) read32(bios_selector, 0x96) & 0x10) {
	kread = 0x10;
	kready = 0x11;
    } else {
	kread = 0;
	kready = 1;
    }

    /* flush input */
    while (rm_bios_read_keybrd(kready))
	rm_bios_read_keybrd(kread);
}

static int setup_environment(char **env)
{
    char *s;

    /* save environment, env-size */
    for (org_envc = 0; env[org_envc] != NULL; org_envc++)
	;
    org_env = env;

    /* get enviroment options */
    s = getenv("RSXOPT");
    if (s != NULL) {
	for (; *s != '\0'; ++s) {
	    while (*s == ' ')
		++s;
	    if (*s == '-') {
		s = scan_for_option(++s, NULL);
		if (s == NULL) {
		    puts(version);
		    puts("error in RSXOPT");
		    return (1);
		}
	    } else
		break;
	}
    }
    return 0;
}

static int get_rsx_options(int start, char **argv)
{
    int i;
    char *s;

    for (i = start; argv[i]; i++) {
	if (argv[i][0] == '-') {
	    s = scan_for_option(& argv[i][1], NULL);
	    if (s == NULL) {
		puts(version);
		printf("bad option: %s\n", argv[i]);
		return -1;
	    }
	} else
	    break;
    }
    return i;
}

#ifdef __EMX__
#define exit(x) dos_exit(x)
#endif

/*
** MAIN():
** real mode for rsx16
** protected mode rsx32
*/

void main(int argc, char **argv, char **env)
{
    static char exefile[128];
    int file_arg, err;

#ifndef __EMX__
    set_stdout();
#endif
    init_real_mode();

    if (setup_environment(environ))
	exit(1);

    /* check bound exe-file */
    if (argc == 2 && argv[1][0] == '-' && argv[1][1] == '/'
	&& argv[1][6] == '/') {
	emxl_psp = hexstr2int(&argv[1][2]);
	get_emxl_psp(emxl_psp);
	build_emx_args(&argc, &argv);
	file_arg = 0;
    } else if (argc > 1 && strcmp(argv[1], "!proxy") == 0) {
	build_dj_args(&argc, &argv);
	emxl_psp = 1;
	file_arg = 0;
    } else if (strcmp(argv[0], "!RSX") == 0) {
	emxl_psp = 1;
	file_arg = 1;
    } else {
	file_arg = get_rsx_options(1, argv);
	if (file_arg == -1)
	    exit(1);
	opt_version = 1;
    }

    /* print version, if rsx running from prompt */
    if (opt_version)
	puts(version);

    /* check filename */
    if (argc <= file_arg) {
	puts("no filename defined");
	exit(1);
    }

    /* copro required, 387 there ? */
    npx_present = (char) npx_installed();

    if (opt_force_copro)
	copro = opt_force_copro - (char) 1;
    else if (copro == 1 && !npx_present)
	copro = 3;

    if (real_to_protected(1))
	exit(1);

    /* - - - now protected mode! - - - rsx/rsx32 */

    if (test_dpmi_capabilities())
	protected_to_real(1);

    if (hangin_extender()) {
	puts("ERROR: can't hang in extensions");
	protected_to_real(1);
    }

    if (opt_kdeb && argv[file_arg + 2])
	strcpy(kdeb_program, argv[file_arg + 2]);

    /* init process-tables */
    init_this_process();

    /* init bios_selector & get keyboard */
    init_bios_keyboard();

    strcpy(exefile, argv[file_arg]);
    if (rm_access(exefile, 0) == -1) {
	strcat(exefile, ".exe");
	if (rm_access(exefile, 0) == -1) {
	    printf("file not found: %s\n", argv[file_arg]);
	    shut_down(1);
	}
    }
    if (!opt_max_dos_handles)
	opt_max_dos_handles = N_FILES;
    else if (opt_max_dos_handles > RSX_NFILES)
	opt_max_dos_handles = RSX_NFILES;
    rm_sethandles(opt_max_dos_handles);

    /* hang in emulation */
    if (copro == 3)
	if (install_rsx387())
	    shut_down(2);

    init_fpu();

    /* rsx output */
    if (opt_redir)
	if ((rsx_stdout = rm_creat("rsx.log", _A_NORMAL)) < 0)
	    rsx_stdout = 1;

    err = exec32(P_WAIT, exefile, argc - file_arg, argv + file_arg, org_envc, org_env);

    printf("%s: ", exefile);
    switch (err) {
	case EMX_ENOEXEC:
	    puts("Not a valid a.out format");
	    break;
	case EMX_ENOMEM:
	    puts("Not enough DPMI memory");
	    break;
	default:
	    printf("Can't load file, emx errno = %d\n", err);
	    break;
    }

    shut_down(1);
    /* never reached */
}
