#include <stdio.h>
#include <signal.h>
#include <dpmi.h>
#include <llp.h>
#include <stdlib.h>

extern int _stacktop;

int winxcept = 0;

static UWORD pmodeds,pmodess;
static UWORD oldcbrseg,cbseg;
static UWORD oldcbrofs,cbofs;
static int axsave,bxsave,cxsave,dxsave,sisave,disave,spsave,bpsave,ipsave;
static int flagssave;
static short essave,dssave,sssave,cssave,fssave,gssave;

int _breakflag;

#pragma startup llsignal 149

void __clearxcept(void)
{
	  asm push eax
		asm push ebx
		asm mov	ax,cs:[pmodess]
    asm mov	bx,ss
 		asm cmp	ax,bx
		asm jnz	windowsxcept
		asm pop ebx
		asm pop eax
		return;
windowsxcept:
		asm push ds
		asm mov ds,cs:[pmodeds]
		asm mov [winxcept],1
		asm mov ebx,[esp + 16 + 12 + 12]
		asm sub ebx,12;
		asm mov eax,[esp + 16 + 12+8]
		asm mov [ebx + 8],eax
		asm mov eax,[esp + 16 + 12+4]
		asm mov [ebx +4],eax
		asm mov eax,[esp + 12]
		asm xchg eax,[esp + 16 + 12]
		asm mov [ebx],eax
		asm sub ebx,4
		asm mov eax,[esp + 16 + 8]
		asm mov [ebx],eax
		asm mov [esp + 16 + 12 + 12],ebx
		asm and word ptr [esp + 16 + 12 + 8],0xfeff
		asm pop ds
		asm pop ebx
		asm pop eax
		asm add esp,4
		asm retf
}
#pragma regopt

static void raise12(void)
{
  asm mov ax,ss
	asm mov bx,ds
	asm cmp ax,bx
	asm jz  restack
	asm mov ebx,[_stacktop]
	asm mov dword ptr [esp + 12 + 12],ebx
		__clearxcept();
	asm jmp join
restack:
	asm mov esp,[_stacktop]
	asm sti
join:
	printf("Stack overflow, use larger stack");
	exit(EXIT_FAILURE);
}
	
static void raise3(void)
{
		__clearxcept();
		if (winxcept)
			asm pop eax;
		asm iretd;
}


static void raise13(void)
{
		__clearxcept();
		asm mov ss:[dssave],ds
		asm mov ss:[essave],es
		asm mov	ds,cs:[pmodeds]
		asm mov	es,cs:[pmodeds]
		asm mov [axsave],eax
		asm mov [bxsave],ebx
		asm mov [cxsave],ecx
		asm mov [dxsave],edx
		asm mov [sisave],esi
		asm mov [disave],edi
		asm mov [bpsave],ebp
		asm mov [spsave],esp
		asm mov [sssave],ss
		asm mov [fssave],fs
		asm mov [gssave],gs
		asm pop eax
		asm pop eax
		asm mov [ipsave],eax
		asm pop eax
		asm mov [cssave],ax
		asm pop eax
		asm mov [flagssave],eax
		asm mov esp,[_stacktop]
		asm sti;
		raise(SIGSEGV);
		fprintf(stderr,"\nGeneral protection fault\n\n");
		fprintf(stderr,"CS:EIP %04X:%08X\n",cssave,ipsave);
		fprintf(stderr,"SS:ESP %04X:%08X\n",sssave,spsave+12);
		fprintf(stderr,"EAX: %08X  EBX: %08X  ECX: %08X  EDX: %08X  flags: %08X\n",
				axsave,bxsave,cxsave,dxsave,flagssave);
		fprintf(stderr,"EBP: %08X  ESI: %08X  EDI: %08X\n",bpsave,sisave,disave);
		fprintf(stderr," DS:     %04X   ES:     %04X   FS:     %04X   GS:     %04X\n",
			dssave,essave,fssave,gssave);
		exit(EXIT_FAILURE);
}

static void div0(void)
{
		__clearxcept();
		asm mov	ds,cs:[pmodeds]
		asm mov	es,cs:[pmodeds]
		asm mov esp,[_stacktop]
		asm sti;
		fprintf(stderr,"\nDivide by 0");
		exit(EXIT_FAILURE);
}
#pragma regopt daf
/* __________________________________________________________________________
 *
 * this next is called from the DOS call routine
 * if there was a ctrl-break while we were calling DOS
 * this is because can't exit DPMI from an interrupt
 */
void raisecb(void)
{
	_breakflag = 0;
	if (raise(SIGBREAK))
		exit(EXIT_FAILURE);
}
static void intcb(void)
{
	asm push ds
  asm mov ds,cs:[pmodeds]
	asm mov [_breakflag],1
  asm pop ds
  asm iretd
}

void __setxcept(int num, void *address)
{
  SELECTOR sel;
	asm mov [sel],cs

	if (dpmi_set_protected_except(num,sel,(ULONG) address))
		dpmi_set_protected_interrupt(num,sel,(ULONG) address);
}
static void llsignal(void)
{
		SELECTOR pmodecs;
		asm mov	[pmodeds],ds
		asm mov [pmodecs],cs
		asm mov [pmodess],ss

		__setxcept(3,raise3);
		__setxcept(12,raise12);
		__setxcept(13,raise13);
		__setxcept(14,raise13);
		__setxcept(6,raise13);

		__setxcept(0,div0);
		__setxcept(4,div0);

		dpmi_set_protected_interrupt(0x23,pmodecs,(ULONG)intcb);
}