/*****************************************************************************
 * FILE: rmlib.c							     *
 *									     *
 * DESC:								     *
 *	- Interface to Real Mode calls					     *
 *									     *
 * Copyright (C) 1993,1994						     *
 *	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld		     *
 *	email: rainer@mathematik.uni-bielefeld.de			     *
 *									     *
 *****************************************************************************/

#include "DPMI.H"
#include "PROCESS.H"
#include "START32.H"
#include "CDOSX32.H"
#include "RMLIB.H"
#include "DOSERRNO.H"

#define LV_WORD *(WORD *)&

unsigned rm_bios_read_keybrd(unsigned mode)
{
    TRANSLATION tr;
    unsigned key;

    LV_WORD tr.eax = mode << 8;
    tr.sp = tr.ss = 0;
    tr.flags = 0x3200;
    tr.cs = cs16real;
    tr.ds = cs16real;
    SimulateRMint(0x16, 0, 0, &tr);
    key = (unsigned) (tr.eax & 0xFFFF);

    if ((tr.flags & 64) && (mode == 0x01 || mode == 0x11))
	return 0;
    else
	return key;
}

#ifdef __EMX__
#define SET_SEG_OFF(pointer, seg, off) \
    { seg = (((unsigned) (pointer) & ~0xFFF) >> 4) + ds16real; \
	off = (unsigned) (pointer) & 0xFFF; }
#else				/* 16 bit compiler */
#define SET_SEG_OFF(pointer, seg, off) \
    { seg = ds16real; off = (WORD) (pointer) ;}
#endif

#ifdef __EMX__
#define SET_SEG_OFF_64(pointer, seg, off) \
    { seg = (((unsigned) (pointer) & ~0xFFFF) >> 4) + ds16real; \
	off = (unsigned) (pointer) & 0xFFFF; }

#else				/* 16 bit compiler */
#define SET_SEG_OFF_64(pointer, seg, off) \
    { seg = ds16real; off = (WORD) (pointer) ;}
#endif

unsigned emx_errno;

static int call_rm_dos(TRANSLATION *ts)
{
    ts->flags = 0x3200;
    ts->cs = cs16real;

    SET_SEG_OFF_64(real_mode_stack, ts->ss, ts->sp);

    /* DPMI-Call to Real-Mode DOS */
    SimulateRMint(0x21, 0, 0, ts);

    /* return Carry-bit */
    if (ts->flags & 1) {
	_doserrno = (unsigned) ts->eax & 0xFFFF;
	emx_errno = doserror_to_errno(_doserrno);
	return -1;
    } else
	return (int) (ts->eax & 0xFFFF);
}

/* ********** DISK OPERATIONS ********** */

/*
** AH = 0x0E
** DL = drive (a=0,b=1,c=2)
** return: AL max drive
*/
int rm_setdrive(WORD drive)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x0E00;
    LV_WORD ts.edx = drive;
    return (call_rm_dos(&ts) & 0xFF);
}

/*
** AH = 0x19
** return: AL this drive (a=0,b=1,c=2)
*/
int rm_getdrive(void)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x1900;
    return (call_rm_dos(&ts) & 0xFF);
}

/*
** AH = 0x1A
** DS:DX dta address
*/
void rm_setdta(void *buf)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x1A00;
    SET_SEG_OFF(buf, ts.ds, ts.edx);
    call_rm_dos(&ts);
}

/* ********** DATE/TIME OPERATIONS ********** */

/*
** AH = 0x2A
** return: CX=year DX=month/day
*/
void rm_getdate(struct dos_date * dd)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x2A00;
    call_rm_dos(&ts);
    dd->ddate_year = (WORD) ts.ecx;
    dd->ddate_month = (BYTE) ((WORD) ts.edx >> 8);
    dd->ddate_day = (BYTE) ts.edx;
    dd->ddate_dayofweek = (BYTE) ts.eax;
}

/*
** AH = 0x2B
** CX = year
** DX = month/day
*/
int rm_setdate(struct dos_date * dd)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x2B00;
    LV_WORD ts.ecx = (WORD) dd->ddate_year;
    LV_WORD ts.edx = ((WORD) dd->ddate_month << 8) | dd->ddate_day;
    call_rm_dos(&ts);
    if ((ts.eax & 0xFF) == 0xFF)
	return -1;
    else
	return 0;
}

/*
** AH = 0x2C
** return CX=hour/min DX=sec/hsec
*/
void rm_gettime(struct dos_time * dt)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x2C00;
    call_rm_dos(&ts);
    dt->dtime_hour = (BYTE) ((WORD) ts.ecx >> 8);
    dt->dtime_minutes = (BYTE) ts.ecx;
    dt->dtime_seconds = (BYTE) ((WORD) ts.edx >> 8);
    dt->dtime_hsec = (BYTE) ts.edx;
}

/*
** AH = 0x2D
** CX = hour/min
** DX = sec/hsec
*/
int rm_settime(struct dos_time * dt)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x2D00;
    LV_WORD ts.ecx = ((WORD) dt->dtime_hour << 8) | dt->dtime_minutes;
    LV_WORD ts.edx = ((WORD) dt->dtime_seconds << 8) | dt->dtime_hsec;
    call_rm_dos(&ts);
    if ((ts.eax & 0xFF) == 0xFF)
	return -1;
    else
	return 0;
}

/* ********** DIRECTORY OPERATIONS ********** */

/*
** AH = 0x3B
** DS:DX name (64 bytes)
*/
int rm_chdir(char *name)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x3B00;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AH = 0x43 ; AL = 0
** DS:DX name
** return CX: attr
*/
int rm_getfattr(char *name, WORD * attr)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4300;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    if (call_rm_dos(&ts) == -1)
	return -1;
    else {
	*attr = (WORD) ts.ecx;
	return 0;
    }
}

int rm_access(char *name, WORD mode)
{
    WORD attr;

    if (rm_getfattr(name, &attr) == -1)
	return -1;
    if ((attr & 1) && (mode & 2))	/* RDONLY and try WRITE access */
	return -1;
    else
	return 0;
}

/*
** AH = 0x43 ; AL = 1
** CX = attr
** DS:DX name
*/
int rm_setfattr(char *name, WORD attr)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4301;
    LV_WORD ts.ecx = attr;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AH = 0x47
** DS:SI name
*/
int rm_getcwd(WORD drive, char *name)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4700;
    LV_WORD ts.edx = drive;
    SET_SEG_OFF(name, ts.ds, ts.esi);
    return call_rm_dos(&ts);
}

/*
** AH = 0x4E
** CX = attr
** DS:DX name
*/
int rm_findfirst(char *name, WORD attr, struct find_t * ft)
{
    TRANSLATION ts;

    rm_setdta(ft);
    LV_WORD ts.eax = 0x4E00;
    LV_WORD ts.ecx = attr;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/* ********** FILE HANDLE OPERATIONS ********** */

/*
** AH = 0x3C
** CX = attr
** DS:DX = name
** return: AX fileno
*/
int rm_creat(char *name, WORD attr)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x3C00;
    LV_WORD ts.ecx = attr;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AH = 0x3D
** AL = mode
** DS:DX = name
** return: AX fileno
*/
int rm_open(char *name, WORD modes)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x3D00 | modes;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AH = 0x3E
** BX = file handle
** return: -1 on error
*/
int rm_close(WORD handle)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x3E00;
    LV_WORD ts.ebx = handle;
    if (call_rm_dos(&ts) == -1)
	return -1;
    else
	return 0;
}

/*
** AH = 0x3F
** BX = handle
** CX = bytes
** DS:DX = buf
** return: AX bytes
*/
int rm_read(WORD handle, void *buf, WORD bytes)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x3F00;
    LV_WORD ts.ebx = (WORD) handle;
    LV_WORD ts.ecx = (WORD) bytes;
    SET_SEG_OFF(buf, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AH = 0x40
** BX = handle
** CX = bytes
** DS:DX = buf
** return: AX bytes
*/
int rm_write(WORD handle, void *buf, WORD bytes)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4000;
    LV_WORD ts.ebx = (WORD) handle;
    LV_WORD ts.ecx = (WORD) bytes;
    SET_SEG_OFF(buf, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AH = 0x42
** AL = orgin
** BX = handle
** CX:DX = pos
** return: new pos
*/
long rm_lseek(WORD handle, DWORD offset, WORD orgin)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4200 | orgin;
    LV_WORD ts.ebx = (WORD) handle;
    LV_WORD ts.ecx = (WORD) (offset >> 16);
    LV_WORD ts.edx = (WORD) offset;
    if (call_rm_dos(&ts) == -1)
	return -1L;
    else
	return (ts.edx << 16) | (ts.eax & 0xFFFF);
}

/*
** AH = 0x45
** BX = handle
** return: AX new handle
*/
int rm_dup(WORD handle)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4500;
    LV_WORD ts.ebx = handle;
    return call_rm_dos(&ts);
}

/*
** AH = 0x46
** BX = handle
** CX = new handle
** return: -1 on error
*/
int rm_dup2(WORD handle, WORD newhandle)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4600;
    LV_WORD ts.ebx = handle;
    LV_WORD ts.ecx = newhandle;
    return call_rm_dos(&ts);
}

/*
** AX = 0x5700
** BX = handle
** return: CX:DX
*/
int rm_getftime(WORD handle, WORD * date, WORD * time)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x5700;
    LV_WORD ts.ebx = handle;
    if (call_rm_dos(&ts) == -1)
	return -1;
    else {
	*date = (WORD) ts.edx;
	*time = (WORD) ts.ecx;
	return 0;
    }
}

/*
** AX = 0x5701
** BX = handle
** CX:DX = time/date
*/
int rm_setftime(WORD handle, WORD date, WORD time)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x5701;
    LV_WORD ts.ebx = handle;
    LV_WORD ts.ecx = time;
    LV_WORD ts.edx = date;
    return call_rm_dos(&ts);
}

/*
** AH = 0x5B
** CX = attr
** DS:DX = name
** return: AX fileno
*/
int rm_creatnew(char *name, WORD attr)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x5B00;
    LV_WORD ts.ecx = attr;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    return call_rm_dos(&ts);
}

/*
** AX = 0x67
** BX = handles
*/
int rm_sethandles(WORD handles)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x6700;
    LV_WORD ts.ebx = handles;
    return call_rm_dos(&ts);
}

/* ********** IOCTL OPERATIONS ********** */

/*
** AH = 0x44 ; AL = 0
** BX = handle
*/
int rm_ioctl_getattr(WORD handle)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4400;
    LV_WORD ts.ebx = handle;
    if (call_rm_dos(&ts) == -1)
	return -1;
    else
	return (WORD) ts.edx;
}

int rm_isatty(WORD handle)
{
    int i = rm_ioctl_getattr(handle);
    if (i == -1)
	return 0;
    else
	return i & 128;
}

/*
** AH = 0x44 ; AL = 01
** BX = handle
** DX = mode
*/
int rm_ioctl_setattr(WORD handle, WORD mode)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4401;
    LV_WORD ts.ebx = handle;
    LV_WORD ts.edx = mode;
    if (call_rm_dos(&ts) == -1)
	return -1;
    else
	return 0;
}

/*
** AH = 0x44 ; AL = 06
** BX = handle
*/
int rm_ioctl_select_in(WORD handle)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4406;
    LV_WORD ts.ebx = handle;
    if (call_rm_dos(&ts) == -1)
	return 0;
    return ((BYTE)ts.eax == 0xFF) ? 1 : 0;
}

/*
** AH = 0x44 ; AL = 09
** BL = drive
** ret: 1/0 remote or -1
*/
int rm_ioctl_remotedrive(BYTE drive)
{
    TRANSLATION ts;

    LV_WORD ts.eax = (WORD) 0x4409;
    LV_WORD ts.ebx = (WORD) drive;
    if (call_rm_dos(&ts) == -1)
	return -1;
    else
	return (int) (ts.edx & 0x1000) >> 12;
}

/* ********** PROCESS CONTROL ********** */

/*
** AX = 0x4B00
** DS:DX = name
** ES:BX = exec block
** ret: -1 on error
*/
int rm_exec(char *name, struct execb * eb)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4B00;
    SET_SEG_OFF(name, ts.ds, ts.edx);
    SET_SEG_OFF(eb, ts.es, ts.ebx);
    return call_rm_dos(&ts);
}

/*
** AX = 0x4D00
** ret: exit value
*/
int rm_get_exit_status(void)
{
    TRANSLATION ts;

    LV_WORD ts.eax = 0x4D00;
    return call_rm_dos(&ts);
}
