#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <dpmi.h>
// #include <llp.h>

extern int _breakflag;

int errno,_dos_errno;

#define FROMBCD(x) (((x)>>4)*10 +((x)& 0xf))

struct sb {
	short envseg;
	short ocomtail;
	short scomtail;
	short ofcb1,sfcb1;
	short ofcb2,sfcb2;
};
	
extern UWORD _pspseg;

static char *fcbstring = "                ";

static UWORD dtaseg;
static SELECTOR dtasel;

#pragma startup initdta 140
#pragma rundown freedta 10

#pragma startup checkver 19

/* this is only required if we don't use pmode/308 */

static void checkver(void)
{
	DPMI_REGS regs;
	__doscall(0x30,&regs);
	if (regs.b.al < 3) {
		fprintf(stderr,"This program requires DOS 3.0 or better");
		exit(255);
	}
}
static void initdta(void)
{
	dpmi_alloc_real_memory(&dtasel,&dtaseg,0x80); /* 2048 bytes */
}
static void freedta(void)
{
	dpmi_dealloc_real_memory(dtasel);
}

int __nametodta(char *name, int index)
{
	int len = strlen(name) + 1;
	dpmi_copy_from_ds(dtasel,index,name,len);
	return index;
}
int __buftodta(unsigned char *buf, int len)
{
	dpmi_copy_from_ds(dtasel,0,buf,len);
	return 0;
}
int __dtatobuf(unsigned char *buf, int len)
{
	dpmi_copy_to_ds(buf,dtasel,0,len);
	return 0;
}
int __doscall(int func, DPMI_REGS *regs)
{
	regs->b.ah = func;
	regs->h.ds = dtaseg;
	regs->h.es = dtaseg;
	regs->h.ss = 0;
	regs->h.sp = 0;
	regs->h.flags = 0x42;
	dpmi_simulate_real_interrupt(0x21,regs);
	if (_breakflag)
		raisecb();
	if (regs->h.flags & 1) {
		DPMI_REGS reg2;
		reg2.b.ah = 0x59;
		reg2.h.bx = 0;
		reg2.h.ss = 0;
		reg2.h.sp = 0;
		reg2.h.flags = 0x42;
		dpmi_simulate_real_interrupt(0x21,&reg2);
		_dos_errno = errno = reg2.b.al;
		return 0;
	}
	return regs->h.ax;
}
void __realint(int val, DPMI_REGS *regs)
{
	regs->h.ds = dtaseg;
	regs->h.es = dtaseg;
	regs->h.ss = 0;
	regs->h.sp = 0;
	regs->h.flags = 0x42;
	dpmi_simulate_real_interrupt(val,regs);
}
int _ll_spawn(char *file, char *parms, char *env)
{
	DPMI_REGS regs;
	struct sb spawnblock;
	UWORD envseg;
	SELECTOR envsel;
	ULONG base;
	char parms1[256];

	strcpy(parms1+1,parms);
	parms1[0] = strlen(parms);

	dpmi_copy_to_ds(&envsel,_pspseg,0x2c,2);
	dpmi_get_sel_base(&base,envsel);
	envseg = base >> 4;

	regs.h.dx = __nametodta(file,256);
	spawnblock.ocomtail =  __nametodta(parms1,512);
	spawnblock.scomtail =  dtaseg;
	spawnblock.ofcb1 = __nametodta(fcbstring,768);
	spawnblock.ofcb2 = __nametodta(fcbstring,768+32);
	spawnblock.sfcb1 = spawnblock.sfcb2 = dtaseg;
	spawnblock.envseg = envseg;
	regs.h.bx = __buftodta(&spawnblock,sizeof(struct sb));
	regs.b.al = 0;
	__doscall(0x4b,&regs);
	if (regs.h.flags & 1)
		return -1;

	__doscall(0x4d,&regs);
	return regs.b.al;
}
struct tm *_ll_gettime(struct tm *tm2)
{
	DPMI_REGS regs;
	memset(tm2,0,sizeof(struct tm));
	regs.b.ah = 2;
	__realint(0x1a,&regs);

  tm2->tm_sec = FROMBCD(regs.b.dh);
  tm2->tm_min = FROMBCD(regs.b.cl);
  tm2->tm_hour = FROMBCD(regs.b.ch);

	regs.b.ah = 4;
	__realint(0x1a,&regs);

  tm2->tm_mday = FROMBCD(regs.b.dl);
  tm2->tm_mon = FROMBCD(regs.b.dh)-1;
  tm2->tm_year = FROMBCD(regs.b.cl);
	if (tm2->tm_year < 71)
		tm2->tm_year += 100;
	return tm2;
}
int _ll_ticks(void)
{
	DPMI_REGS regs;
	regs.b.ah = 0;
	__realint(0x1a,&regs);
	return (regs.h.cx << 16) + regs.h.dx;
}
int _ll_getenv(char *buf, int id)
{
	int answer;
	asm push esi
	asm push edi
	asm push ebx
	asm push es
	asm cld
	asm mov es,[_pspseg]
	asm mov es,es:[0x2c]
	asm mov edx,[ebp+8]
	asm or edx,edx
	asm jz lbl_count
	asm mov edx,[ebp+12]
	asm sub	eax,eax			/* set up for scan */
	asm mov edi,eax
	asm mov ecx,-1
	asm test 	byte ptr es:[edi],0xff
	asm jz	lbl_errenv
lbl_lp1:
	asm dec edx
	asm jz	lbl_gotenv
	asm repnz	scasb			/* scan for end of environment */
	asm test	byte ptr es:[edi],0xff
	asm jnz 	lbl_lp1
	asm jmp	lbl_errenv
lbl_gotenv:
	asm mov	esi,[ebp+8]
	asm xchg	esi,edi
	asm cli
	asm push	ds
	asm push	ds
	asm push	es
	asm pop	ds
	asm pop	es
lbl_mvlp:
	asm lodsb
	asm stosb
	asm or al,al
	asm jnz lbl_mvlp
	asm pop ds
	asm sti
	asm sub eax,eax
	asm inc eax
	asm jmp	lbl_exit
lbl_errenv:
	asm sub	eax,eax
	asm jmp	lbl_exit
	

lbl_count:
	asm sub	eax,eax			/* set up for scan */
	asm mov 	edi,eax
	asm mov 	ecx,-1
	asm mov edx,eax
lbl_lp:
	asm inc edx
	asm repnz	scasb			/* scan for end of environment */
	asm test	byte ptr es:[edi],0xff
	asm jnz 	lbl_lp
lbl_noenv:
	asm mov eax,edx
lbl_exit:
	asm pop es
	asm pop ebx
	asm pop edi
	asm pop esi
	answer = _EAX;
	return answer;
}	