/* bhole header - main stuff */
#ifndef _BHOLE_H_MAIN
#define _BHOLE_H_MAIN

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<time.h>
#include<SDL.h> /* yeah baby, SDL */
#include<stdlib.h>
#include<math.h>
#include<limits.h>

#ifdef WIN32
#include<windows.h>
#include<io.h>
#include<ctype.h>
#else
#include<ctype.h>
#endif

#include<sys/stat.h>

#include<sys/time.h>

#include<unistd.h>
/*#else
#include<winsock.h>
#endif
*/

extern FILE *report_file;




/* HYPER_SPEED_MODE - in this mode
 * everything will work in a more efficient way
 * but some features will be disabled:
 * effects of HYPER_SPEED_MODE:
 * - debug_vcpu will be ignored
 * - get_csip will IGNORE memory redirection
 * - in little endian systems, get_csip_word
 *   will work faster
 * - IP will be stored in the size of the default machine
 *   (for pentium, 32-bit) instead of 16-bit
 */
#define HYPER_SPEED_MODE


/* system sizes check */
/* TODO: modify the code to handle these kind of shits */
#if USHRT_MAX != 65535
#error USHRT_MAX does not fit
#endif
#if UINT_MAX != 4294967295U
#error UINT_MAX does not fit
#endif
#if UCHAR_MAX != 255
#error UCHAR_MAX does not fit
#endif
#if ULONG_MAX != 4294967295UL
#error ULONG_MAX does not fit
#endif
extern int assume_com_file ;
extern int assume_img_file ;

#define REGSCOUNT 12
#define GREGSCOUNT 8
#define SREGSCOUNT 4
#define REGSSIZE  24
#define MEMSIZE 1048576


extern unsigned char __emustuff__[REGSSIZE + MEMSIZE];
extern char *execname;
extern FILE *execfile;

#define regs ((unsigned short*)__emustuff__)

#define ax regs[0]
#define cx regs[1]
#define dx regs[2]
#define bx regs[3]
#define sp regs[4]
#define bp regs[5]
#define si regs[6]
#define di regs[7]
#define es regs[8]
#define cs regs[9]
#define ss regs[10]
#define ds regs[11]

#define al *((unsigned char*)&ax)
#define cl *((unsigned char*)&cx)
#define dl *((unsigned char*)&dx)
#define bl *((unsigned char*)&bx)
#define ah *((unsigned char*)&ax+1)
#define ch *((unsigned char*)&cx+1)
#define dh *((unsigned char*)&dx+1)
#define bh *((unsigned char*)&bx+1)


#define IS_REG_PTR(p) ( ((void*)regs <= (p)) && ((p) <= (void*)(regs+11)) )




#ifndef HYPER_SPEED_MODE
extern unsigned short ip;
#else
extern unsigned int   ip; /* unless program overflows IP all should be okay i hope */
#endif


extern unsigned short flags;

#define GET_CF() (flags&0x01)
#define SET_CF() flags|=0x01
#define CLEAR_CF() flags&=(~0x01)
#define UNSET_CF() CLEAR_CF()

#define GET_PF() (flags&0x04)
#define SET_PF() flags|=0x04
#define CLEAR_PF() flags&=(~0x04)
#define UNSET_PF() CLEAR_PF()

#define GET_AF() (flags&0x10)
#define SET_AF() flags|=0x10
#define CLEAR_AF() flags&=(~0x10)
#define UNSET_AF() CLEAR_AF()

#define GET_ZF() (flags&0x40)
#define SET_ZF() flags|=0x40
#define CLEAR_ZF() flags&=(~0x40)
#define UNSET_ZF() CLEAR_ZF()

#define ZF_ON  0x40


#define GET_SF() (flags&0x80)
#define SET_SF() flags|=0x80
#define CLEAR_SF() flags&=(~0x80)
#define UNSET_SF() CLEAR_SF()

#define GET_OF() (flags&0x8000)
#define SET_OF() flags|=0x8000
#define CLEAR_OF() flags&=(~0x8000)
#define UNSET_OF() CLEAR_OF()


#define GET_DF() (flags&0x0400)
#define SET_DF() flags|=0x0400
#define CLEAR_DF() flags&=(~0x0400)
#define UNSET_DF() CLEAR_DF()


/* 0x000 - 0x3FF interrupt vector */

/* should ENVPOS be 0x9DE0??? */

#define ENVPOS (0x09CF0)
#define PSPPOS (0x119E0)
#define PSPSEG (0x119E)
/* #define DTA_OFFSET (0x80) - OBSOLETE  PSPSEG:DTA_OFFSET = address of DTA */
/* dta is no longer constant */
/* dta is assumed to be in dta_segment:dta_offset and is set
 * in the routine init_env to PSP:0x80 
 */
unsigned short dta_offset;
unsigned short dta_segment; 

/*
 * THESE MACROS WERE CANCELLED CAUSE THEY ALLOW
 * DIRECT MEMORY ADDRESS
 *
 * #define ENV (memory + ENVPOS)
 * #define PSP (memory + 0x119E0)
 *
 */

#define RESERVED 0x11AE0 /* size of reserved stuff before prog can be */

#define __memory__ (__emustuff__ + REGSSIZE)

/* is this definition safe??? */
#define pseudoclock (*(long*)(__memory__ + 0x46C))
#define line pseudoclock


#define emumem2mem(mem,seg,index,len) emudmem2mem((mem),((seg)<<4)+(index),(len));
#define mem2emumem(seg,index,mem,len) mem2emudmem(((seg)<<4)+(index),(mem),(len));
void  emudmem2mem(unsigned char *mem,unsigned int offset,int len);
void  mem2emudmem(unsigned int offset,unsigned char *mem,int len);




/* this declares the important routine used
 * to redirect memory correctly
 */
unsigned char *_redirected_addr(unsigned int   ofs);
unsigned char  get_vidmem(int doffset);
void set_vidmem(unsigned int doffset,unsigned char value);


#define IS_MEMORY_PTR(ptr) ( (__memory__ <= ((unsigned char*)(ptr))) && (((unsigned char *)(ptr)) < __memory__ + 1048576) )
#define IS_CLOCK_PTR(ptr) (__memory__ + 0x470 > (ptr) && (ptr) >= __memory__ + 0x46C)
#define IS_BELOW_HIGHMEM_PTR(ptr) ( ((unsigned char*)(ptr)) < __memory__ + 0xA0000 )
#define IS_BELOW_HIGHMEM_OFS(ofs) (                  (ofs)   <              0xA0000 )
/* should vidmem be considered as 64000 or as 65536??? */
#define IS_VIDMEM_OFS(ofs) ( (           + 0xA0000 <= ofs                    ) && (ofs                      <            + 0xA0000 + 65536 ) )
#define IS_BELOW_VIDMEM_LIMIT_OFS(ofs) ((ofs) < 0xA0000 + 65536)

#define IS_BELOW_FULLVIDMEM_LIMIT_OFS(ofs) ((ofs) < 0xC0000)

#define redirected_addr(ptr) (_redirected_addr(ptr))

#define SET_MEM_BYTE(seg,offset,value) SET_MEM_BYTE2((offset) + ((seg) << 4),value)
#define GET_MEM_BYTE(seg,offset      ) GET_MEM_BYTE2((offset) + ((seg) << 4))


/************* OLD UNUSED VERSION
#define GET_MEM_BYTE2(doffset) \
(IS_VIDMEM_OFS(doffset) ? get_vidmem(doffset) : ( *((unsigned char*)(redirected_addr(__memory__ + (doffset)  ) ) ) ) ) \
**************/

#ifdef ASSUME_VIDEO_PAGE_FLIPPING

#ifdef CHECK_INLIM_MEM
#error unimp
#endif

#define GET_MEM_BYTE2(doffset) \
(IS_BELOW_HIGHMEM_OFS(doffset) ? *(__memory__ + (doffset)) : IS_BELOW_VIDMEM_LIMIT_OFS(doffset) ? get_vidmem(doffset) : \
 *(redirected_addr(doffset)))

#else

#ifndef CHECK_INLIM_MEM

#define GET_MEM_BYTE2(doffset) \
(IS_BELOW_FULLVIDMEM_LIMIT_OFS(doffset) ? *(__memory__ + (doffset)) : *(redirected_addr(doffset)))

#else

#define GET_MEM_BYTE2(doffset) \
(IS_BELOW_FULLVIDMEM_LIMIT_OFS(doffset) ? *(__memory__ +  check(doffset)) : *(redirected_addr(doffset)))

int check(int dofs)
{
	if (dofs < CHECKLIM1 && dofs > CHECKLIM2)
	{
		printf("GET_MEM_BYTE2: debug: %x\n",dofs);
	}
	return dofs;
}

#endif

#endif


/**************** OLD UNUSED VERSION
#define SET_MEM_BYTE2(doffset,value)		\
{						\
	if (IS_VIDMEM_OFS(doffset))		\
		set_vidmem(doffset,value);	\
	else					\
		( *((unsigned char*)(redirected_addr(__memory__ + (doffset)               ) ) ) = (value));	\
}						\
*****************/

/* TODO: SET_MEM_BYTE2, what check should come before, vidmem ofs or other ptr check */

#ifdef ASSUME_VIDEO_PAGE_FLIPPING

#ifdef CHECK_INLIM_MEM
#error unimplemented
#endif

#define SET_MEM_BYTE2(doffset,value)					\
{									\
	if (IS_BELOW_HIGHMEM_OFS(doffset))				\
	{								\
		*(unsigned char*)(__memory__ + (doffset) ) = (value);	\
	}								\
	else if (IS_BELOW_VIDMEM_LIMIT_OFS(doffset))			\
	{								\
		set_vidmem(doffset,value);				\
	}								\
	else								\
	{								\
		*(unsigned char*)(redirected_addr(doffset)) = (value);	\
	}								\
}									\

#else

#ifndef CHECK_INLIM_MEM

#define SET_MEM_BYTE2(doffset,value)					\
{									\
	if (IS_BELOW_HIGHMEM_OFS(doffset))				\
	{								\
		*(unsigned char*)(__memory__ + (doffset) ) = (value);	\
	}								\
	else								\
	{								\
		*(unsigned char*)(redirected_addr(doffset)) = (value);	\
	}								\
}									\

#else


#define SET_MEM_BYTE2(doffset,value)					\
{									\
	if (IS_BELOW_HIGHMEM_OFS(doffset))				\
	{								\
		if (doffset <= CHECKLIM1 && doffset >= CHECKLIM2)				\
			printf("SET_MEM_BYTE2: debug: %x -> %x\n",value,doffset);	\
		*(unsigned char*)(__memory__ + (doffset) ) = (value);	\
	}								\
	else								\
	{								\
		*(unsigned char*)(redirected_addr(doffset)) = (value);	\
	}								\
}									\


#endif

#endif

/* THE FOLLOWING MACROS ARE DISABLED SINCE THEY DO NOT
 * ALWAYS ALLOW MEMORY REDIRECTION
 *
 *

#define GET_MEM_WORD(seg,offset) \
( *((unsigned short*)(memory + (offset) + ((seg) << 4) )) )


#define GET_MEM_DWORD(seg,offset) \
( *((unsigned int*)(memory + (offset) + ((seg) << 4) )) )


#define SET_MEM_WORD(seg,offset,value) \
( *((unsigned short*)(memory + (offset) + ((seg) << 4) )) = (value) )

 *
 * END OF CANCELATION ZONE
 */


#define GET_MEM_WORD(seg,offset) \
(                                \
  ((unsigned short)GET_MEM_BYTE(seg,offset)         ) + \
  (                GET_MEM_BYTE(seg,(offset)+1) << 8)   \
)

#define SET_MEM_WORD(seg,offset,value)                 \
 {                                                     \
 unsigned short val = (value);                           \
 SET_MEM_BYTE(seg,offset  ,   val  & 0xFF         );   \
 SET_MEM_BYTE(seg,offset+1,(( val  & 0xFF00) >> 8));   \
 }                                                     \

#define GET_MEM_WORD2(doffset) \
(                                \
  ((unsigned short)GET_MEM_BYTE2(doffset)         ) | \
  (                GET_MEM_BYTE2((doffset)+1) << 8)   \
)

#define SET_MEM_WORD2(doffset,value)                        \
 {                                                          \
 unsigned short val = (value);                              \
 SET_MEM_BYTE2( (doffset)       , val     & 0xFF         ); \
 SET_MEM_BYTE2(((doffset)+1)   ,((val     & 0xFF00) >> 8)); \
 }

#endif


extern time_t starttime;
