/* TODO: write a new addEwIb */

/* TODO: get rid of the return 1 or 0 in opcodes, use exit() or something like
      that instead
 */
 
/* TODO: implement sandbox-like EMM, this means if a program
 *       does not free an EMM handle, BHole will free it by himself
 */


/*
TODO: check_speeds slows us down, also, it's first measuring is useless
TODO: implement CHECK_SPEEDS_TO
*/

/* TODO: is the machine word size ip optimization helpful? */

/* TODO: replace EXECNAME with execname */

/*TODO : make some optimizations regarding modrm handling so that
       registers pointed out by modrm are to be handled better
       and not as memory references outside of memory (-1, -2, etc.)
       (that causes a small over head) */
       
/* TODO: improve efficency for INB and OUTB */

/* TODO: EWIB movs are shitty because the immediate is loaded to p2 and v2
 *       it is stupid
 */
 

/* current version: a linux version */

/* check out Ralf Brown's interrupt table files for more
 * info...
 */

/* TODO: order routines in a specific order to make the cache
 *       work better... 
 */

/* TODO: some codes say something like if (byte) shit = &al;
 *                                     else      shit = &ax;
 *                                     which is actually the same
 */
/* TODO: apparently, make a different startup environment variables */
/* TODO: maybe i should fine tune the assembler output of the compiler??? */


/* journal:
 * NOTICE...
 * this diary starts in a very late stage of BHole
 *
 *
 * 19/09/02 - 00:00 i am trying to implement
 *                  M1 and M2 routines
 *                  correctly and in an easy way,
 *                  so that all routines
 *                  which access memory can do it
 *                  without accessing directly,
 *                  but through macros which redirect
 *                  memory to make it correct
 * 19/09/02 - 01:21 stopped working on it
 *                  after converting:
 *                  handle_add
 *                  handle_or
 *                  handle_mov
 * 19/09/02 - 19:00 continued conversion...
 * 19/09/02 - 19:57 took a rest, remaining handlers are only
 *                  handle_ldsM2 and handle_lesM2
 * 19/09/02 - 21:07 continued working, did M2 routines
 *                  began modifying handle_modrm_addr
 *                  so it will return an offset and not a direct
 *                  pointer to memory
 *
 * 19/09/02 - 21:42 finished the M1s and M2s, they seem to compile okay
 *                  now i'm modifying the routines which fetches
 *                  parameters to return offsets instead of direct
 *                  emumemory pointers (stopped here)
 * 20/09/02 - 11:09 i will now fix some stuff in the routine which
 *                  fetches params (i'm now focusing on fixing CA case)
 * 20/09/02 - 12:30 stopped working after everything compiles
 *                  however, BHole SIGSEGVs when i run it
 *                  i guess it's somehow related to grp handlers
 * 20/09/02 - 12:50 continued working a bit, fixing the grp handlers
 * 20/09/02 - 12:55 stopped working
 * 20/09/02 - 17:35 returned to work...
 * 20/09/02 - 18:05 stopped working, after implenting
 *                  __emustuff__ and an OCHANDLERM1 for ROL
 * 20/09/02 - 15:10-15:20 worked for ten minutes
 * 20/09/02 - 15:50 continued working, making more OCHANDLERM1s
 * 20/09/02 - 16:50 almost done finishing all OCHANDLERs, going for a small rest
 * 21/09/02 - 11:33 work begins once again, looks like facall2 is the only
 *                  handler left
 * 21/09/02 - 11:37 finished all OCHANDLERs (at least they compile,
 *                                           now i just have to hope they work)
 *
 * 21/09/02 - 11:59 i'm an idiot, i accidently #defined REGSSIZE the same as
 *                  REGSCOUNT
 * 21/09/02 - 12:04 work stopped, re-implementing the regluts is harder than i tought
 * 21/09/02 - 12:29 work continued
 * 21/09/02 - 12:50 BUGFIX: the ASSIGNP????? macros apparently assumed at some cases
 *                          their parameter is a pointer and not the value itself
 *                          this is now fixed
 * 21/09/02 - 13:00 there seems to be some sort of a loop which causes some serious shit (go for break)
 * 21/09/02 - 17:46 i'm trying to find out what is causing the loop, please
 *                  check out BHole5.txt for more details
 * 21/09/02 - 17:57 found a bug in SET_MEM_WORD2, it accidently
 *                  wrote twice to the same byte instead once to the 2 bytes
 * 21/09/02 - 18:04 the starting values for bx and cx are wrong, what are they anyway?
 *                  fixed bx and cx start values and actually got a small grasp
 *                  of their meaning, please check out their initializer's to find out more
 * 21/09/02 - 18:21 something's wrong, memory interface is okay but still
 *                  looks like some MOV does not access it...
 * 21/09/02 - 18:27 well, the problem was in the getparam routine, one << too much
 * 21/09/02 - 18:33 more strange problems ahead, but now i go to rest...
 * 21/09/02 - 22:00 back to work
 * 21/09/02 - 22:19 i am such an idiot *nrrrghhhhh*,
 *                  i have put M1 and M2 handlers for all of the routines
 *                  which get AOR parameters, i have modified AOR registers
 *                  so that they look like memory, but there is one thing i forgot
 *                  i forgot that the M1 and M2 handlers should also be placed
 *                  for all of the routines with a CA parameter
 *                  ca means always an address (forgot what the initials mean)
 * 21/09/02 - 22:24 good news, i was correct and got rid of THAT endless loop
 *                  bad news, there's a new endless loop, i'm taking a break now...
 * 21/09/02 - 22:58 after a BHolent checking i think the problem is related
 *                  to reglutb
 * 25/09/02 - 18:13 wow, what a long break i took from BHole
 *                  i just fixed the reglutb and got a new bug
 *                  BHole SIGSEGVs somewhere after line 1405
 * 25/09/02 - 18:53 apparently 0x83,0xC6,0x08
 *                  is translated to ADD SI,108
 *                  instead of ADD SI,8
 *                  which means there's a bug in the GRP handler
 *                  (i'm not trying to solve it now)
 * 27/09/02 - 16:30 what's the problem???
 * 27/09/02 - 16:46 apparently the value is calculated correctly,
 *                  however, when it's assigned to SI
 *                  something goes wrong
 * 27/09/02 - 16:51 it is as if somehow the ADD changes the value
 *                  of the right side parameter!?
 * 27/09/02 - 16:53 FUUUUCCCCKKK I HAVE FOUND THE PROBLEM!!!
 *                  AS WE ALL KNOW, #DEFINE macros
 *                  are like inlines, only they do not regenerate
 *                  the params, so if a macro uses it's parameter
 *                  twice and the parameter is the value returned
 *                  from a routine, it is returned twice
 *                  and this causes some major fuck ups
 *                  i must fix the memory macros
 *                  which use their parameter twice
 *                  to prevent this
 * 27/09/02 - 17:03 good news, that bug is fixed, but BHole still SIGSEGVs
 * 27/09/02 - 17:22 a SHR goes wrong (25A)
 * 27/09/02 - 17:31 i can't believe it, in SHR_TEXT, accidently i did i = GETP1BYTE()
 *                  instead of i = GETP2BYTE()
 * 27/09/02 - 17:54 there's a new error, i have no idea what's causing it,
 *                  some sigsegv occurs near line 1427, but even line 1300
 *                  appears to be all okay
 * 27/09/02 - 18:11 SIGSEGV is caused by MUL BX instruction (0xF7,0xE3)
 *                  at line 1429 (Resting...)
 * 27/09/02 - 21:12 holy shit, same mistake in the SHR happened in MUL as well
 *                  now it's fixed
 * 27/09/02 - 21:14 something goes wrong, causing the fileopen (some int 21h)
 *                  to go wrong, trying to open non-existing files,
 *                  causing shit all over...
 *                  found the problem, apparently, some of the routines
 *                  which get filenames only took the first 8 characters
 *                  of the filename (i forgot a filename also has an extenstion)
 *
 * 27/09/02 - 21:21 some new problem, still getting "Abnormal program terminiation"
 * 27/09/02 - 21:59 found the error, it's in fileread, apparently, after
 *                  fileread finishes, it should change AX to the number of bytes
 *                  read, it used to, but i changed it and the new version didn't,
 *                  well, now it does (take a rest...)
 * 27/09/02 - 22:14 back to work...
 * 27/09/02 - 22:36 bug fixed... (something's now wrong with redirection to EMM)
 * 27/09/02 - 22:52 EMM redirector should be okay now (wierd bug, one indirect less)
 * 27/09/02 - 23:13 i take a rest
 * 28/09/02 - 10:44 back to work
 * 28/09/02 - 12:00 took a rest
 * 28/09/02 - 12:45 returned to work
 * 28/09/02 - 13:08 there was a bug in the EMM redirector, it accidently
 *                  redirected everything to the beginning of the lpage
 *                  instead to different places in the lpage
 * 28/09/02 - 13:14 begun implementing IDIV
 * 28/09/02 - 13:23 began implementing (not fully???) int 67 with ax = 5901h
 * 28/09/02 - 13:33 implemented a fucked up version of int 67 with ah = 40h
 * 28/09/02 - 13:35 bugfix: now int67_freehandle does emm_availble+=size_of_handle
 * 28/09/02 - 13:37 problem? what does int 67 with ah=47h does???
 * 28/09/02 - 13:43 int 67,47 will now give ah=84h which means - "unimplemented"
 * 28/09/02 - 13:49 we get an infinite loop somewhere... (first time: 5CB18-5CB28)
 * 28/09/02 - 13:52 ALERT!!! codstrim is growing too much...
 * 28/09/02 - 13:54 csanal is barely working, i must devise a new way to handle
 *                  theses problems, log out...
 * 28/09/02 - 16:21 the csanal now works much quicker, but codstrim is still too big
 *                  i will now try to understand what causes the loop
 *
 * 28/09/02 - 16:29 there is an infinite loop, but it's not that one, it's another one
 *                  which starts in: 608F2, or not? i will check - everything looks too okay
 * 28/09/02 - 16:44 there has been an AMAZING BREAKTHROUGH(!!!!!!!!!!!!!!!)
 *                  apparently, there was no infinite loop, it's just that BHole
 *                  is really really really slow (oh no...)
 *                  okay, i will do optimization later, first, i will implement
 *                  palette
 * 28/09/02 - 16:55 it is tooooooo sloooooowwwwwwww, nnnoooot gooood ennnooouuugggghhhhh
 * 28/09/02 - 17:07 TODO: implement handle_in for port 0x3DA
 * 28/09/02 - 17:07 log out
 * 28/09/02 - 20:07 log in - it seems like get_params and get_grp_params are the major bottlenecks - logout
 * 28/09/02 - 21:10 log in - the time has come for me to finally get rid of get_params
 *                  i will start the painstaking mission now by modifying
 *                  the opcode table to a new form with different names for each opcode
 * 28/09/02 - 21:53 finished re-writing the opcode table to support the new naming
 *                  convention to get rid of grp_params
 *                  each opcode will do it's own parameter fetching, writing will be shortened
 *                  with the use of #defines - log out
 * 29/09/02 - 16:32 log in - i will now begin preparing the converted routines
 *                  in order to create word and byte routines seperatly - log out
 * 29/09/02 - 21:35 log in - i have begun replacing #defines like this:
 *                  MOV_TEXT---->MOVB_TEXT (for bytes)
 *                          |
 *                          |--->MOVW_TEXT (for words)
 * 29/09/02 - 22:01 converted some of those ugly #defines, i will now take a rest...
 * 30/09/02 - 13:39 back to work - log in
 * 30/09/02 - 14:35 stopped work, finished RCL, ROL and SHL - log out
 * 30/09/02 - 18:32 log in - continue working
 * 30/09/02 - 18:50 work complete, all _TEXT #defines successfuly converted,
 *                  i will now continue making all appropriate declarations
 *                  and definitions for GET/ASSIGNP1/2BYTE/WORD macros
 * 30/09/02 - 19:12 i have already made full DEF_OCHANDLER_???? declarations
 *                  for:
 *                  AXIw
 *                  ALIb
 *                  EbGb
 *                  EwGw
 *                  GbEb
 *                  GwEw
 * 30/09/02 - 19:16 log out
 * 01/10/02 - 08:51 log in - continuing work on DEF_OCHANDLER declarations
 * 01/10/02 - 10:01 ugh, the *juice*, it is *everythere*, *nrghhhhh*
 * 01/10/02 - 11:35 back to work - log in, stopped a few BHolents later - log out
 * 02/10/02 - 17:08 log in, by the time i'm writing this,
 *                  i wrote the stuff for opcodes 0x00 - 0x7F 0x84 - 0x9D
 * 02/10/02 - 17:58 many errors trying to compile things
 * 02/10/02 - 18:38 compile errors corrected, most handlers are already done - log out
 * 02/10/02 - 23:20 log in
 * 02/10/02 - 23:55 log out
 * 02/10/02 - 01:30 log in
 * 02/10/02 - 01:47 log out
 * 03/10/02 - 17:50 log in
 * 03/10/02 - 18:01 now, comes the time to write grp handlers
 * 03/10/02 - 18:27 how THE FUCK WILL I HANDLE handle_grp - log out
 * 03/10/02 - 19:21 log in - grp handlers are here but not yet
 * 03/10/02 - 19:57 so much *frumple* i must take a *swim* - log out
 * 03/10/02 - 21:49 log in
 * 03/10/02 - 21:51 log out
 * 03/10/02 - 22:57 log in
 * 03/10/02 - 23:43 log out
 * 04/10/02 - 12:09 log in - now i will do grp2 EbCL and EwCL handlers
 * 04/10/02 - 13:14 only GRP5 is left, very soon
 *                  all can be compiled and i will start searching for possible
 *                  bugs... (if only i could get a test .EXE or something...)
 * 04/10/02 - 13:29 log out
 * 04/10/02 - 18:30 log in
 * 04/10/02 - 18:37 found a bug in the mov DXIw, CXIw, ... routines
 * 04/10/02 - 18:46 fixed the ALOB, AXOW, OBAL, OWAX initializers to work correctly
 * 05/10/02 - 11:05 BHole seems to work, still to slow...
 *                  how can i further optimize it???
 * 07/10/02 - 15:49 i have done a big rewind because of many strange bugs...
 *                  looks like when i try optimizing some stuff the compiler optimized it better...
 * 07/10/02 - 16:57 suddently there is an endless loop, the only possible explanation i see
 *                  is that i should try fully implement the PeekMessage things
 * 07/10/02 - 21:40 TODO: implement RCR, we may be getting somewhere after all!!!
 * 07/10/02 - 21:53 TODO: attempt to make redirected_addr by making return it's value through a variable
 *                        instead of through void
 * 08/10/02 - 10:21 RCR implemented
 * 08/10/02 - 10:51 all of this time, redirecting did not actually work, now it does...
 * 08/10/02 - 11:07 i have no idea what's wrong, but some shits occur, i suspect
 *                  an overlay manager error
 * 08/10/02 - 11:46 i think i will monitor memory under 0x9Cf0
 * 08/10/02 - 12:01 memory seems okay, what could be wrong???
 * 08/10/02 - 15:17 fixed a bug in openfile
 * 08/10/02 - 15:38 is RCR wrong???
 * 08/10/02 - 22:20 RCL (and probably RCR) when P2 is not one
 * 08/10/02 - 22:38 RCL implemented in a working version (i hope...)
 * 08/10/02 - 22:49 RCR implemented as well
 * 09/10/02 - 16:37 RCL is not implmeneted right, i'm revising it, also,
 *                  BHole output made entire windows crush and did wierd
 *                  things to the screen, therefore, i did some modifications
 *                  and it's working partially
 * 09/10/02 - 16:46 RCL fixed, output fixed, but what with RCR??? and
 *                  also, is RCR fucking the rest of BHole or is it something else
 * 09/10/02 - 23:33 RCR is working, memory redirecting to pseudoclock is now totally okay
 *                  (it had some bugs) and i fixed some int 67h shit
 * 10/10/02 - 23:15 change: now, pseudoclock is the same value as line (TODO: merge them)
 * 12/10/02 - 20:26 since i have no idea what's causing the errors,
 *                  i will try putting more flag support in the opcodes and check them...
 * 12/10/02 - 23:22 i think i found out another thing, apparently, SHR should set zero flag
 * 12/10/02 - 23:38 more flags needs to be implemented...
 * 13/10/02 - 14:30 2607:333 - something wrong
 * 13/10/02 - 22:30 i'm an idiot, i did a fix in the multimapping EMM interrupt
 *                  and now everything is okay, some opcode is not handled okay though
 * 14/10/02 - 11:40 i need to implement int 16h!!!
 * 14/10/02 - 14:55 oh oh, no VGA output
 * 14/10/02 - 20:02 TODO: after running for a while, BHole gets a divide error
 * 14/10/02 - 23:?? TODO: implement page flipping
 * 17/10/02 - 00:?? converting to linux, TODO: code assumes low endian in some places
 *                  (check all mem2emumem/emumem2mem style calls), SET_MEM_BYTE
 *                   should work fine though)
 * 17/10/02 - 00:50 TODO: a) fix palette, actual hardware change should be done 
 *                           only when painting to screen
 *                        b) add page flipping handling
 * 19/10/02 - 21:40 BHole's working, kinda strangely, stuff is going slow,
 *                  yet it works... (i will try gcc 3.0)
 * 20/10/02 - 13:?? some optimization was done by doing a "dirtiness" check on the surface
 *                  before redrawing it
 * 22/10/02 - 09:50 i will now attempt to make address redirection
 *                  a bit more efficient by making the first checks
 *                  the ones to find normal memory (mem is below 0xA0000 and above 0x00000)
 * 22/10/02 - 10:00 the SET_MEM_BYTE2 routine has been converted, it may hurt future redirections
 *                  but currently we will probably get some extra speed for the change
 * 22/10/02 - 10:40 began optimizing ???_TEXT #defines so that they will do as much
 *                  less GETP????? and ASSIGNP????? as possible
 * 29/10/02 - 19:33 fixed a final bug in the mouse IRQ fake (mousehandler stuff)
 *                  made a first beta release
 * 17/11/02 - 09:02 Moon Bugs and round 42 both work successfuly
 *                  i am now busy making the emu more efficient in terms
 *                  of speed
 * 17/11/02 - 13:48 hyper_speed_mode got us a better speed,
 *                  however, with a price
 * 21/11/02 - 13:23 i finally got the wierd MoM bug,
 *                  it was in NEG, not setting the flags correctly
 * 21/11/02 - 13:29 the graphics bugs in MoM remain
 * 23/11/02 - 11:49 STYX and Exterminator are also tested and supported
 * 24/11/02 - 15:15 name changed to BHole ( Black Hole ),
 *                  first windows version successfuly compiled
 *                  some bugs exist with console output
 * 17/01/02 - 17:36 oh, do i forget logging here,
 *                  anyhow, today i began writing some code
 *                  to allow the wizards.exe <--> magic.exe interaction
 *                  and i also added a new kind of CHECK, CHECK_SPEEDS
 * 18/02/02 - 16:40 some optimizations performed on movsb,
 *                  ADDW_TEXT changed to use unsigned ints
 * 18/02/02 - 17:05 i have moved pseudoclock++ to be near the if which checks
 *                  for it, this may have done somewhat improvment of the
 *                  optimization?, BHole speed seems to have improved a bit
 * 18/02/02 - 17:13 memcpy optimization done on movsb - does
 *                  it improve it???
 * 18/02/02 - 20:38 i have modified pseudoclock increasing to be done
 *                  only after every 4th instruction and not after every
 *                  instruction, thus giving some speed improvment for 
 *                  run_code
 */


#if !defined(WIN32) && !defined(unix)
#error UNSUPPORTED OPERATING SYSTEM!!!
#endif

#include"bhole.h"
#include"mm.h"
#include"loaders.h"
#include"pcspkr.h"
#include"ems.h"
#include"finish.h"

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
 * - movsb will do a check, and if it's working on mapped memory
 *   it will use memcpy with direct pointers to the memory
 * - pseudoclock will be changed only after 4 instructions are processed
 *   and not after each instruction 
 */
/* HYPER_SPEED_MODE is no longer defined here, it is defined in bhole.h */

/* long long support */
/* #define dint long long /* in some compilers, this should be replaced
                        * with __int64
			*/



/* 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

/***** HERE COME DISK STUFF *****/
#define SECTOR_SIZE 512
#define CYLINDER_SIZE SECTOR_SIZE * 9
#define HEAD_SIZE CYLINDER_SIZE * 40
/***** HERE COME SYNCHRONIZATION STUFF *****/

#define CHECKEVENTS_LINEMASK 0x1FFF
	
time_t starttime;

unsigned int ect; /* Event Cycle Time - how much time should come between
                   * 2 calls to handle_events,
	           * ect is measured in micro-seconds
                   */

void init_slowdown(double maxmips)
	/* routine: init_slowdown
	 * purpose: initializes the slowdown mechanism
	 *          see do_slowdown for more information
	 */ 
{
	printf("BHole: init_slowdown: initializing for maxmips %f\n",maxmips);
	ect = (CHECKEVENTS_LINEMASK) / (maxmips * 1000000.0) * 1000000.0; 
	
	 
	printf("BHole: init_slowdown: ect is %d micro seconds\n",ect);
}

void do_slowdown()
	/* routine: do_slowdown
	 * purpose: slows down BHole to certain speed in MIPS
	 *          acts as a maximal limit for speed
	 *          useful for many games, especially
	 *          really old ones
	 */
{
	static struct timeval tvold = {0,0};
	struct timeval tvnew;
	unsigned int tvd;
	gettimeofday(&tvnew,NULL);
	
	tvd = (tvnew.tv_sec  - tvold.tv_sec ) * 1000000 + 
	      (tvnew.tv_usec - tvold.tv_usec);


	if (tvd < ect)
	{
		usleep(ect - tvd);
	}
	tvold = tvnew;	
}

/***** END OF SYNCHRONIZATION STUFF ******/

/***** here are debugging checks ********/

#define CHECK_SPEEDS /* every event CHECK_SPEEDS_EVERYth 
                      * handling period, speed related infos
                      * are outputted
		      */
		      
#define CHECK_SPEEDS_EVERY 2000 /* if this is 10, call report_speeds
                               * every 10th period
			       */

#define CHECK_SPEEDS_TO stdout /* where does CHECK_SPEEDS
                                * outputs it's results
				*/


#undef CHECK_SPEEDS

#define CHECK_GETCSIP /* if debug_vcpu is 1 and this is #defined
                       * get_csip and get_csip_word report
		       * what they have read
		       * notice: you may only #define this
		       * if HYPER_SPEED_MODE is NOT #defined
		       */
		
#undef  CHECK_GETCSIP

#define CHECK_MEMORY /* when the user presses 'o' - 640k of conv memory
                      * are sent to a file called memory
		      */


#undef  CHECK_MEMORY

#define CHECK_USED_OPCODES /* sets keys: 'c' - reset list
                            *            'o' - output opcodes used to reportfile
			    */

#undef CHECK_USED_OPCODES

#ifdef CHECK_USED_OPCODES
unsigned char used_opcodes[256] = {0};
#endif
   
#define CHECK_FLAG_USERS   /* checks after what opcodes are flags
                            * used by conditional jumps
			    */

#undef CHECK_FLAG_USERS

#ifdef CHECK_FLAG_USERS
unsigned char last_opcode;
unsigned long cond_suppliers[256] = {0}; /* each index is set to 1 according to 
                                          * an opcode which gave a flag to 
					  * a conditional JMP 
					  */ 
#endif



      
#define CHECK_INLIM_MEM /* accesses to memory below CHECKLIM1 and above CHECKLIM2 are monitored */
#define CHECKLIM1 0x600
#define CHECKLIM2 0x400

#undef  CHECK_INLIM_MEM

#define CHECK_VIDPLOT /* checks for plotting in video memory
                       * (currently only monitors set_vidmem)
		       */

#undef CHECK_VIDPLOT



#define CHECK_OPCODE_FIRSTTIME /* everytime an opcode is executed for
                                * the first time, a note is made in the
				* report file
				*/


#undef CHECK_OPCODE_FIRSTTIME


#define CHECK_STACK /* this is #define slows us down, but does stack
                     * self-checking
                     */

#undef CHECK_STACK

#define CHECK_OPCODELUT /* same, does opcode LUT, self-checking */

#undef CHECK_OPCODELUT

#define CHECK_MEMPIECE /* self-checking of ONE WORD in the memory */
#define MEMPIECE 0x1B81 /* what part word to check? */

#undef  CHECK_MEMPIECE

#ifdef CHECK_STACK
unsigned char bakstack[65536];
int bstack_index = 64000;
#endif

#define MINIDEBUG /* full debug is only summarized to
                   * reports of cs:ip and line
                   */
#undef  MINIDEBUG


#define DEBUG_CODESTREAM /* creates a file called codstrem */
/* this file contains a binary sequence of all values of cs:ip
 * in order which they had appeared in
 */

#undef DEBUG_CODESTREAM

#ifndef HYPER_SPEED_MODE
int debug_vcpu = 0; /* 0 - no debugging
                     * 1 - full debugging
                     */
#define DEBUG_VCPU_STOP 0xFFFFFFFF
#endif





/**************** HERE COME ASSUMPTIONS ******************/
/* assumptions are things which are not standard
 * and are not used by all 80386 DOS programs
 * this assumptions guarntee speed
 * and sometimes, you gotta have them in order to run the program
 * one possible example of this is ASSUME_CGA
 * all assumations except for assume_com_file are #defined for
 * maximum speed...
 * (they are hard-coded, not soft-coded)
 */

/* assume_com_file means:
 * BHole assumes the executable is in .COM format and not in .EXE format
 * this is a MUST for games which are in .COM format -
 * this is not contained in the executable data structure (see below)
 * instead, it is checked according to the file's extenstion
 */
int assume_com_file = 0;

/* assume_img_file means: 
 * BHole assumes the executable is a bootable image 
 * NOTICE: it's impossible for both assume_com_file and assume_img_file
 *         to be both TRUE
 */
int assume_img_file = 0;

/* assume_video_page_flipping means:
 * BHole assumes the program ALWAYS uses page-flipping MODE-X graphics
 * and only synchronizes screen redraw with page-flipping
 *
 * game needing this: Master Of Magic
 */
#define ASSUME_VIDEO_PAGE_FLIPPING 
#undef  ASSUME_VIDEO_PAGE_FLIPPING

/* ASSUME_CGA means:
 * BHole acts like the video card is a CGA, thus allowing the old and obsolete
 * 160x100 16 colors resolution mode
 *
 * game needing this: round 42, moonbugs
 */
#define ASSUME_CGA

/* assume_int1c_as_irq means:
 * interrupt 0x1C is called as an IRQ every time handle_events
 * is called (notice: this makes an application think time is slow/fast/wierd)
 * 
 * game needing this: round 42, moonbugs
 * 
 * notice: in fact, the real IRQ is interrupt 0x08 which calls interrupt 0x1C
 * notice: by enabling this, int 08 as IRQ is automatically enabled as well
 */

#define ASSUME_INT1C_AS_IRQ

/* assume_int9_as_irq means:
 * interrupt 0x9 is called as an IRQ every time a key is pressed/released
 * 
 * game needing this: round 42, moonbugs
 * 
 * NOTICE: TODO: emulated program cannot disable the int 16 BIOS
 *               service by chaining in here
 */
#define ASSUME_INT9_AS_IRQ

/* assume_direct_mode3 means:
 * this program uses graphical mode 3 directly and outputs stuff
 * to B800 - games needing this: round 42 opening screen
 */
#define ASSUME_DIRECT_MODE3
#undef  ASSUME_DIRECT_MODE3

/* struct containing data for executables BHole supports */
typedef struct
{
	char exename[32];
	char description[512];
	int video_page_flipping;
	int cga_low_res_mode;
	int int9_as_irq;
	int int1c_as_irq;
	int direct_mode3;
} exec_data;
	
exec_data executables_known[] = 
{
{"magic.exe"   ,"Master Of Magic - menu EXE"    ,1,0,0,0,0},
{"wizards.exe" ,"Master Of Magic - gameplay EXE",1,0,0,0,0},
{"round.com"   ,"Round 42"                      ,0,1,1,1,1},
{"moonbugs.com","Moon Bugs - WindMill Software(c) 1983",0,1,1,1,0},
{"styx.com","Styx - WindMill Software(c)",0,1,1,1,0},
{"exterm.com","Exterminator - WindMill Software(c)",0,1,1,1,0},
{"rollo.img","Rollo and the brush bros. - WindMill Software(c)",0,1,1,1,0}
};



#define EXECUTABLES_KNOWN_CNT sizeof(executables_known) / sizeof(exec_data)

/********** END OF ASSUMPTIONS **********************/



/* this array contains both emumemory and registers */
/* "architecture" of emustuff:
 * 1) registers
 * 2) memory
 */

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


unsigned char __emustuff__[REGSSIZE + MEMSIZE];


#define SWAP(a,b) { int t; t = a; a = b; b = t; }

/* virtual machine: */
#define EXECNAME execname

char *execname;
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)


unsigned char *reglutb[8];
unsigned int   reglutbo[8] = {0 - REGSSIZE,2 - REGSSIZE,
                              4 - REGSSIZE,6 - REGSSIZE,
			      1 - REGSSIZE,3 - REGSSIZE,
			      5 - REGSSIZE,7 - REGSSIZE}; /* offsets are from memory */


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




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


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 */


/************************************************/
/********* HERE COME MEMORY STUFF ****************/

#define __memory__ (__emustuff__ + REGSSIZE)

			   /* __memory__ should not be used
                            * except for in places, since C has no
			    * "private:" declaration, i use these underscores
			    * ("_"s instead)
			    */




#define GET_AH() ( *(((unsigned char*)&ax) + 1) )
#define GET_AL() ( (unsigned char)ax )




/* MACRO CANCELLED - ALLOWS DIRECT MEMORY ACCESS, THEREFORE FORBIDDEN
 * #define GET_MEM_ADDR(seg,offset) \
 * (  (memory + (offset) + ((seg) << 4) ))
 */


/* the following macros are used to get stuff from memory
 * why we cannot allow direct memory address?
 * that is a good question.
 * with a simple answer, in the CPU, before every memoery address is
 * accessed redirections can be made to different places,
 * if we allow direct access to the array of "memory" it won't be
 * like that, therefore it had it's name changed to "__memory__"
 * which is an ugly name that no one will use and instead
 * people will use the memory touching interface which involves the following
 * macros:
 * GET_MEM_BYTE   (gets a byte from emu memory by segment and index  )
 * SET_MEM_BYTE   ( sets a byte from emu memory by segment and index )
 * SET_MEM_BYTE2  ( sets a byte from emu memory by a direct offset   )
 * GET_MEM_WORD   ( same as GET_MEM_BYTE, but gets a word            )
 * SET_MEM_WORD   ( same as SET_MEM_BYTE, but sets a word            )
 * all other functions ending with a 2 are a direct offset versions
 * of their friends with similar names
 *
 * the older versions were modified to include PERFECT
 * memory redirection (yes, it slows down BHole, but allows
 *                     better emulation)
 *
 *
 *
 * also there are routines to act like memcpy between real memory
 * and emu memory, these are called:
 *
 * emumem2mem  - emu memory  ----> real memory
 * mem2emumem  - real memory ----> emu memory
 * emudmem2mem
 * mem2emudmem
 *
 *
 *
 * NOTICE: to point to real memory we use a pointer
 *         to point to emu  memory we use a pair of 2 ints, segment and index
 *         in emudmem, you use a single pure offset
 *
 */


#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)); \
 }





void emudmem2mem(unsigned char *mem,unsigned int offset,int len)
{
	while(len > 0)
	{
		*mem = GET_MEM_BYTE2(offset);
		len--;
		offset++;
		mem++;
	}
}

void mem2emudmem(unsigned int offset,unsigned char *mem,int len)
{
	while(len > 0)
	{
		SET_MEM_BYTE2(offset,*mem);
		len--;
		offset++;
		mem++;
	}
}




/***** HERE COME MEMORY MANAGMENT STUFF *****/

/* NOTICE: MCBs are managed slightly different than DOS
 *         BHole does not bother filling all fields.
 *         the only fields which are important are:
 *         1) signature (must be 'M' cannot be 'Z' like in normal DOS)
 *         2) process id/psp (0x0000 means free)
 *         3) size of block
 */
 

/* TODO: should mem_alloc/realloc routines set the newly allocated
 *       memory to all-zeroes (as calloc does) or not
 */

unsigned long imgsize; /* includes all allocated memory */


/* EXE */
unsigned char header_top[0x20];

#define GET_HEADER_SHORT(offset) \
( *((unsigned short*)(header_top + (offset))) )



/* get_csip and his friend... */

#ifndef HYPER_SPEED_MODE
unsigned int get_csip_word()
#else
__inline__ unsigned int get_csip_word()
#endif
	/* routine: get_csip_word()
	 * purpose: returns the word pointed by CS:IP
	 */
{
#ifndef HYPER_SPEED_MODE
	unsigned int ret = GET_MEM_WORD(cs,ip);
#ifdef CHECK_GETCSIP
	if (debug_vcpu) printf("BHole: get_csip_word() %x:%x -> %x\n",cs,ip,ret);
#endif
	ip+=2;
	return ret;
#else
#if SDL_BYTEORDER == SDL_LITTLE_ENDIAN 
#if USHRT_MAX == 65535 
	unsigned int ret = ((unsigned short*)__memory__)[(cs<<4)+ip];
	ip+=2;
	return ret;
#endif
#else 
	unsigned int ret = __memory__[(cs<<4)+ip  ] + 
	                  (__memory__[(cs<<4)+ip+1]<<8);
	ip+=2;
	return ret;
	
#endif
#endif	
}


#ifndef HYPER_SPEED_MODE
unsigned int get_csip()
#else
__inline__ unsigned int get_csip()
#endif
	/* routine: get_csip()
	 * purpose: returns the byte pointed by CS:IP
	 */
{
#ifndef HYPER_SPEED_MODE
	unsigned char ret;
	ret = GET_MEM_BYTE(cs,ip);
#ifdef CHECK_GETCSIP
	if (debug_vcpu) printf("BHole: get_csip_word() %x:%x -> %x\n",cs,ip,ret);
#endif
	ip++;
	return ret;
#else
	return __memory__[(cs<<4)+ip++];
#endif
}


void goto_interrupt(unsigned int csip);

/* modrm */
#define MOD(x) (((x) & 0xC0) >> 6)
#define REG(x) (((x) & 0x38) >> 3)
#define RM(x)  (((x) & 0x07)     )




/* opcodes */
typedef int (*ochandler()); /* 0 - failure, 1 - success */
/* explanation of ochandler
 * params: p1, p2 - pointers to parameters
 *         byte - 1 if it's a byte operation, 0 if it's a word operation
 */

/* NOTICE:
 * it is possible to implenet one handler
 * for all, but it's less efficient
 * and has some potential problems...
 * problem with this one is that i will have to fix everything
 * 3 times... (or maybe not???)
 *
 * DAMN THE STUPID x86 architecture, i bet it would never
 * have happened if i tried emulating a RISC machine :)
 *
 *
 * good news, i have reduced the amount of lines needed
 * now, the code for each handler is not going to be written a lots,
 * but just once, using special macros that will be #defined
 * differently for each declaration (there are 3 of them you know...)
 */

/* solution to the AOR problem is a simple
 * one, the registers are now placed near the memory
 * so GET_MEM_BYTE2(value_less_than_zero) can give you access to them
 */

#define DEF_OCHANDLER(handler)   \
void handler() { \


#define END_OCHANDLER }

typedef struct
{
	void *handler;
} opcode;

/* parameter types */
#define ESCAPE 0xFFFF /* escape, operand is the next byte */
                      /* (special second byte operand state) */
#define AOR 0  /* address or general register (from rm field) */
#define CA  1  /* constant address (offset only) */ /* 1 */
#define IMM 2  /* immediate */        /* 10 */
#define RES 4  /* ES register */      /* 100 */
#define RCS 8  /* CS register */      /* 1000 */
#define RSS 16    /* 10000 */
#define RDS 32    /* 100000 */
#define RAX 64    /* 1000000 */  /* can also be used as AL */
#define RCX 128   /* 10000000 */                /* also CL */
#define RDX 256   /* 1 00000000 */              /* also DL */
#define RBX 512   /* 10 00000000 */             /* also BL */
#define RSP 1024  /* 100 00000000 */            /* also AH */
#define RBP 2048  /* 1000 00000000 */           /* also CH */
#define RSI 4096  /* 10000 00000000 */          /* also DH */
#define RDI 8192  /* 100000 00000000 */         /* also BH */

#define RAL RAX
#define RCL RCX
#define RDL RDX
#define RBL RBX
#define RAH RSP
#define RCH RBP
#define RDH RSI
#define RBH RDI




/* general register (from reg field */ /* only 8 GRs*/
#define R   (RAX | RBX | RCX | RDX | RSP | RBP | RSI | RDI)

/* segment register (from reg field)*/ /* only 4 GRs */
#define SR  (RCS | RDS | RES | RSS)


#define BYTE 0x4000 /* 01000000 00000000 */ /*0 in MSB  - existance */
#define WORD 0x0000 /* 00000000 00000000 */
#define NONE 0x8000 /* 10000000 00000000 */


int imm; /* temporary used to hold immediate values */
unsigned short override_seg = 0xFFFF; /* override segment */ /* -1 = none */
unsigned char rep;

#define NOREP 0
#define REPNE 1
#define REP   2
#define REPE  REP

opcode opcodes[256];

/********************* CPU grp stuff **************************/

unsigned char grp_modrm;

#define DECLARE_GRP_OPCODE_ARRAY(name) \
opcode name##_ops[8]; int name##_ops_cnt = 0;


DECLARE_GRP_OPCODE_ARRAY(grp1EbIb)
DECLARE_GRP_OPCODE_ARRAY(grp1EwIw)
DECLARE_GRP_OPCODE_ARRAY(grp1EwIb)
DECLARE_GRP_OPCODE_ARRAY(grp2Eb1)
DECLARE_GRP_OPCODE_ARRAY(grp2Ew1)
DECLARE_GRP_OPCODE_ARRAY(grp2EbCL)
DECLARE_GRP_OPCODE_ARRAY(grp2EwCL)
DECLARE_GRP_OPCODE_ARRAY(grp2EbIb)
DECLARE_GRP_OPCODE_ARRAY(grp2EwIb)
DECLARE_GRP_OPCODE_ARRAY(grp3Eb)
DECLARE_GRP_OPCODE_ARRAY(grp3Ew)
DECLARE_GRP_OPCODE_ARRAY(grp4Eb)
DECLARE_GRP_OPCODE_ARRAY(grp5Ew)


#define ADD_GRP_OPCODE_HANDLER(group,paramtypes,handlername) \
group##paramtypes##_ops[group##paramtypes##_ops_cnt++].handler = handlername##paramtypes##_##group; \


/* creates a handle for an entire grp opcode (which redirects
 * according to a LUT to the required routine
 */
#define DECLARE_GRP_MULTIPLEXER_HANDLER(fullgroup)			\
DEF_OCHANDLER(handle_##fullgroup)					\
{									\
	grp_modrm = get_csip();						\
	/* normal modrm extraction, however, REG is not a register
	 * but an index to the handler LUT
	 */								\
	((ochandler*)fullgroup##_ops[REG(grp_modrm)].handler)();	\
}									\
END_OCHANDLER								\

DECLARE_GRP_MULTIPLEXER_HANDLER(grp1EbIb)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp1EwIw)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp1EwIb)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp2Eb1)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp2Ew1)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp2EbCL)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp2EwCL)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp2EbIb)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp2EwIb)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp3Eb)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp3Ew)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp4Eb)
DECLARE_GRP_MULTIPLEXER_HANDLER(grp5Ew)

/**********************end of CPU grp stuff ********************/





/**** HERE ARE THINGS RELATED TO VIDEO *****/
/* CGA stuff !? */
unsigned char cga_modecon = 0xA1;
unsigned char cga_stat = 0x0;
unsigned char cga_pal = 0xFF;
#ifdef ASSUME_VIDEO_PAGE_FLIPPING
unsigned char __vidmemory__[262144];
#endif

/* VGA stuff */
unsigned int vgamode = 3;
unsigned int vgadisp = 0; /* disp = display */
SDL_Surface *vidsurface = NULL;

int vga_surface_dirtiness = 0; /* only when dirtiness is greater or equal to VIDSURFACE_DIRTINESS_MAX,
                                * the screen is refreshed
	                        * surface_dirtiness is increased for every set_vidmem call
			        */

/* cursor stuff */
unsigned char cursor_top; /* sizes of */
unsigned char cursor_bottom;/*cursor  */
unsigned char cursor_x = 0; /* cursor */
unsigned char cursor_y = 0; /* coordinates */

#define VIDSURFACE_DIRTINESS_MAX 200000


#define CGA_STATUS_PORT 0x3DA /* used for retrace checkings */

#define CGA_MODECON_PORT 0x3D8


/* VGA sequence controller stuff */
#define VGA_SEQREG_PORT 0x3C4
#define VGA_SEQDAT_PORT 0x3C5

unsigned char seqcon[0x20] = {0,0,0x0F,0,0x0E}; /* sequence controller registers(?) */
unsigned char seqreg;                        /* what reg we write/read to/form */


#define vga_writeplanes seqcon[2]
#define vga_readplane gdcregs[4]
#define vga_bitmask gdcregs[8]

#define VGA_PALINDEX_PORT 0x3C8
#define VGA_PALSET_PORT 0x3C9


int palindex;
int palpos; /* position inside chosen palette cell */
SDL_Color palette[256];

/* seq registers: */
#define MEMORY_MODE 0x04


/* VGA GDC regs */
#define VGA_GDCREG_PORT 0x3CE
#define VGA_GDCDAT_PORT 0x3CF

unsigned char gdcregs[0x20] = {0,0,0,0,0,0,0,0,0xFF};
unsigned char gdcreg;



/* VGA CRT controller stuff */
#define VGA_CRTREG_PORT 0x3D4
#define VGA_CRTDAT_PORT 0x3D5


unsigned char crtcon[0x20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xA3}; /* CRT controller registers */
unsigned char crtreg;       /* what reg we write/read to/from */
/* CRT registers: */



/* video and memory stuff */
#define VRM1POS (0x484) /* in this place in memory, you can get number
                         * of rows - 1
                         */


/* NOTICE: refresh_screen currently also handle slowdown for some
 * apps
 */
/* TODO: can video page flipping assuming be used to make only 64k of
 *       __vidmemory__
 */
#ifdef ASSUME_VIDEO_PAGE_FLIPPING
/* __inline void refresh_screen()
{
	if (vidsurface != NULL && vga_surface_dirtiness >= VIDSURFACE_DIRTINESS_MAX)
	{
		SDL_LockSurface(vidsurface);
		memcpy(vidsurface->pixels,__vidmemory__ /* +  (crtcon[0xC] << 10) + (crtcon[0xD] << 2) */ /*,320 * 200);
		SDL_UnlockSurface(vidsurface);
		SDL_SetPalette(vidsurface,SDL_PHYSPAL,palette,0,256);
		SDL_UpdateRect(vidsurface,0,0,0,0);		
		vga_surface_dirtiness = 0;
	}
} OBSOLETE OBSOLETE OBSOLETE OBSOLETE OBSOLETE OBSOLETE */

__inline void force_refresh_screen()
	/* the different between this one and the previous one 
	 * is that here there's no dirtiness check
	 */
{
#ifdef CHECK_VIDPLOT
	vga_surface_dirtiness = 0;
#endif
	if (vidsurface != NULL)
	{
		SDL_LockSurface(vidsurface);
		memcpy(vidsurface->pixels,__vidmemory__ + (crtcon[0xC] << 10) + (crtcon[0xD] << 2),320 * 200);
		SDL_UnlockSurface(vidsurface);
		SDL_SetPalette(vidsurface,SDL_PHYSPAL,palette,0,256);
		SDL_UpdateRect(vidsurface,0,0,0,0);		
	}
}

#else
int cga_16_mode = 0; /* 1 - on, 0 - off */
__inline void refresh_screen()
{
	/* TODO: optimize refresh to make use of more pointers and less
	 * indices, less variables will also do good
	 */
	static int cnt = 0;
	static unsigned char outputbuf[81*25];
	unsigned int i,j,k = 0xB8000;
	unsigned char *p = outputbuf;
#define p2 ((unsigned char*)i)
	struct timeval tv;
	if (!vga_surface_dirtiness) return;
	if (vgamode == 0x4)
	{
		SDL_LockSurface(vidsurface);
		p = __memory__ + 0xB8000;
		p2 = vidsurface->pixels;
		for(j = 0;j < 200;j+=2)
		{
			for(k = 0;k < 320;k+=4)
			{	
				/* TODO: direct access to __memory__ is stupid */
				*p2 = (*p & 0xC0) >> 6; p2++;
				*p2 = (*p & 0x30) >> 4; p2++;
				*p2 = (*p & 0x0C) >> 2; p2++; 
				*p2 = (*p & 0x03)     ; p2++;
				p++;
			}
			p2+=320;
		}
		SDL_UnlockSurface(vidsurface);
		
		p = __memory__ + 0xB8000 + 8192;
		p2 = vidsurface->pixels + 320;
		for(j = 0;j < 200;j+=2)
		{
			for(k = 0;k < 320;k+=4)
			{	
				/* TODO: direct access to __memory__ is stupid */
				*p2 = (*p & 0xC0) >> 6; p2++;
				*p2 = (*p & 0x30) >> 4; p2++;
				*p2 = (*p & 0x0C) >> 2; p2++; 
				*p2 = (*p & 0x03)     ; p2++;
				p++;
			}
			p2+=320;
		}
		SDL_UpdateRect(vidsurface,0,0,0,0);				 
	}
	else if (cga_modecon & 0x80)
	{
		tv.tv_sec = 0;
		tv.tv_usec = 10000;
		
		if (cnt & 0x03)
			select(0,NULL,NULL,NULL,&tv);
			
		cnt++;
		
		for(i = 0;i < 25;i++)
		{
			for(j = 0;j < 80;j++)
			{
				*p = GET_MEM_BYTE2(k);
				p++;
				k+=2;
			}
			*p = '\n';
			p++;
		}
		fwrite(outputbuf,81*25,1,stdout);
	}
	else 
	     /* TODO: unefficient code, i assume all CGA apps do not require
	      *       speed
	      */
	{
		vga_surface_dirtiness = 0;
		if (!cga_16_mode) 
		{
			vidsurface = SDL_SetVideoMode(640,400,8,SDL_SWSURFACE) ; 
			SDL_SetPalette(vidsurface,SDL_PHYSPAL,palette,0,16);
			cga_16_mode = 1;
		}
/*		tv.tv_sec = 0;
		tv.tv_usec = 6000;
		select(0,NULL,NULL,NULL,&tv); /* useful slowdown */
	
		
		SDL_LockSurface(vidsurface);	
		p2 = vidsurface->pixels;
		p = __memory__ + 0xB8000;
		for(j = 0;j < 100;j++)
		{
			for(k = 0;k < 80;k++) /* 160 / 2 = 80 */
			{
				/* draw every 4th line */
				if (*p == 221) 
				{
				/* calc every 4th pixels and copy the rest...*/
					*(p2  ) = *(p2+1) = *(p2+2) = *(p2+3) =  
						 *(p+1) & 0x0F;
					*(p2+4) = *(p2+5) = *(p2+6) = *(p2+7) =
						(*(p+1) & 0xF0) >> 4;		 
				}	
				else if (*p == 222)
				{
					*(p2  ) = *(p2+1) = *(p2+2) = *(p2+3) =
						(*(p+1) & 0xF0) >> 4;
					*(p2+4) = *(p2+5) = *(p2+6) = *(p2+7) =
						 *(p+1) & 0x0F;		 			
				}		
				p2+=8;
				p+=2;
			}
			memcpy(p2,p2 - 640,640); p2+=640;
			memcpy(p2,p2 - 640,640); p2+=640;
			memcpy(p2,p2 - 640,640); p2+=640;
		}	
		
		SDL_UnlockSurface(vidsurface);
		SDL_UpdateRect(vidsurface,0,0,0,0);				 

	}
}
#undef p2
#endif


#ifdef ASSUME_CGA
#define INIT_PALETTE(i,r_,g_,b_) palette[i].r = r_; palette[i].g = g_; palette[i].b = b_; palette[i].unused = 0
__inline void init_cga()
{
	vidsurface = SDL_SetVideoMode(160,100,8,SDL_SWSURFACE) ;
	/* TODO: fix the palette settings */
	INIT_PALETTE(0,0,0,0);
	INIT_PALETTE(1,0,0,192);
	INIT_PALETTE(2,0,192,0);
	INIT_PALETTE(3,0,192,192);
	INIT_PALETTE(4,192,0,0);
	INIT_PALETTE(5,192,0,192);
	INIT_PALETTE(6,192,192,0);
	INIT_PALETTE(7,192,192,192);
	INIT_PALETTE(8,64,64,64);
	INIT_PALETTE(9,64,64,255);
	INIT_PALETTE(10,64,255,64);
	INIT_PALETTE(11,64,255,255);
	INIT_PALETTE(12,255,64,64);
	INIT_PALETTE(13,255,64,255);
	INIT_PALETTE(14,255,255,64);
	INIT_PALETTE(15,255,255,255);
	
	SDL_SetPalette(vidsurface,SDL_PHYSPAL,palette,0,16);
}

__inline void uninit_cga()
{
}

#endif

/*******************************************/


/***** here are things related to keyboard *****/
#define KEYBUF_SIZE 32
unsigned short keybuf[32];
unsigned short keybufi = 0;

#define KEYBOARD_DATA_PORT 0x60

unsigned char keydata; /* current key pressed used for keyboard port I/O */




void fill_keybuf(SDL_KeyboardEvent *key)
/* adds 1 char to keybuf */
{
	extern unsigned char scancode[1024]; /* check out key_scancodes.c */
	int i;
	keydata = scancode[key->keysym.sym];
	switch(key->keysym.sym)
	{
	case 'c':
		if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE)
			SDL_ShowCursor(SDL_DISABLE);
		else
			SDL_ShowCursor(SDL_ENABLE );
		break;
	case 'd':
		for(i = 0;i < 256;i++)
		{
			if (GET_MEM_WORD2((i*4)+2) != 0xFFFF)
			{
				fprintf(report_file,"BHole: interrupt %x is %x:%x\n",i,GET_MEM_WORD2(i*4+2),GET_MEM_WORD2(i*4));
			}
		}
		
		break;
#ifdef CHECK_FLAG_USERS
	case 'f':
		fputs("BHole: conditonal jumps use flags by opcodes:\n",report_file);
		for(i = 0;i < 256;i++)
		{
			if (cond_suppliers[i] != 0)
				fprintf(report_file,"BHole: %x\n",i);
		}
		fflush(report_file);
		break; 
	case 'c':
		puts("BHole: resetting cond_suppliers");
		for(i = 0;i < 256;i++)
		{
			cond_suppliers[i] = 0;
		}
		break;
#endif	
#ifdef CHECK_USED_OPCODES
	case 'o':
		fputs("BHole: opcodes found:\n",report_file);
		for(i = 0;i < 256;i++)
		{
			if (used_opcodes[i] != 0)
				fprintf(report_file,"BHole: %x\n",i);
		}
		fflush(report_file);
		break; 
		
	case 'c':
		puts("BHole: resetting used_opcodes");
		for(i = 0;i < 256;i++)
		{
			used_opcodes[i] = 0;
		}
		break;
	
#endif
#ifdef CHECK_MEMORY
	case 'o':
	{
		FILE *fm;
		fm = fopen("memory","wb");
		fwrite(__memory__,655360,1,fm);
		fclose(fm);
	}
#endif
	}		
	if (keybufi == KEYBUF_SIZE) return; /* TODO: possibly a wrong solution */	

	keybuf[keybufi] = (key->keysym.sym & 0xFF) | (keydata << 8);	
	keybufi++;
}

void empty_keybuf()
/* eliminiates one char from keybuf */
/* TODO: make a circlized array (wrap around at the end
 *       for more efficiency
 */
{
	int i;
	for(i = 0;i < keybufi;i++)
	{
		keybuf[i] = keybuf[i+1];
	}
	keybufi--;
}
/***** here is the end of keyboard related stuff *****/

 



/***** HERE ARE THINGS REALTED TO MOUSE *******/

#define MOUSE_CS 0xF000
#define MOUSE_IP 0x0000


/* these variables always contain mouse info */
int      mouseposx,mouseposy,mousebtns;

/* these few things are for an int 33 handler
 * (for more info, read description of int 33 AH = 0x0C)
 */
int mouse_callmask = 0;
int mousehandler_ip = 0;
int mousehandler_cs = 0;


void handle_mousemove(SDL_MouseMotionEvent *motion)
{
	extern int allow_events;
	int handle_storea(void);
	void do_push(int);
	/* TODO: handler implementation is quite fucked up
	 *       in some places 
	 *	 (example: call mask)
	 */
	mouseposx = motion->x;
	mouseposy = motion->y;
		
	if (mousehandler_cs && mousehandler_ip && (mouse_callmask & 1))
	{
		do_push(flags);
		do_push(cs);
		do_push(ip);
		handle_storea();
		cs = MOUSE_CS;
		ip = MOUSE_IP;
		ax = mouse_callmask;
		bx = mousebtns;
		cx = mouseposx*=2; /* why??? */
		dx = mouseposy;
		/* TODO: set si and di as well */
		SET_MEM_WORD(MOUSE_CS,MOUSE_IP + 3,mousehandler_ip);
		SET_MEM_WORD(MOUSE_CS,MOUSE_IP + 5,mousehandler_cs); 
		if (allow_events == 0) puts("FUCK");
		allow_events = 0;
	}
}


void handle_mousebtn(SDL_MouseButtonEvent *btn)
{
	extern int allow_events;
	int do_handler = 0;
	int handle_storea(void);
	void do_push(int);
	mouseposx = btn->x;
	mouseposy = btn->y;
	if (btn->button == SDL_BUTTON_LEFT)
	{
		if (btn->state == SDL_PRESSED) 
		{
			mousebtns|=1;
			do_handler |= mouse_callmask & 2;
		}
		else 
		{
			mousebtns &= ~1;
			do_handler |= mouse_callmask & 4;
		} 
	}

	if (btn->button == SDL_BUTTON_RIGHT)
	{
		if (btn->state == SDL_PRESSED)
		{
			mousebtns |= 2;
			do_handler |= mouse_callmask & 8;
		}
		else 
		{
			mousebtns &= ~2;
			do_handler |= mouse_callmask & 16;
		}
	}

	if (mousehandler_cs && mousehandler_ip)
	{
		do_push(flags);
		do_push(cs);
		do_push(ip);
		handle_storea();
		cs = MOUSE_CS;
		ip = MOUSE_IP;
		ax = mouse_callmask;
		bx = mousebtns;
		cx = mouseposx*=2; /* why??? */
		dx = mouseposy;
		/* TODO: set si and di as well */
		SET_MEM_WORD(MOUSE_CS,MOUSE_IP + 3,mousehandler_ip);
		SET_MEM_WORD(MOUSE_CS,MOUSE_IP + 5,mousehandler_cs);
		allow_events = 0; 
	}

}

/***** END OF THINGS RELATED TO MOUSE *******/


/*********** BHole PSEUDO SHADOW ROM  **********/
/* INFORMATION: (TODO: programs should be prevented from writing in here)
 * i cannot promise i will always update this
 * F000:0 - F000:8 - mouse handler
 * F000:FFFF - an IRET
 * F000:E000 - F000:E200, contains FAKE
 *             interrupt handlers,
 *             these are in fact the handle_bholeint opcode with
 *             an imm 8
 */

#define IRET_CS 0xF000
#define IRET_IP 0xFFFF

#define INT_CS 0xF000
#define INT_IP 0xE000


#define HANDLE_BHOLEINT_OPCODE 0xF4 /* let's hope no body uses HLT =8-) */

/* the handle_bholeint opcode:
 * this opcodes takes one argument (an 8-bit unsigned immediate),
 * this immediate is then used as an internal service request
 * from the BHole emulator, this is used to emulate ROM BIOS interrupts
 * without actually writing the code for them, the sequence:
 * "HANDLE_BHOLEINT_OPCODE, opcode-number" is parsed
 * as a call to the required internal BHole routine
 * (a real routine that is REAL code, not emulated code)
 */

#define IRET_FAKEPTR_VALUE 0xF000FFFF


__inline void init_mouse_rom()
	/* currently this code only sets up a small mouse handler
	 * in MOUSE_CS:MOUSE_IP
	 */
{
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP  ,0x90);
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+1,0x90); /* TODO: should this be NOP??? */
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+2,0x9A); 
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+3,0x00); /* TODO: there is a more efficient way filling these */
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+4,0x00);  
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+5,0x00);
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+6,0x00);
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+7,0xF1);
	SET_MEM_BYTE(MOUSE_CS,MOUSE_IP+8,0xCF);	
}

__inline void init_iret_rom()
{
	SET_MEM_BYTE(IRET_CS,IRET_IP,0xCF);
}

__inline void init_int_rom()
{
	int i;
	for(i = 0;i < 512;i+=2)
	{
		SET_MEM_BYTE(INT_CS,INT_IP+i  ,HANDLE_BHOLEINT_OPCODE);
		SET_MEM_BYTE(INT_CS,INT_IP+i+1,i >> 1                ); 
		/* (i << 8 >> 1) --> (i << 7) */
	}
}

__inline void init_rom()
{
	init_mouse_rom();
	init_iret_rom();
	init_int_rom();
}

/***** HERE COMES PC-SPEAKER EMULATION *****/
/* notice: most emulation code is in pcspkr.c
 * this is only code for emulating the I/O
 * interface, the emulation of the pc speaker itself
 * is done in pcspkr.c
 */

#define PIT_PORT 0x43


unsigned short pcspkr_freq;
unsigned char port_61 = 0x2C;
unsigned char pcspkr_mode;
unsigned char pcspkr_done = 0; /* 0 - 16-bit sound not ready
                                * 1 - got half of the sound
				* we only use bit 0
				* of pcspkr_done
				*/ 
unsigned short pcspkr_divisor = 0; /* what the fuck is this??? */

/* TODO: inefficient, combine pcspkr_done this to pcspkr_mode */
/***** HERE COMES SDL EVENT HANDLER ******/
/* TODO: SwEw with SS should change allow_events to zero */
int allow_events = 1;  /* this one is in fact the IF flag */

/* NOTICE: IRQs should only be called from handle_events 
 *         or from routines called by handle_events
 */
/* TODO: make a standard BHole interface for faking IRQs */

void handle_events()
{
	SDL_Event event;
	unsigned int t;
	extern unsigned char scancode[256]; /* check out key_scancodes.c */
	void do_push(int);
	/* BUGFIX: i now also check for override_seg and rep, to prevent a case in which
	 * an IRQ occurs after a prefix (CS: or DS: or REP etc.) but before the instruction
	 */
	event.type = SDL_NOEVENT;
	if (!allow_events || override_seg != 0xFFFF || rep != NOREP) return;
	SDL_PollEvent(&event);
	do
	{
		switch(event.type)
		{
		case SDL_KEYDOWN:
			/* fill_keybuf also fills keydata */
			fill_keybuf(&event.key);
			
#ifdef ASSUME_INT9_AS_IRQ
			t = GET_MEM_WORD2(0x09<<2)   +						
			   (GET_MEM_WORD2((0x09<<2)+2) << 16);					
											
			goto_interrupt(t);
#endif			
			break;
		case SDL_KEYUP:
#ifdef ASSUME_INT9_AS_IRQ
			t = GET_MEM_WORD2(0x09<<2)   +						
			   (GET_MEM_WORD2((0x09<<2)+2) << 16);					
											
			goto_interrupt(t);
#endif			
			keydata = scancode[event.key.keysym.sym] | 0x80;
			break;
		case SDL_MOUSEMOTION:
		/*	puts("mousemove"); */
			handle_mousemove(&event.motion);
			break;
		case SDL_MOUSEBUTTONDOWN:
		case SDL_MOUSEBUTTONUP:
			handle_mousebtn(&event.button);
#ifdef ASSUME_INT1C_AS_IRQ
			break;
		default:
			t = GET_MEM_WORD2(0x08<<2)   +						
			   (GET_MEM_WORD2((0x08<<2)+2) << 16);					
											
			goto_interrupt(t);

			t = GET_MEM_WORD2(0x1C<<2)   +						
			   (GET_MEM_WORD2((0x1C<<2)+2) << 16);					
											
			goto_interrupt(t);
			
			
#endif		
		}			
	}
	while( allow_events && SDL_PollEvent(&event) && override_seg == 0xFFFF && rep == NOREP);

	
}
/***** END OF SDL EVENT HANDLER ************/




#define MAXFILES 65
FILE *fhandles[MAXFILES-1] = {(FILE*)666,(FILE*)69,NULL,(FILE*)69,(FILE*)666};
/*
 *
 *usually, the first 5 handles are taken...
 *notice one of them points to stdout
 */



void add_elem(unsigned short a,unsigned short b,void *c)
	/* routine: add_elem
	 * purpose: adds an element to the opcode table
	 * notice : parameters a and b are ignored
	 */
{
	static int i;
	if (i == 256)
	{
		fputs("add_elem: can only call add_elem 256 times\n",report_file);
		return;
	}
	opcodes[i++].handler  = c;


}

int handle_escape()
{
	return 0;
}



int handle_override()
{
	return 0;
}

int handle_das()
{
	return 0;
}

int handle_daa()
{
	return 0;
}


int handle_aaa()
{
	return 0;
}


int handle_aas()
{
	return 0;
}




int handle_todo()
{
	return 0;
}

int handle_invalid()
{
	return 0;
}

int handle_jo()
{
	return 0;
}

int handle_jno()
{
	return 0;
}




int handle_jp()
{
	return 0;
}

int handle_jnp()
{
	return 0;
}





/*************** OCHANDLERs  initializers ************/

/* first, we handle parameter specific opcodes
 * we define their function body as a macro
 * which uses other macros, these macros are #defined
 * for every different kind of parameter
 * for information on the parameter naming convention
 * read the comment near the opcode table
 */

/* explanation of macros:
 * ASSIGNPxBYTE - assigns a byte value to whatever pointed by
 *                the routine's first param
 * ASSIGNPxWORD - same thing, but with a 2-byte value
 * GETPxBYTE - gets the byte pointed by the routine's first param
 * GETPxWORD - same thing but with a word
 * GETPxOWORD - gets the byte pointed by the routine's first param, with
 *              an offset given as parameter to the macro
 */

/* NOTICE: it seems like my lcc has a bug, or maybe it's the C specification
 *         that is insane, after a \ in a #define, there must be \n
 *         and no spaces are allowed
 */

void do_flags_byte(int c);
void do_flags_word(int c);

/* TODO: improve "caching" to only cache memory references??? */

/* TODO: in some cases, we do not need a real pointer to what we look for
 * (in case we are not going to change a Gb parameter's value,
 *  in that case, only it's value can be extracted)
 */
/* TODO: i have the feelings some of the mask LUTs are not efficient
 *       as they seem
 */

unsigned long  calc_modrm_addrb(unsigned char modrm);
unsigned long  calc_modrm_addrw(unsigned char modrm);



/* these 2 are empty initializers */
#define DXAL_INIT
#define ALDX_INIT

#define EB1_GRP2_INIT						\
GETEB_GRP(p1);							\

#define EW1_GRP2_INIT						\
GETEW_GRP(p1);							\

#define EBCL_GRP2_INIT						\
GETEB_GRP(p1);							\
/* TODO: remove this code */					\
if (cl > 8)  fputs("BHole: EBCL_GRP2_INIT: ALERT!!!\n",report_file);		\

#define EWCL_GRP2_INIT						\
GETEW_GRP(p1);							\
/* TODO: remove this code */					\
if (cl > 16) fputs("BHole: EWCL_GRP2_INIT: ALERT!!!\n",report_file);		\


#define EB_GRP3_INIT						\
GETEB_GRP(p1)							\

#define EW_GRP3_INIT						\
GETEW_GRP(p1)							\

#define EB_GRP4_INIT						\
GETEB_GRP(p1)							\

#define EW_GRP5_INIT						\
GETEW_GRP(p1)							\


#define EBIB_INIT						\
unsigned char modrm = get_csip();				\
GETEB(p1);							\
p2 = get_csip();						\

#define EWIW_INIT						\
unsigned char modrm = get_csip();				\
GETEW(p1);							\
p2 = get_csip_word();						\

#define EBIB_GRP1_INIT						\
GETEB_GRP(p1);							\
p2 = get_csip();						\

#define EBIB_GRP2_INIT EBIB_GRP1_INIT


#define EWIW_GRP1_INIT						\
GETEW_GRP(p1);							\
p2 = get_csip_word();						\

/* NOTICE: only in EWIB_GRP1 the byte is signed,
 *         here it is unsigned
 */

#define EWIB_GRP2_INIT						\
GETEW_GRP(p1);							\
p2 = (unsigned char)get_csip(); 				\


/* NOTICE: in EWIB_GRP1_INIT we need to type cast to a (signed char) */

#define EWIB_GRP1_INIT						\
GETEW_GRP(p1);							\
p2 = (signed char)get_csip();					\


#define EBGB_INIT 						\
unsigned char modrm = get_csip();				\
GETEB(p1);							\
GETGB(p2);							\


#define GBEB_INIT						\
unsigned char modrm = get_csip();				\
GETGB(p1);							\
GETEB(p2);							\

#define EWGW_INIT 						\
unsigned char modrm = get_csip();				\
GETEW(p1);							\
GETGW(p2);							\

#define GWEW_INIT						\
unsigned char modrm = get_csip();				\
GETGW(p1);							\
GETEW(p2);							\

#define EWSW_INIT 						\
unsigned char modrm = get_csip();				\
GETEW(p1);							\
GETSW(p2);							\

#define SWEW_INIT 						\
unsigned char modrm = get_csip();				\
GETSW(p1);							\
GETEW(p2);							\


#define ALIB_INIT						\
p2 = get_csip();						\

#define IBAL_INIT						\
p1 = get_csip();						\


#define AXIW_INIT						\
p2 = get_csip_word();						\

#define ALOB_INIT						\
if (override_seg == 0xFFFF)					\
	p2 = (ds<<4) + get_csip_word();				\
else								\
{								\
	p2 = (override_seg<<4) + get_csip_word();		\
	override_seg = 0xFFFF;					\
}								\

#define AXOW_INIT						\
if (override_seg == 0xFFFF)					\
	p2 = (ds<<4) + get_csip_word();				\
else								\
{								\
	p2 = (override_seg<<4) + get_csip_word();		\
	override_seg = 0xFFFF;					\
}								\


#define OBAL_INIT						\
if (override_seg == 0xFFFF)					\
	p1 = (ds<<4) + get_csip_word();				\
else								\
{								\
	p1 = (override_seg<<4) + get_csip_word();		\
	override_seg = 0xFFFF;					\
}								\

#define OWAX_INIT						\
if (override_seg == 0xFFFF)					\
	p1 = (ds<<4) + get_csip_word();				\
else								\
{								\
	p1 = (override_seg<<4) + get_csip_word();		\
	override_seg = 0xFFFF;					\
}								\


#define IB_INIT							\
p1 = get_csip();						\

#define IW_INIT							\
p1 = get_csip_word();						\

#define EW_INIT 						\
unsigned char modrm = get_csip();				\
GETEW(p1);							\



#define GETEB(p)						\
p = calc_modrm_addrb(modrm);					\

#define GETEB_GRP(p)						\
p = calc_modrm_addrb(grp_modrm);				\

#define GETGB(p)						\
p = (int)reglutb[REG(modrm)];					\

#define GETEW(p)						\
p = calc_modrm_addrw(modrm);					\

#define GETEW_GRP(p)						\
p = calc_modrm_addrw(grp_modrm);				\


#define GETGW(p)						\
p = (int)(regs + (REG(modrm)));					\

#define GETSW(p)						\
p = (int)(regs + GREGSCOUNT + REG(modrm));			\




/*********** OCHANDLERs bodies (the OCHANDLER itself) *********/

/* NOTICE: everytime a mov change is needed, it needs to be done
 * both here and in  ALIb, CLIb, ..., AXIw, BXIw ... mov only handlers
 */

/* TODO: possibly make some ochandlers make their flags change
 *       before performing the actual action, that way v1 could be
 *       flushed down with the action together
 */

/* TODO: make the v1 = ASSIGNP1BYTE()/GETP1BYTE() actions
 *       work while doing something to v1
 *       (i.e. blabla = (v1 = GETP1BYTE()) * 2)
 */

/* TODO: make shift opcodes exploit the fact that
 *       some of them have a known IMMEDIATE operator (1)
 *       (i.e. shr ax,1)
 */

#define TODO_TEXT {puts("todo"); exit_bhole();}
#define RORB_TEXT TODO_TEXT
#define RORW_TEXT TODO_TEXT


#define MOVB_TEXT					\
{							\
	ASSIGNP1BYTE(GETP2BYTE());			\
}							\

#define MOVW_TEXT					\
{							\
	ASSIGNP1WORD(GETP2WORD());			\
}							\


/* TODO: all flags in ADD */

#define ADDB_TEXT							\
{									\
	unsigned char v1,v2;						\
	v1 = GETP1BYTE();						\
	v2 = GETP2BYTE();						\
	v1 = v1 + v2;							\
	if (v1                    < v2                   )		\
	{								\
		if ( (v1          & 0x7F) < (v2          & 0x7F) )	\
			UNSET_OF();					\
		else							\
			SET_OF();					\
		SET_CF();						\
	}								\
	else								\
	{								\
		if ( (v1          & 0x7F) < (v2          & 0x7F) )	\
			SET_OF();					\
		else							\
			UNSET_OF();					\
		UNSET_CF();						\
	}								\
	do_flags_byte(v1);						\
	ASSIGNP1BYTE(v1);						\
	/* v2 is not chagned, so assigment is not needed */		\
}									\

/* TODO: does using unsigned int here actually optimizes this thing??? */
#define ADDW_TEXT							\
{									\
	unsigned int v1,v2;						\
	v1 = GETP1WORD();						\
	v2 = GETP2WORD();						\
	v1 = (v1 + v2) & 0xFFFF;					\
									\
	if (v1                    < v2                   )		\
	{								\
		if ( (v1          & 0x7FFF) < (v2          & 0x7FFF) )	\
			UNSET_OF();					\
		else							\
			SET_OF();					\
		SET_CF();						\
	}								\
	else								\
	{								\
		if ( (v1          & 0x7FFF) < (v2          & 0x7FFF) )	\
			SET_OF();					\
		else							\
			UNSET_OF();					\
		UNSET_CF();						\
	}								\
	do_flags_word(v1         );					\
	ASSIGNP1WORD(v1);						\
	/* v2 is not changed, so assigment is not needed */		\
}									\

/* TODO: all flags in or */

#define ORB_TEXT						\
{								\
	unsigned short v1;					\
	UNSET_OF(); UNSET_CF();					\
	v1 = GETP1BYTE();					\
	v1 |= GETP2BYTE();					\
	do_flags_byte(v1);					\
	ASSIGNP1BYTE(v1);					\
}								\


#define ORW_TEXT						\
{								\
	unsigned short v1;					\
	UNSET_OF(); UNSET_CF();					\
	v1 = GETP1WORD();					\
	v1 |= GETP2WORD();					\
	do_flags_word(v1);					\
	ASSIGNP1WORD(v1);					\
}								\

#define SUBB_TEXT								\
{										\
	unsigned char v1,v2;							\
	void do_byte_cmp(int a,int b);						\
	v2 = GETP2BYTE();							\
	v1 = GETP1BYTE();							\
	do_byte_cmp((unsigned char )v1         ,(unsigned char)v2         );	\
	ASSIGNP1BYTE(v1 - v2);							\
}										\

#define SUBW_TEXT								\
{										\
	unsigned short v1,v2;							\
	void do_word_cmp(int a,int b);						\
	v2 = GETP2WORD();							\
	v1 = GETP1WORD();							\
	do_word_cmp((unsigned short)v1,(unsigned short)v2);				\
	ASSIGNP1WORD(v1 - v2);                       				\
}										\


#define ANDB_TEXT								\
{										\
	unsigned char v1,v2;							\
	void do_byte_test(unsigned char a,unsigned char b);			\
	v1 = GETP1BYTE();							\
	v2 = GETP2BYTE();							\
	UNSET_OF(); UNSET_CF();							\
	do_byte_test(v1,v2);							\
	ASSIGNP1BYTE(v1          & v2         );				\
}										\


#define ANDW_TEXT								\
{										\
	unsigned short v1,v2;							\
	void do_word_test(unsigned short a,unsigned short b);			\
	v1 = GETP1WORD(); v2 = GETP2WORD();					\
	UNSET_OF(); UNSET_CF();							\
	do_word_test(v1,v2);							\
	ASSIGNP1WORD(v1 & v2);							\
}										\


/* TODO: all flags for XOR */

#define XORB_TEXT								\
{										\
	unsigned char  v1;							\
	UNSET_OF(); UNSET_CF();							\
	ASSIGNP1BYTE(v1 = (GETP1BYTE() ^ GETP2BYTE()));				\
	do_flags_byte(v1);							\
}										\

#define XORW_TEXT								\
{										\
	unsigned short v1;							\
	UNSET_OF(); UNSET_CF();							\
	ASSIGNP1WORD(v1 = (GETP1WORD() ^ GETP2WORD()));				\
	do_flags_word(v1);							\
}										\


#define TESTB_TEXT								\
{										\
	void do_byte_test(unsigned char a,unsigned char b);			\
	UNSET_OF(); UNSET_CF();							\
	do_byte_test(GETP1BYTE(),GETP2BYTE());					\
}

#define TESTW_TEXT								\
{										\
	void do_word_test(unsigned short a,unsigned short b);			\
	UNSET_OF(); UNSET_CF();							\
	do_word_test(GETP1WORD(),GETP2WORD());					\
}

/* TODO: all flags for ADC */


#define ADCB_TEXT								\
{										\
	unsigned char v1,v2;							\
	v1 = GETP1BYTE();							\
	v2 = GETP2BYTE();							\
	v1 = v1 + v2 + (GET_CF() != 0);						\
										\
	if (v1          < v2         )						\
	{									\
		if ((v1          & 0x7F) < (v2          & 0x7F))		\
			UNSET_OF();						\
		else								\
			SET_OF();						\
		SET_CF();							\
	}									\
	else									\
	{									\
		if ((v1          & 0x7F) < (v2          & 0x7F))		\
			SET_OF();						\
		else								\
			UNSET_OF();						\
		UNSET_CF();							\
	}									\
										\
	do_flags_byte(v1         );						\
	ASSIGNP1BYTE(v1);							\
}										\


#define ADCW_TEXT								\
{										\
	unsigned short v1,v2;							\
	v1 = GETP1WORD();							\
	v2 = GETP2WORD();							\
	v1 = v1 + v2 + (GET_CF() != 0);						\
										\
	if (v1 < v2)								\
	{									\
		if ((v1          & 0x7FFF) < (v2          & 0x7FFF))		\
			UNSET_OF();						\
		else								\
			SET_OF();						\
		SET_CF();							\
	}									\
	else									\
	{									\
		if ((v1          & 0x7FFF) < (v2          & 0x7FFF))		\
			SET_OF();						\
		else								\
			UNSET_OF();						\
		UNSET_CF();							\
	}									\
										\
	do_flags_word(v1         );						\
	ASSIGNP1WORD(v1);							\
}										\

/* TODO: those SBB sets flags correctly??? */

#define SBBB_TEXT								\
{										\
	unsigned char v1,v2;							\
	void do_byte_cmp(int a,int b);						\
	unsigned char brw = (GET_CF() != 0);					\
										\
	v1 = GETP1BYTE();							\
	v2 = GETP2BYTE();							\
	do_byte_cmp((unsigned char )v1         ,(unsigned char )v2          + brw);	\
	ASSIGNP1BYTE(v1 - v2 - brw);						\
										\
}										\

#define SBBW_TEXT								\
{										\
	unsigned short v1,v2;							\
	void do_word_cmp(int a,int b);						\
	unsigned char brw = (GET_CF() != 0);					\
										\
	v1 = GETP1WORD();							\
	v2 = GETP2WORD();							\
	do_word_cmp((unsigned short)v1         ,(unsigned short)v2          + brw);	\
	ASSIGNP1WORD(v1 - v2 - brw);						\
										\
}										\

#define CMPB_TEXT								\
{										\
	void do_byte_cmp(int a,int b);						\
	do_byte_cmp(GETP1BYTE(),GETP2BYTE());					\
}										\

#define CMPW_TEXT								\
{										\
	void do_word_cmp(int a,int b);						\
	do_word_cmp(GETP1WORD(),GETP2WORD());					\
}										\


/* NOTICE: everytime an xchg change is needed, it needs to be done
 * both here and in the AXCX, AXDX, ... xchg handlers
 */

#define XCHGB_TEXT								\
{										\
	unsigned char xchgtmp;							\
	xchgtmp                  = GETP2BYTE();					\
	ASSIGNP2BYTE(GETP1BYTE());						\
	ASSIGNP1BYTE(xchgtmp);			 				\
}

#define XCHGW_TEXT								\
{										\
	unsigned short xchgtmp;							\
	xchgtmp                  = GETP2WORD();					\
	ASSIGNP2WORD(GETP1WORD());						\
	ASSIGNP1WORD(xchgtmp);			 				\
}

#define POP_TEXT									\
{											\
	unsigned short do_pop();							\
	ASSIGNP1WORD(do_pop());								\
}											\


#define LDS_TEXT									\
{											\
	ASSIGNP1WORD(GETP2WORD());							\
	ds                   = GETP2OWORD(2);						\
}											\


#define LES_TEXT									\
{											\
	ASSIGNP1WORD(GETP2WORD());							\
	es                   = GETP2OWORD(2);						\
}											\


/* TODO: are CF and OF set correctly for ROL?  */

#define ROLB_TEXT								\
{										\
	static unsigned char  rol_maskb[] =					\
	{0,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF};				\
	static unsigned char  rol_maskb2[] = 					\
	{0,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};				\
	static unsigned char  rol_maskb3[] = 					\
	{0,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0};				\
	unsigned char v1 = GETP1BYTE();						\
	unsigned short roltmp;							\
	unsigned char roli = GETP2BYTE();					\
	if (v1 & rol_maskb2[roli])						\
	{									\
		if (v1 & rol_maskb3[roli])					\
			UNSET_OF();						\
		else								\
			SET_OF();						\
		SET_CF();							\
	}									\
	else									\
	{									\
		if (v1 & rol_maskb3[roli])					\
			SET_OF();						\
		else								\
			UNSET_OF();						\
		UNSET_CF();							\
	}									\
										\
	roltmp = (v1 & rol_maskb[roli]) >> (16 - roli);				\
	ASSIGNP1BYTE((v1 << roli) | roltmp);					\
}										\


#define ROLW_TEXT								\
{										\
	static unsigned short rol_maskw[] =					\
	{0,0x8000,0xC000,0xE000,0xF000,0xF800,0xFC00,0xFE00,0xFF00,		\
	   0xFF80,0xFFC0,0xFFE0,0xFFF0,0xFFF8,0xFFFC,0xFFFE,0xFFFF};		\
	static unsigned short rol_maskw2[] =					\
	{0,0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,		\
	   0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};		\
	static unsigned short rol_maskw3[] =					\
	{0,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,0x0080,		\
	   0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001,0x0000};		\
										\
	unsigned short roltmp;							\
	unsigned short v1 = GETP1WORD();					\
	unsigned char roli = GETP2BYTE();					\
	if (v1          & rol_maskw2[roli])					\
	{									\
		if (v1          & rol_maskw3[roli])				\
			UNSET_OF();						\
		else								\
			SET_OF();						\
		SET_CF();							\
	}									\
	else									\
	{									\
		if (v1          & rol_maskw3[roli])				\
			SET_OF();						\
		else								\
			UNSET_OF();						\
		UNSET_CF();							\
	}									\
										\
	roltmp = (v1          & rol_maskw[roli]) >> (16 - roli); 		\
	ASSIGNP1WORD((v1          << roli) | roltmp);				\
}										\





/* TODO: are CF/OF set correctly for RCL (what happens for RCL bleh,CL) */
/* TODO: modify ROL, RCL, ROR, RCR
 * to possibly use processor ROL,RCL,ROR,RCR opcodes???
 * also, there's probably a more efficient way of doing them
 * (a way which is less readable, try thinking of it..., check out
 *  rcl_shits.txt in desktop)
 */

#define RCLB_TEXT									\
{											\
	static unsigned short rcl_maskb[] =						\
	{0,0x100,0x180,0x1C0,0x1E0,0x1F0,0x1F8,0x1FC,0x1FE,0x1FF};			\
	unsigned short rcl_tmp;								\
	unsigned short rcl_value;							\
	unsigned short rcli = GETP2BYTE();						\
	rcl_value = (GETP1BYTE() << 1) | (GET_CF() ? 1 : 0);				\
	rcl_tmp   = (rcl_value & rcl_maskb[rcli]) >> (9 - rcli);			\
	rcl_value = rcl_value << rcli | rcl_tmp;					\
	ASSIGNP1BYTE(rcl_value >> 1); /* notice: rcl_value is automatically truncated */\
	if (rcl_value & 1) 								\
	{										\
		SET_CF(); 								\
		if (rcl_value & 0x100) UNSET_OF();					\
		else                   SET_OF();					\
	}										\
	else 										\
	{										\
		UNSET_CF();								\
		if (rcl_value & 0x100) SET_OF();					\
		else                   UNSET_OF();					\
	}										\
}											\


#define RCLW_TEXT									\
{											\
	static unsigned int rcl_maskw[] =						\
	{0,										\
	0x10000,0x18000,0x1C000,0x1E000,0x1F000,0x1F800,0x1FC00,0x1FE00,0x1FF00,	\
	0x1FF80,0x1FFC0,0x1FFE0,0x1FFF0,0x1FFF8,0x1FFFC,0x1FFFE,0x1FFFF};		\
	unsigned int   rcl_tmp;								\
	unsigned int   rcl_value;							\
	unsigned short rcli = GETP2BYTE();						\
	rcl_value = (GETP1WORD() << 1) | (GET_CF() ? 1 : 0);				\
	rcl_tmp   = (rcl_value & rcl_maskw[rcli]) >> (17 - rcli);				\
	rcl_value = rcl_value << rcli | rcl_tmp;					\
	ASSIGNP1WORD(rcl_value >> 1); /* notice: rcl_value is automatically truncated */\
	if (rcl_value & 1) 								\
	{										\
		SET_CF(); 								\
		if (rcl_value & 0x10000) UNSET_OF();					\
		else                     SET_OF();					\
	}										\
	else 										\
	{										\
		UNSET_CF();								\
		if (rcl_value & 0x10000) SET_OF();					\
		else                     UNSET_OF();					\
	}										\
}											\

/* TODO: is RCRB/W overflow flag correct??? */
#define RCRB_TEXT									\
{											\
	static unsigned short  rcr_maskb[] =						\
	{0,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF,0x1FF};				\
	unsigned short rcr_tmp;								\
	unsigned char rcri = GETP2BYTE();						\
	unsigned short rcr_value; 							\
	rcr_value = (GETP1BYTE() << 1) | (GET_CF() ? 1 : 0);				\
	rcr_tmp   = (rcr_value & rcr_maskb[rcri]) << (9 - rcri);			\
	rcr_value = rcr_value >> rcri | rcr_tmp;					\
	ASSIGNP1BYTE(rcr_value >> 1);							\
	if (rcr_value & 1) SET_CF(); else UNSET_CF();					\
	if ((rcr_value & 0x100) ^ (rcr_value & (0x100 >> rcri))) 			\
		SET_OF();								\
	else										\
		UNSET_OF();								\
}											\


#define RCRW_TEXT									\
{											\
	static unsigned int    rcr_maskw[] =						\
	{0,										\
	0x00001,0x00003,0x00007,0x0000F,0x0001F,0x0003F,0x0007F,0x000FF,0x001FF,	\
	0x003FF,0x007FF,0x00FFF,0x01FFF,0x03FFF,0x07FFF,0x0FFFF,0x1FFFF};		\
	unsigned int   rcr_tmp;								\
	unsigned char rcri = GETP2BYTE();						\
	unsigned int   rcr_value; 							\
	rcr_value = (GETP1WORD() << 1) | (GET_CF() ? 1 : 0);				\
	rcr_tmp   = (rcr_value & rcr_maskw[rcri]) << (17 - rcri);			\
	rcr_value = rcr_value >> rcri | rcr_tmp;					\
	ASSIGNP1WORD(rcr_value >> 1);							\
	if (rcr_value & 1) SET_CF(); else UNSET_CF();					\
	if ((rcr_value & 0x10000) ^ (rcr_value & (0x10000 >> rcri)))			\
		SET_OF();								\
	else										\
		UNSET_OF();								\
}											\

/* TODO: flags for SHL */

#define SHLB_TEXT									\
{											\
	static unsigned char  shl_maskb[] = 						\
	{0,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};					\
	static unsigned char  shl_maskb2[] = 						\
	{0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0};					\
	unsigned char shli = GETP2BYTE();						\
	unsigned char v1 = GETP1BYTE();							\
	if (v1 & shl_maskb[shli])							\
	{										\
		SET_CF();								\
		if (v1 & shl_maskb2[shli])						\
			UNSET_OF();							\
		else									\
			SET_OF();							\
	}										\
	else										\
	{										\
		UNSET_CF();								\
		if (v1 & shl_maskb2[shli])						\
			SET_OF();							\
		else									\
			UNSET_OF();							\
	}										\
	ASSIGNP1BYTE(v1 = (v1 << shli));						\
	do_flags_byte(v1);								\
}											\

#define SHLW_TEXT									\
{											\
	static unsigned short shl_maskw[] =						\
	{0,0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,			\
	   0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};			\
	static unsigned short shl_maskw2[] = 						\
	{0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,			\
	 0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001,0};			\
	unsigned char shli = GETP2BYTE();						\
	unsigned short v1 = GETP1WORD();						\
	if (v1          & shl_maskw[shli])						\
	{										\
		SET_CF();								\
		if (v1 & shl_maskw2[shli])						\
			UNSET_OF();							\
		else									\
			SET_OF();							\
	}										\
	else										\
	{										\
		UNSET_CF();								\
		if (v1 & shl_maskw2[shli])						\
			SET_OF();							\
		else									\
			UNSET_OF();							\
	}										\
	ASSIGNP1WORD(v1 = (v1 << shli));						\
	do_flags_word(v1);								\
}											\


/* TODO: implement flags for SHR */

#define SHRB_TEXT									\
{											\
	static unsigned short shr_masks[] = {0,						\
	                                     0x0001,0x0002,0x0004,0x0008,		\
	                                     0x0010,0x0020,0x0040,0x0080,		\
	                                     0x0100,0x0200,0x0400,0x0800,		\
	                                     0x1000,0x2000,0x4000,0x8000};		\
	unsigned char shri = GETP2BYTE();						\
	unsigned char v1;								\
											\
	if ((v1 = GETP1BYTE()) & shr_masks[shri])					\
	{										\
		SET_CF();								\
	}                								\
	else										\
	{										\
		UNSET_CF();								\
	}										\
	if (v1 & 0x80) SET_OF(); else UNSET_OF();					\
	do_flags_byte(v1 = v1 >> shri);							\
	ASSIGNP1BYTE(v1);								\
}											\

#define SHRW_TEXT									\
{											\
	static unsigned short shr_masks[] = {0,						\
	                                     0x0001,0x0002,0x0004,0x0008,		\
	                                     0x0010,0x0020,0x0040,0x0080,		\
	                                     0x0100,0x0200,0x0400,0x0800,		\
	                                     0x1000,0x2000,0x4000,0x8000};		\
	unsigned char shri = GETP2BYTE();						\
	unsigned short v1;								\
											\
	if ((v1 = GETP1WORD()) & shr_masks[shri])					\
	{										\
		SET_CF();								\
	}                								\
	else										\
	{										\
		UNSET_CF();								\
	}										\
	if (v1 & 0x8000) SET_OF(); else UNSET_OF();					\
	do_flags_word(v1 = v1 >> shri);							\
	ASSIGNP1WORD(v1);								\
}											\

#define NOTB_TEXT									\
{											\
	unsigned char v1;								\
	ASSIGNP1BYTE( v1=~GETP1BYTE() );						\
}

#define NOTW_TEXT									\
{											\
	unsigned short v1;								\
	ASSIGNP1WORD( v1=~GETP1WORD() );						\
}

/* TODO: flags for NEG */

#define NEGB_TEXT								\
{										\
	unsigned char v1;							\
	ASSIGNP1BYTE(v1 = -((signed char)GETP1BYTE()));				\
	if (v1 == 0) { UNSET_SF(); SET_ZF(); UNSET_OF(); UNSET_CF(); }		\
	else									\
	{									\
		UNSET_ZF();							\
		SET_CF();							\
		if (v1 & 0x80) 							\
		{								\
			SET_SF(); 						\
			if (v1 == 0x80)						\
				SET_OF();					\
			else							\
				UNSET_OF();					\
		}								\
		else								\
		{								\
			UNSET_SF();						\
			UNSET_OF();						\
		}								\
	}									\
}										\


#define NEGW_TEXT								\
{										\
	unsigned short v1;							\
	ASSIGNP1WORD(v1 = -((signed short)GETP1WORD()));			\
	if (v1 == 0) { UNSET_SF(); SET_ZF(); UNSET_OF(); UNSET_CF(); }		\
	else									\
	{									\
		UNSET_ZF();							\
		SET_CF();							\
		if (v1 & 0x8000)						\
		{								\
			SET_SF();						\
			if (v1 == 0x8000)					\
				SET_OF();					\
			else							\
				UNSET_OF();					\
		}								\
		else								\
		{								\
			UNSET_SF();						\
			UNSET_OF();						\
		}								\
	}									\
}										\

/* TODO: flags for MUL */

/* how mul works: first param is in fact the second multiplier,
 * the first multiplier is AX/AL
 */

#define MULB_TEXT								\
{										\
	ax = al * GETP1BYTE();							\
	if (ax > 0xFF) { SET_CF()  ; SET_OF();  }				\
	else           { UNSET_CF(); UNSET_OF();}				\
}										\

#define MULW_TEXT								\
{										\
	unsigned int mul_t = ax * GETP1WORD();					\
	if (mul_t > 0xFFFF) { SET_CF()  ; SET_OF();  }				\
	else                { UNSET_CF(); UNSET_OF();}				\
	ax = mul_t & 0xFFFF;							\
	dx = (mul_t & 0xFFFF0000) >> 16;					\
										\
}										\

/* TODO: flags for DIV and IDIV */

#define DIVB_TEXT								\
{										\
	unsigned short divtmp;							\
	unsigned char divisor = GETP1BYTE();					\
	divtmp = ax;								\
/*	printf("divb %x,%x\n",divtmp,divisor);	*/				\
	if (!divisor)								\
	{									\
		unsigned int t;							\
		t = GET_MEM_WORD2(0) + (GET_MEM_WORD2(2) << 16);		\
		goto_interrupt(t);						\
	}									\
	al = divtmp / divisor;							\
	ah = divtmp % divisor;							\
}										\

#define DIVW_TEXT								\
{										\
	unsigned int divtmp;							\
	unsigned short divisor = GETP1WORD();					\
	divtmp = (dx << 16) + ax;						\
/*	printf("divw %d,%d line: %d\n",divtmp,divisor,line);		*/			\
/*	fprintf(report_file,"divw %d,%d line: %d\n",divtmp,divisor,line);*/			\
	if (!divisor)								\
	{									\
		unsigned int t;							\
		t = GET_MEM_WORD2(0) + (GET_MEM_WORD2(2) << 16);		\
		goto_interrupt(t);						\
	}									\
	ax = divtmp / divisor;							\
	dx = divtmp % divisor;							\
}										\

#define IDIVB_TEXT								\
{										\
	signed short idivtmp;							\
	signed char idivisor = GETP1BYTE();					\
	idivtmp = ax;								\
	if (!idivisor)								\
	{									\
		unsigned int t;							\
		t = GET_MEM_WORD2(0) + (GET_MEM_WORD2(2) << 16);		\
		goto_interrupt(t);						\
	}									\
	al = idivtmp / idivisor;						\
	ah = idivtmp % idivisor;						\
}										\

#define IDIVW_TEXT								\
{										\
	signed int idivtmp;							\
	signed short idivisor = GETP1WORD();					\
	idivtmp = (signed int)((dx << 16) + ax);				\
	if (!idivisor)								\
	{									\
		unsigned int t;							\
		t = GET_MEM_WORD2(0) + (GET_MEM_WORD2(2) << 16);		\
		goto_interrupt(t);						\
	}									\
	ax = idivtmp / idivisor;						\
	dx = idivtmp % idivisor;						\
}										\


/* TODO: more flags for INC */


#define INCB_TEXT								\
{										\
	unsigned char v1;							\
	ASSIGNP1BYTE(v1 = GETP1BYTE() + 1);					\
										\
	if (v1          == 0) { SET_CF();   SET_ZF();  }			\
	else                  { UNSET_CF(); UNSET_ZF();}			\
	if (v1          & 0x80)   						\
	{									\
		if (v1 == 0x80) SET_OF(); else UNSET_OF();			\
		SET_SF(); 							\
	}									\
	else									\
	{									\
		UNSET_SF();							\
		UNSET_OF();							\
	}									\
}										\

#define INCW_TEXT								\
{										\
	unsigned short v1;							\
	ASSIGNP1WORD(v1 = GETP1WORD() + 1);					\
										\
	if (v1          == 0) { SET_CF();   SET_ZF();  }			\
	else                  { UNSET_CF(); UNSET_ZF();}			\
	if (v1          & 0x8000)						\
	{									\
		if (v1 == 0x8000) SET_OF(); else UNSET_OF();			\
		SET_SF(); 							\
	}									\
	else 									\
	{									\
		UNSET_SF();							\
		UNSET_OF();							\
	}									\
}										\

/* TODO: flags for IMUL */

#define IMULB_TEXT								\
{										\
	ax = al * (signed char)(GETP1BYTE());					\
	if (ax > 0x7F) { SET_CF()  ; SET_OF();  }				\
	else           { UNSET_CF(); UNSET_OF();}				\
}										\

#define IMULW_TEXT								\
{										\
	unsigned int imul_t;							\
	imul_t = ax * (signed short)(GETP1WORD());				\
	if (imul_t > 0x7FFF) { SET_CF()  ; SET_OF();  }				\
	else                 { UNSET_CF(); UNSET_OF();}				\
	ax = imul_t & 0xFFFF;							\
	dx = (imul_t & 0xFFFF0000) >> 16;					\
}										\


#define DECB_TEXT								\
{										\
	unsigned char v1;							\
	void do_byte_cmp(int a,int b);						\
	do_byte_cmp(v1 = (signed char)GETP1BYTE() ,1);				\
	ASSIGNP1BYTE(v1 - 1);							\
}										\

#define DECW_TEXT								\
{										\
	unsigned short v1;							\
	void do_word_cmp(int a,int b);						\
	do_word_cmp(v1 = (signed short)GETP1WORD(),1);				\
	ASSIGNP1WORD(v1 - 1);							\
}										\



#define PUSH_TEXT									\
{											\
	void do_push(int);								\
	do_push(GETP1WORD());								\
}											\

#define NACALL_TEXT									\
	/* na = near and absolute */							\
{											\
	void do_push(int);								\
	do_push(ip);									\
	ip = GETP1WORD();								\
}											\

#define AJMP_TEXT									\
/* ajmp - absolute jmp */								\
{											\
	ip = GETP1WORD();								\
}											\

#define AFJMP_TEXT									\
/* ajmp - absolute far jmp */								\
{                    									\
											\
	ip = GETP1WORD();								\
	cs = GETP1OWORD(2);								\
}											\

#define FACALL2_TEXT									\
/* facall2 - absolute far call */							\
	/* facall2 already has the parameter in p1 */					\
{                    									\
	void do_push(int);								\
	do_push(cs);									\
	do_push(ip);									\
	ip = GETP1WORD();								\
	cs = GETP1OWORD(2);								\
}											\


#define NRET2_TEXT									\
	/* the case of RET near ?							\
	 * (RET with a parameter)							\
	 */										\
{											\
	unsigned short do_pop();							\
	ip = do_pop();									\
	sp+=GETP1WORD();								\
}											\

#define FRET2_TEXT									\
{											\
	unsigned short do_pop();							\
	ip = do_pop();									\
	cs = do_pop();									\
	sp+=GETP1WORD();								\
}											\

#define INTERRUP_TEXT									\
{											\
	int do_int13();									\
	int do_int16();									\
	int do_int21();									\
	int do_int33();									\
	int do_int67();									\
	int do_inttime();								\
	int do_vidint();								\
	void do_push(int);								\
											\
	unsigned short intnum;								\
	unsigned int t;									\
	intnum = GETP1BYTE();								\
											\
	t = GET_MEM_WORD2(intnum<<2)   +						\
	   (GET_MEM_WORD2((intnum<<2)+2) << 16);					\
											\
	goto_interrupt(t);								\
}											\

/* this is needed because for bigger than 16-bit int, wierd overflow may occur here */
#ifdef HYPER_SPEED_MODE
#define NORMALIZER & 0xFFFF
#else
#define NORMALIZER
#endif


#define RJMPB_TEXT /* rjmp - relative jmp */						\
{											\
	ip = (ip + (signed char )GETP1BYTE()) NORMALIZER;				\
}											\

#define RJMPW_TEXT /* rjmp - relative jmp */						\
{											\
	ip = (ip + (signed short)GETP1WORD()) NORMALIZER;				\
}											\


#define NRCALL_TEXT									\
	/* nr = near and relative */							\
{											\
	void do_push(int);								\
	do_push(ip);									\
	ip =(ip+(signed short)GETP1WORD()) NORMALIZER;					\
}											\




#define LOOP_TEXT									\
{											\
	cx--;										\
	if (cx != 0)									\
	{										\
		ip =(ip+(signed char)GETP1BYTE()) NORMALIZER;				\
	}										\
}											\


#define LOOPE_TEXT									\
{											\
	cx--;										\
	if (cx != 0 && GET_ZF())							\
	{										\
		ip =(ip+(signed char)GETP1BYTE()) NORMALIZER;				\
	}										\
}											\


#define LOOPNE_TEXT									\
{											\
	cx--;										\
	if (cx != 0 && !GET_ZF())							\
		ip =(ip+(signed char)GETP1BYTE()) NORMALIZER;				\
}											\


#define JCXZ_TEXT									\
{											\
	if (cx == 0)									\
		ip =(ip+(signed char)GETP1BYTE()) NORMALIZER;				\
}											\


#define INB_TEXT									\
{											\
/*	COMMENTED CODE IS OBSOLETE							\
 *											\
 *	if (!byte)									\
 *		printf("BHole: IN : value %x port %x\n",*(unsigned short*)p1 ,*(unsigned short*)p2);
 *	else
 *		printf("BHole: IN : value %x port %x\n",*(unsigned char *)p1,*(unsigned short*)p2);
 *
 *	return 1;
 */											\
	switch(GETP2WORD())								\
	{										\
	case CGA_STATUS_PORT: /* this where there are checks for vertical retrace */	\
		if (cga_stat == 9)								\
		{	cga_stat = 0; ASSIGNP1BYTE(9);	}				\
		else									\
		{	cga_stat = 9; ASSIGNP1BYTE(0);	}				\
		break;									\
	case VGA_SEQDAT_PORT:								\
		ASSIGNP1BYTE(seqcon[seqreg]);						\
		break;									\
	case VGA_CRTDAT_PORT:								\
		ASSIGNP1BYTE(crtcon[crtreg]);						\
		break;									\
	case VGA_GDCREG_PORT:								\
		ASSIGNP1BYTE(gdcreg);							\
		break;									\
	case VGA_GDCDAT_PORT:								\
		ASSIGNP1BYTE(gdcregs[gdcreg]);						\
		break;									\
	case 0x61:									\
		ASSIGNP1BYTE(port_61);							\
		if ((port_61 & 3) != 3) pcspkr_nosound();				\
		break;									\
	case 0x60:									\
		ASSIGNP1BYTE(keydata);							\
		break;									\
	case 0x40:									\
		/* TODO: using rand() is idiotic */					\
		ASSIGNP1BYTE(rand() & 0xFF);						\
		break;									\
	case 0x41:									\
		ASSIGNP1BYTE(rand() & 0xFF);						\
		/* ignores port 40 and 41 */						\
		break;									\
	case CGA_MODECON_PORT:								\
		ASSIGNP1BYTE(cga_modecon);						\
		break;									\
	case 0x201:									\
	case 0x3D9:									\
		ASSIGNP1BYTE(0xFF); /* port 3d9 is ignored */				\
		break;									\
	default:									\
		fprintf(report_file,"BHole: handle_in: unknown port %x\n",GETP2WORD());	\
		exit_bhole();								\
	}										\
}											\


#ifdef ASSUME_VIDEO_PAGE_FLIPPING
#define HANDLE_PAGEFLIP if (crtreg == 0xC || crtreg == 0xD) { force_refresh_screen(); }
#else
#define HANDLE_PAGEFLIP
#endif


#define OUTB_TEXT									\
{											\
/*	COMMENTED CODE IS OBSOLETE
 *	if (!byte)
 *		printf("BHole: OUT: value %x port %x\n",*(unsigned short*)p2 ,*(unsigned short*) p1);
 *	else
 *		printf("BHole: OUT: value %x port %x\n",*(unsigned char *)p2,*(unsigned short*)p1);
 *
 *	return 1;
 */											\
											\
	static int seqregs_used[0x50] = {0};						\
	static int crtregs_used[0x50] = {0};						\
	static int gdcregs_used[0x50] = {0};						\
	switch(GETP1WORD())								\
	{										\
	case VGA_SEQREG_PORT:								\
		seqreg = GETP2BYTE();							\
		break;									\
	case VGA_SEQDAT_PORT:								\
		seqcon[seqreg] = GETP2BYTE();						\
		if (seqregs_used[seqreg] == 0) 						\
		{									\
			printf("BHole: out: debug: seqreg %x - first time\n",seqreg);	\
			seqregs_used[seqreg]++;						\
		}									\
		break;									\
	case VGA_CRTREG_PORT:								\
		crtreg = GETP2BYTE();							\
		break;									\
	case VGA_CRTDAT_PORT:								\
		crtcon[crtreg] = GETP2BYTE();						\
		HANDLE_PAGEFLIP								\
		if (crtregs_used[crtreg] == 0) 						\
		{									\
			printf("BHole: out: debug: crtreg %x - first time, value %x\n",crtreg,GETP2BYTE());	\
			crtregs_used[crtreg]++;						\
		}									\
		break;									\
	case VGA_GDCREG_PORT:								\
		gdcreg = GETP2BYTE();							\
		break;									\
	case VGA_GDCDAT_PORT:								\
		gdcregs[gdcreg] = GETP2BYTE();						\
		if (gdcregs_used[gdcreg] == 0) 						\
		{									\
			printf("BHole: out: debug: gdcreg %x - first time\n",gdcreg);	\
			gdcregs_used[gdcreg]++;						\
		}									\
		break;									\
	case VGA_PALINDEX_PORT:								\
		palindex = GETP2BYTE();							\
		palpos = 0;								\
		break;									\
	case VGA_PALSET_PORT:								\
		switch(palpos)								\
		{									\
		case 0:									\
			palette[palindex].r = GETP2BYTE() << 2;				\
			palpos = 1;							\
			break;								\
		case 1:									\
			palette[palindex].g = GETP2BYTE() << 2;				\
			palpos = 2;							\
			break;								\
		case 2:									\
			palette[palindex].b = GETP2BYTE() << 2; 			\
			palette[palindex].unused = 0;					\
			palpos = 0; palindex++;						\
			if (palindex == 256) palindex = 0;				\
			break;								\
		}									\
		break;									\
	case 0x61:									\
		port_61 = GETP2BYTE();							\
		if ((port_61 & 3) != 3) pcspkr_nosound();				\
		break;									\
	case 0x3D9:									\
		fprintf(report_file,"BHole: handle_out: port 3D9 ignored\n");		\
/*		cga_pal = GETP2BYTE();			*/				\
		break;									\
	case 0x20:									\
	/*	fprintf(report_file,"BHole: handle_out: port 20 ignored\n");*/		\
		break;									\
	case 0x42:									\
		if ((port_61 & 3) == 3) /* is port 42 only writable in this condition? */ \
		{									\
			/* TODO: handle the PIT */					\
			pcspkr_done++;							\
			pcspkr_freq = (pcspkr_freq >> 8) | (GETP2BYTE() << 8);		\
	/*		if ((pcspkr_done & 1) == 0) */					\
			{								\
				if (pcspkr_divisor != 0)				\
				{							\
					pcspkr_sound(pcspkr_freq * pcspkr_divisor / 0x10000);	\
				}							\
				else							\
					pcspkr_sound(pcspkr_freq);			\
			}								\
		}									\
		break;									\
	case 0x43:									\
/*		fprintf(report_file,"BHole: handle_out: port 43 ignored\n");*/		\
		if (GETP2BYTE() & 0xC0 == 0xC0)						\
		{									\
			if (pcspkr_mode != GETP2BYTE())					\
				printf("BHole: debug: pcspeaker mode %x\n",GETP2BYTE());	\
			pcspkr_mode = GETP2BYTE();					\
		}									\
		break;									\
	case 0x40: /* port 0x40 is wtf? */						\
		pcspkr_divisor = (pcspkr_divisor >> 8) | (GETP2BYTE() << 8);		\
		break;									\
											\
	case 0x201:									\
	case 0x3F2:									\
	case 0x41: /* port 0x41 is also ignored */					\
		fprintf(report_file,"BHole: handle_out port %x value %x ignored\n",GETP1WORD(),GETP2BYTE());	\
		break;									\
	case CGA_MODECON_PORT:								\
		cga_modecon = GETP2BYTE();						\
		break;									\
	default:									\
		fprintf(report_file,"BHole: handle_out: unknown port %x\n",GETP1WORD()); \
		exit_bhole();								\
	}										\
}											\


/* TODO: SAR flags (is CF set correctly) */
#define SARB_TEXT								\
{										\
	static unsigned char  sar_maskb[] =					\
	{0,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF};				\
	static unsigned short shr_masks[] = {0,					\
	                                     0x0001,0x0002,0x0004,0x0008,	\
	                                     0x0010,0x0020,0x0040,0x0080,	\
	                                     0x0100,0x0200,0x0400,0x0800,	\
	                                     0x1000,0x2000,0x4000,0x8000};	\
	unsigned char sign;							\
	unsigned char v1,v2;							\
	UNSET_OF();								\
	sign = ((signed char )(v1 = GETP1BYTE()) < 0);				\
	if (v1 & shr_masks[v2 = GETP2BYTE()])					\
		SET_CF();							\
	else									\
		UNSET_CF();							\
										\
	if (sign)								\
	{	ASSIGNP1BYTE(v1 = ((v1 >> v2) | sar_maskb[v2]));	}	\
	else 									\
	{	ASSIGNP1BYTE(v1 = ( v1 >> v2)                 );	}	\
	do_flags_byte(v1);							\
}										\

#define SARW_TEXT								\
{										\
	static unsigned short sar_maskw[] =					\
	{0,0x8000,0xC000,0xE000,0xF000,0xF800,0xFC00,0xFE00,0xFF00,		\
	   0xFF80,0xFFC0,0xFFE0,0xFFF0,0xFFF8,0xFFFC,0xFFFE,0xFFFF};		\
	static unsigned short shr_masks[] = {0,					\
	                                     0x0001,0x0002,0x0004,0x0008,	\
	                                     0x0010,0x0020,0x0040,0x0080,	\
	                                     0x0100,0x0200,0x0400,0x0800,	\
	                                     0x1000,0x2000,0x4000,0x8000};	\
	unsigned char sign;							\
	unsigned short v1;							\
	unsigned char v2;							\
	UNSET_OF();								\
	sign = ((signed short)(v1 = GETP1WORD()) < 0);				\
	if (v1 & shr_masks[v2 = GETP2BYTE()])					\
		SET_CF();							\
	else									\
		UNSET_CF();							\
										\
	if (sign)								\
	{	ASSIGNP1WORD(v1 = ((v1 >> v2) | sar_maskw[v2]));	}	\
	else									\
	{	ASSIGNP1WORD(v1 = ( v1 >> v2)                 );	}	\
	do_flags_word(v1);							\
}										\


/******* OCHANDLERs declarations ************/
/* handlers for impossible (impossible != TODO) opcodes */
#define DEF_INVALID(suffix   ) 		\
DEF_OCHANDLER(handle_invalid##suffix) 	\
{		  			\
	fputs("invalid\n",report_file); \
	exit_bhole();			\
}					\
END_OCHANDLER				\


DEF_INVALID(Eb1_grp2)
DEF_INVALID(Ew1_grp2)
DEF_INVALID(EbCL_grp2)
DEF_INVALID(EwCL_grp2)
DEF_INVALID(EbIb_grp2)
DEF_INVALID(EwIb_grp2)
DEF_INVALID(Eb_grp3)
DEF_INVALID(Ew_grp3)
DEF_INVALID(Eb_grp4)
DEF_INVALID(Ew_grp5)




/* EbGb handlers */
#define ASSIGNP1BYTE(x) SET_MEM_BYTE2(p1,x)
#define ASSIGNP2BYTE(x) (*(unsigned char* )(p2) = (x))

#define GETP1BYTE() ( GET_MEM_BYTE2(p1)      )
#define GETP2BYTE() ( *(unsigned char* )(p2) )

#define DEF_OCHANDLER_EBGB(handler) \
DEF_OCHANDLER(handler##EbGb)	\
	int p1,p2;		\
	EBGB_INIT		\
				\
				

DEF_OCHANDLER_EBGB(handle_add)		ADDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_sub)		SUBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_adc)		ADCB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_sbb)		SBBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_xor)		XORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_cmp)		CMPB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_or )		ORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_and)		ANDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_test)		TESTB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_xchg)		XCHGB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBGB(handle_mov)		MOVB_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_EBGB

#undef ASSIGNP1BYTE
#undef ASSIGNP2BYTE

#undef GETP1BYTE
#undef GETP2BYTE


/* EbIb handlers, grp1 (EbIb) handlers, grp2 (Eb1,EbCL,EbIB) handlers, grp3 handlers (Eb and testIbEb),
 * grp4 handlers
 */
/* notice: in fact, test is EbIb, it's simply called IbEb to be more comfortable */
/* TODO: more efficient grp handlers??? */
#define ASSIGNP1BYTE(x) SET_MEM_BYTE2(p1,x)
#define GETP1BYTE() ( GET_MEM_BYTE2(p1)      )
#define GETP2BYTE() ( (unsigned char)(p2) )

#define DEF_OCHANDLER_EBIB(handler) \
DEF_OCHANDLER(handler##EbIb)	\
	int p1,p2;		\
	EBIB_INIT		\
	
#define DEF_OCHANDLER_EBIB_GRP1(handler) \
DEF_OCHANDLER(handler##EbIb_grp1)	\
	int p1,p2;			\
	EBIB_GRP1_INIT			\

#define DEF_OCHANDLER_EBIB_GRP2(handler) \
DEF_OCHANDLER(handler##EbIb_grp2)	\
	int p1,p2;			\
	EBIB_GRP2_INIT			\

DEF_OCHANDLER_EBIB_GRP1(handle_add)	ADDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_or )	ORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_adc)	ADCB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_sbb)	SBBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_and)	ANDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_sub)	SUBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_xor)	XORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP1(handle_cmp)	CMPB_TEXT	END_OCHANDLER

DEF_OCHANDLER_EBIB_GRP2(handle_rol)		ROLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP2(handle_ror)		RORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP2(handle_rcl)		RCLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP2(handle_rcr)		RCRB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP2(handle_shl)		SHLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP2(handle_shr)		SHRB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBIB_GRP2(handle_sar)		SARB_TEXT	END_OCHANDLER


DEF_OCHANDLER_EBIB(handle_mov)		MOVB_TEXT	END_OCHANDLER


/* this one is special for the special-case TEST in grp3 */
DEF_OCHANDLER(handle_testIbEb_grp3) /* should be EbIb */
{
	int p1,p2;
	/* in fact, p1 contains Eb and p2 contains Ib */
	/* it does not matter here cause in TEST
	 * the parameter order does not matter
	 */
	EBIB_GRP1_INIT
	TESTB_TEXT
}
END_OCHANDLER

#undef DEF_OCHANDLER_EBIB_GRP2
#undef DEF_OCHANDLER_EBIB_GRP1
#undef DEF_OCHANDLER_EBIB

#undef GETP2BYTE
#define GETP2BYTE() 1

#define DEF_OCHANDLER_EB1_GRP2(handler) \
DEF_OCHANDLER(handler##Eb1_grp2)		\
	int p1;					\
	EB1_GRP2_INIT				\


DEF_OCHANDLER_EB1_GRP2(handle_rol)		ROLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB1_GRP2(handle_ror)		RORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB1_GRP2(handle_rcl)		RCLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB1_GRP2(handle_rcr)		RCRB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB1_GRP2(handle_shl)		SHLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB1_GRP2(handle_shr)		SHRB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB1_GRP2(handle_sar)		SARB_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_EB1_GRP2

#undef GETP2BYTE
#define GETP2BYTE() cl

#define DEF_OCHANDLER_EBCL_GRP2(handler)	\
DEF_OCHANDLER(handler##EbCL_grp2)	\
	int p1;				\
	EBCL_GRP2_INIT			\

DEF_OCHANDLER_EBCL_GRP2(handle_rol)		ROLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBCL_GRP2(handle_ror)		RORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBCL_GRP2(handle_rcl)		RCLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBCL_GRP2(handle_rcr)		RCRB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBCL_GRP2(handle_shl)		SHLB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBCL_GRP2(handle_shr)		SHRB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EBCL_GRP2(handle_sar)		SARB_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_EBCL_GRP2

#undef GETP2BYTE

#define DEF_OCHANDLER_EB_GRP3(handler)		\
DEF_OCHANDLER(handler##Eb_grp3)		\
	int p1;				\
	EB_GRP3_INIT			\
	
DEF_OCHANDLER_EB_GRP3(handle_not)		NOTB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB_GRP3(handle_neg)		NEGB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB_GRP3(handle_mul)		MULB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB_GRP3(handle_div)		DIVB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB_GRP3(handle_imul)		IMULB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB_GRP3(handle_idiv)		IDIVB_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_EB_GRP3

#define DEF_OCHANDLER_EB_GRP4(handler)		\
DEF_OCHANDLER(handler##Eb_grp4)	\
	int p1;			\
	EB_GRP4_INIT		\
	
DEF_OCHANDLER_EB_GRP4(handle_inc)		INCB_TEXT	END_OCHANDLER
DEF_OCHANDLER_EB_GRP4(handle_dec)		DECB_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_EB_GRP4





#undef ASSIGNP1BYTE
#undef GETP1BYTE


/* EwIw handlers, grp1 handlers (both EwIw_grp1 and EwIb_grp1), grp2 handlers (Ew1,EwCL),
 * grp3 handlers (including test and all other WORD operations)
 * grp5 handlers
 */
/* notice: in fact, test is EwIw, it's simply called IwEw to be more comfortable */
#define ASSIGNP1WORD(x) SET_MEM_WORD2(p1,x)
#define GETP1WORD() ( GET_MEM_WORD2(p1)      )
#define GETP2WORD() ( (unsigned short)(p2) )

#define DEF_OCHANDLER_EWIW(handler) \
DEF_OCHANDLER(handler##EwIw)	\
	int p1,p2;		\
	EWIW_INIT		\
	
#define DEF_OCHANDLER_EWIW_GRP1(handler) \
DEF_OCHANDLER(handler##EwIw_grp1)	\
	int p1,p2;			\
	EWIW_GRP1_INIT			\
	
/* notice: EWIB_GRP2 does not assume the immediate is signed */
#define DEF_OCHANDLER_EWIB_GRP2(handler) \
DEF_OCHANDLER(handler##EwIb_grp2)	\
	int p1,p2;			\
	EWIB_GRP2_INIT			\
	
#define DEF_OCHANDLER_EWIB_GRP1(handler) \
DEF_OCHANDLER(handler##EwIb_grp1)	\
	int p1,p2;			\
	EWIB_GRP1_INIT			\
	
/* notice: EWIB_GRP1_INIT assumes the immediate is SIGNED,
 * it is important, cause a cbw (the asm opcode, written here as a type cast)
 * is used instead
 * of plain conversion
 */

DEF_OCHANDLER_EWIW_GRP1(handle_add)	ADDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_or )	ORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_adc)	ADCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_sbb)	SBBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_and)	ANDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_sub)	SUBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_xor)	XORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIW_GRP1(handle_cmp)	CMPW_TEXT	END_OCHANDLER


#define GETP2BYTE() ((unsigned char)(p2))
DEF_OCHANDLER_EWIB_GRP2(handle_rol)		ROLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP2(handle_ror)		RORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP2(handle_rcl)		RCLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP2(handle_rcr)		RCRW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP2(handle_shl)		SHLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP2(handle_shr)		SHRW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP2(handle_sar)		SARW_TEXT	END_OCHANDLER
#undef GETP2BYTE


/* notice: EwIb handlers get WORD handlers, and have their
 * byte value converted to a word
 */
DEF_OCHANDLER_EWIB_GRP1(handle_add)	ADDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_or )	ORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_adc)	ADCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_sbb)	SBBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_and)	ANDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_sub)	SUBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_xor)	XORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWIB_GRP1(handle_cmp)	CMPW_TEXT	END_OCHANDLER


DEF_OCHANDLER_EWIW(handle_mov)		MOVW_TEXT	END_OCHANDLER

/* this one is special for the special-case TEST in grp3 */
DEF_OCHANDLER(handle_testIwEw_grp3) /* should be EwIw */
{
	int p1,p2;
	/* in fact, p1 contains Eb and p2 contains Ib */
	/* it does not matter here cause in TEST
	 * the parameter order does not matter
	 */
	EWIW_GRP1_INIT
	TESTW_TEXT
}
END_OCHANDLER



#undef DEF_OCHANDLER_EWIW_GRP1
#undef DEF_OCHANDLER_EWIB_GRP1
#undef DEF_OCHANDLER_EWIW

#undef GETP2WORD
#define GETP2BYTE() 1

#define DEF_OCHANDLER_EW1_GRP2(handler) \
DEF_OCHANDLER(handler##Ew1_grp2)	\
	int p1;				\
	EW1_GRP2_INIT			\
	
DEF_OCHANDLER_EW1_GRP2(handle_rol)		ROLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW1_GRP2(handle_ror)		RORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW1_GRP2(handle_rcl)		RCLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW1_GRP2(handle_rcr)		RCRW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW1_GRP2(handle_shl)		SHLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW1_GRP2(handle_shr)		SHRW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW1_GRP2(handle_sar)		SARW_TEXT	END_OCHANDLER


#undef GETP2BYTE
#define GETP2BYTE() cl

#define DEF_OCHANDLER_EWCL_GRP2(handler)	\
DEF_OCHANDLER(handler##EwCL_grp2)	\
	int p1;				\
	EWCL_GRP2_INIT			\
	

DEF_OCHANDLER_EWCL_GRP2(handle_rol)		ROLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWCL_GRP2(handle_ror)		RORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWCL_GRP2(handle_rcl)		RCLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWCL_GRP2(handle_rcr)		RCRW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWCL_GRP2(handle_shl)		SHLW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWCL_GRP2(handle_shr)		SHRW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWCL_GRP2(handle_sar)		SARW_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_EWCL_GRP2

#undef GETP2BYTE

#define DEF_OCHANDLER_EW_GRP3(handler)		\
DEF_OCHANDLER(handler##Ew_grp3)	\
	int p1;			\
	EW_GRP3_INIT		\
	

DEF_OCHANDLER_EW_GRP3(handle_not)		NOTW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP3(handle_neg)		NEGW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP3(handle_mul)		MULW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP3(handle_div)		DIVW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP3(handle_imul)		IMULW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP3(handle_idiv)		IDIVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_EW_GRP3

#define DEF_OCHANDLER_EW_GRP5(handler)		\
DEF_OCHANDLER(handler##Ew_grp5)	\
	int p1;			\
	EW_GRP5_INIT		\
	

#define GETP1OWORD(x) ( GET_MEM_WORD2(p1+x)      )

DEF_OCHANDLER_EW_GRP5(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP5(handle_dec)		DECW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP5(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP5(handle_nacall)		NACALL_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP5(handle_facall2)		FACALL2_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP5(handle_ajmp  )		AJMP_TEXT	END_OCHANDLER
DEF_OCHANDLER_EW_GRP5(handle_afjmp )		AFJMP_TEXT	END_OCHANDLER

#undef  GETP1OWORD

#undef ASSIGNP1WORD
#undef GETP1WORD




/* EwGw handlers */
#define ASSIGNP1WORD(x) SET_MEM_WORD2(p1,x)
#define ASSIGNP2WORD(x) (*(unsigned short* )(p2) = (x))

#define GETP1WORD() ( GET_MEM_WORD2(p1)      )
#define GETP2WORD() ( *(unsigned short*)(p2) )

#define DEF_OCHANDLER_EWGW(handler) \
DEF_OCHANDLER(handler##EwGw)	\
	int p1,p2;		\
	EWGW_INIT		\
	

DEF_OCHANDLER_EWGW(handle_add)		ADDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_sub)		SUBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_adc)		ADCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_sbb)		SBBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_xor)		XORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_cmp)		CMPW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_or )		ORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_and)		ANDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_test)		TESTW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_xchg)		XCHGW_TEXT	END_OCHANDLER
DEF_OCHANDLER_EWGW(handle_mov)		MOVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_EWGW

#undef ASSIGNP1WORD
#undef ASSIGNP2WORD

#undef GETP1WORD
#undef GETP2WORD


/* EwSw handlers */
#define ASSIGNP1WORD(x) SET_MEM_WORD2(p1,x)
#define ASSIGNP2WORD(x) (*(unsigned short* )(p2) = (x))

#define GETP1WORD() ( GET_MEM_WORD2(p1)      )
#define GETP2WORD() ( *(unsigned short*)(p2) )

#define DEF_OCHANDLER_EWSW(handler) \
DEF_OCHANDLER(handler##EwSw)	\
	int p1,p2;		\
	EWSW_INIT		\
	

DEF_OCHANDLER_EWSW(handle_mov)		MOVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_EWSW

#undef ASSIGNP1WORD
#undef ASSIGNP2WORD

#undef GETP1WORD
#undef GETP2WORD

/* SwEw handlers */
#define ASSIGNP1WORD(x) (*(unsigned short* )(p1) = (x))
#define ASSIGNP2WORD(x) SET_MEM_WORD2(p2,x)

#define GETP1WORD() (*(unsigned short* )(p1) )
#define GETP2WORD() ( GET_MEM_WORD2(p2)      )

#define DEF_OCHANDLER_SWEW(handler) \
DEF_OCHANDLER(handler##SwEw)	\
	int p1,p2;  		\
	SWEW_INIT   		\

DEF_OCHANDLER_SWEW(handle_mov)		MOVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_SWEW

#undef ASSIGNP1WORD
#undef ASSIGNP2WORD

#undef GETP1WORD
#undef GETP2WORD

/* GbEb handlers */
#define ASSIGNP1BYTE(x) (*(unsigned char* )(p1) = (x))
#define ASSIGNP2BYTE(x) SET_MEM_BYTE2(p2,x)

#define GETP1BYTE() ( *(unsigned char* )(p1) )
#define GETP2BYTE() ( GET_MEM_BYTE2(p2)       )

#define DEF_OCHANDLER_GBEB(handler) \
DEF_OCHANDLER(handler##GbEb)	\
	int p1,p2;		\
	GBEB_INIT		\
	

DEF_OCHANDLER_GBEB(handle_add)		ADDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_sub)		SUBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_adc)		ADCB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_sbb)		SBBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_xor)		XORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_cmp)		CMPB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_or )		ORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_and)		ANDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_GBEB(handle_mov)		MOVB_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_GBEB

#undef ASSIGNP1BYTE
#undef ASSIGNP2BYTE

#undef GETP1BYTE
#undef GETP2BYTE

/* GwEw handlers */
#define ASSIGNP1WORD(x) (*(unsigned short*)(p1) = (x))
#define ASSIGNP2WORD(x) SET_MEM_WORD2(p2,x)

#define GETP1WORD() ( *(unsigned short*)(p1) )
#define GETP2WORD() ( GET_MEM_WORD2(p2)       )
#define GETP2OWORD(x) ( GET_MEM_WORD2(p2+x)       )

#define DEF_OCHANDLER_GWEW(handler) \
DEF_OCHANDLER(handler##GwEw)	\
	int p1,p2;		\
	GWEW_INIT		\
	

DEF_OCHANDLER_GWEW(handle_add)		ADDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_sub)		SUBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_adc)		ADCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_sbb)		SBBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_xor)		XORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_cmp)		CMPW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_or )		ORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_and)		ANDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_mov)		MOVW_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_les)		LES_TEXT	END_OCHANDLER
DEF_OCHANDLER_GWEW(handle_lds)		LDS_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_GWEW

#undef ASSIGNP1WORD
#undef ASSIGNP2WORD

#undef GETP1WORD
#undef GETP2WORD

/* TODO: optimize Immediate handlers to have p2/p1 variable the size
 *       of the immediate
 */
/* ALIb handlers */
#define ASSIGNP1BYTE(x) (al = (x))
#define GETP1BYTE()    (al)
#define GETP2BYTE()    (p2)

#define DEF_OCHANDLER_ALIB(handler) \
DEF_OCHANDLER(handler##ALIb)	\
	int p2;			\
	ALIB_INIT		\
	

DEF_OCHANDLER_ALIB(handle_add)		ADDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_sub)		SUBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_adc)		ADCB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_sbb)		SBBB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_xor)		XORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_cmp)		CMPB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_or )		ORB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_and)		ANDB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_mov)		MOVB_TEXT	END_OCHANDLER
DEF_OCHANDLER_ALIB(handle_test)		TESTB_TEXT	END_OCHANDLER

/* #defines for handle_inALIb  ONLY!!! */
#define GETP2WORD() GETP2BYTE()
DEF_OCHANDLER_ALIB(handle_in )		INB_TEXT	END_OCHANDLER
#undef GETP2WORD

#undef DEF_OCHANDLER_ALIB

#undef ASSIGNP1BYTE
#undef GETP1BYTE
#undef GETP2BYTE





/* IbAL handlers */
#define ASSIGNP2BYTE(x) (al = (x))
#define GETP2BYTE()    (al)
#define GETP1BYTE()    (p1)

#define DEF_OCHANDLER_IBAL(handler) \
DEF_OCHANDLER(handler##IbAL)	\
	int p1;			\
	IBAL_INIT		\
	

/* #defines for handle_outIbAL ONLY!!! */
#define GETP1WORD() GETP1BYTE()
DEF_OCHANDLER_IBAL(handle_out)		OUTB_TEXT	END_OCHANDLER
#undef GETP1WORD

#undef DEF_OCHANDLER_IBAL

#undef ASSIGNP2BYTE
#undef GETP2BYTE
#undef GETP1BYTE



/* AXIw handlers */
#define ASSIGNP1WORD(x) (ax = (x))
#define GETP1WORD()    (ax)
#define GETP2WORD()    (p2)

#define DEF_OCHANDLER_AXIW(handler) \
DEF_OCHANDLER(handler##AXIw)	\
	int p2;			\
	AXIW_INIT		\
	

DEF_OCHANDLER_AXIW(handle_add)		ADDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_sub)		SUBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_adc)		ADCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_sbb)		SBBW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_xor)		XORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_cmp)		CMPW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_or )		ORW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_and)		ANDW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_test)		TESTW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AXIW(handle_mov)		MOVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_AXIW

#undef ASSIGNP1WORD
#undef GETP1WORD
#undef GETP2WORD


/* ALDX handlers */
#define ASSIGNP1BYTE(x) (al = (x))
#define ASSIGNP2WORD(x) (dx = (x))
#define GETP1BYTE()    (al)
#define GETP2WORD()    (dx)

#define DEF_OCHANDLER_ALDX(handler) \
DEF_OCHANDLER(handler##ALDX)	\
	ALDX_INIT		\

DEF_OCHANDLER_ALDX(handle_in)		INB_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_ALDX

#undef ASSIGNP1BYTE
#undef ASSIGNP2WORD
#undef GETP1BYTE
#undef GETP2WORD


/* DXAL handlers */
#define ASSIGNP2BYTE(x) (al = (x))
#define ASSIGNP1WORD(x) (dx = (x))
#define GETP2BYTE()    (al)
#define GETP1WORD()    (dx)

#define DEF_OCHANDLER_DXAL(handler) \
DEF_OCHANDLER(handler##DXAL)	\
	DXAL_INIT		\
	

DEF_OCHANDLER_DXAL(handle_out)		OUTB_TEXT	END_OCHANDLER


#undef DEF_OCHANDLER_DXAL

#undef ASSIGNP2BYTE
#undef ASSIGNP1WORD
#undef GETP2BYTE
#undef GETP1WORD




/* Ib handlers */
#define GETP1BYTE() (p1)

#define DEF_OCHANDLER_IB(handler) \
DEF_OCHANDLER(handler##Ib)	\
	int p1;			\
	IB_INIT			\
	


/* this #define is quick and meant to do easy declaring
 * of all conditional JMPs
 */
#ifdef CHECK_FLAG_USERS

#define DEF_CONDJMPR_OCHANDLER_IB(handler,cond) \
DEF_OCHANDLER_IB(handler)			\
	if (cond) ip =(ip+(signed char)p1) NORMALIZER;		\
	if (!cond_suppliers[last_opcode])	\
		cond_suppliers[last_opcode]++;  \
END_OCHANDLER					\

#else

#define DEF_CONDJMPR_OCHANDLER_IB(handler,cond) \
DEF_OCHANDLER_IB(handler)		\
	if (cond) ip =(ip+(signed char)p1) NORMALIZER;	\
END_OCHANDLER				\

#endif


DEF_CONDJMPR_OCHANDLER_IB(handle_jnz ,!GET_ZF())
DEF_CONDJMPR_OCHANDLER_IB(handle_jz  , GET_ZF())

DEF_CONDJMPR_OCHANDLER_IB(handle_jno ,!GET_OF())
DEF_CONDJMPR_OCHANDLER_IB(handle_jo  , GET_OF())

DEF_CONDJMPR_OCHANDLER_IB(handle_jns ,!GET_SF())
DEF_CONDJMPR_OCHANDLER_IB(handle_js  , GET_SF())

DEF_CONDJMPR_OCHANDLER_IB(handle_jnb ,!GET_CF())
DEF_CONDJMPR_OCHANDLER_IB(handle_jb  , GET_CF())

DEF_CONDJMPR_OCHANDLER_IB(handle_jnbe,!GET_CF() && !GET_ZF())
DEF_CONDJMPR_OCHANDLER_IB(handle_jbe , GET_CF() ||  GET_ZF())

DEF_CONDJMPR_OCHANDLER_IB(handle_jl  , GET_SF() !=  GET_OF())
DEF_CONDJMPR_OCHANDLER_IB(handle_jnl , GET_SF() ==  GET_OF())

DEF_CONDJMPR_OCHANDLER_IB(handle_jle ,(GET_SF() !=  GET_OF()) ||   GET_ZF())
DEF_CONDJMPR_OCHANDLER_IB(handle_jnle,(GET_SF() ==  GET_OF()) && (!GET_ZF()) )

#undef DEF_CONDJMPR_OCHANDLER_IB

DEF_OCHANDLER_IB(handle_interrup) INTERRUP_TEXT END_OCHANDLER
DEF_OCHANDLER_IB(handle_loope)    LOOPE_TEXT    END_OCHANDLER
DEF_OCHANDLER_IB(handle_loopne)   LOOPNE_TEXT   END_OCHANDLER
DEF_OCHANDLER_IB(handle_loop)     LOOP_TEXT     END_OCHANDLER
DEF_OCHANDLER_IB(handle_jcxz)     JCXZ_TEXT     END_OCHANDLER
DEF_OCHANDLER_IB(handle_rjmp)     RJMPB_TEXT   END_OCHANDLER

#undef DEF_OCHANDLER_IB
#undef GETP1BYTE


/* Iw handlers */
#define GETP1WORD() (p1)

#define DEF_OCHANDLER_IW(handler) \
DEF_OCHANDLER(handler##Iw)	\
	int p1;			\
	IW_INIT			\
	

DEF_OCHANDLER_IW(handle_nret2)  NRET2_TEXT  END_OCHANDLER
DEF_OCHANDLER_IW(handle_fret2)  FRET2_TEXT  END_OCHANDLER
DEF_OCHANDLER_IW(handle_push )  PUSH_TEXT   END_OCHANDLER
DEF_OCHANDLER_IW(handle_nrcall) NRCALL_TEXT END_OCHANDLER
DEF_OCHANDLER_IW(handle_rjmp)   RJMPW_TEXT  END_OCHANDLER

#undef DEF_OCHANDLER_IW
#undef GETP1WORD


/* ES handlers */
#define ASSIGNP1WORD(x) (es = (x))
#define GETP1WORD() (es)

#define DEF_OCHANDLER_ES(handler) \
DEF_OCHANDLER(handler##ES)	\


DEF_OCHANDLER_ES(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_ES(handle_pop)		POP_TEXT	END_OCHANDLER



#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_ES


/* CS handlers */
#define ASSIGNP1WORD(x) (cs = (x))
#define GETP1WORD() (cs)

#define DEF_OCHANDLER_CS(handler) \
DEF_OCHANDLER(handler##CS)	\


DEF_OCHANDLER_CS(handle_push)		PUSH_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_CS


/* SS handlers */
#define ASSIGNP1WORD(x) (ss = (x))
#define GETP1WORD() (ss)

#define DEF_OCHANDLER_SS(handler) \
DEF_OCHANDLER(handler##SS)	\


DEF_OCHANDLER_SS(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_SS(handle_pop)		POP_TEXT	END_OCHANDLER



#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_SS


/* DS handlers */
#define ASSIGNP1WORD(x) (ds = (x))
#define GETP1WORD() (ds)

#define DEF_OCHANDLER_DS(handler) \
DEF_OCHANDLER(handler##DS)	\


DEF_OCHANDLER_DS(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_DS(handle_pop)		POP_TEXT	END_OCHANDLER



#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_DS



/* AX handlers */
#define ASSIGNP1WORD(x) (ax = (x))
#define GETP1WORD() (ax)

#define DEF_OCHANDLER_AX(handler) \
DEF_OCHANDLER(handler##AX)	\


DEF_OCHANDLER_AX(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_AX(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_AX(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_AX(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_AX

/* BX handlers */
#define ASSIGNP1WORD(x) (bx = (x))
#define GETP1WORD() (bx)

#define DEF_OCHANDLER_BX(handler) \
DEF_OCHANDLER(handler##BX)	\


DEF_OCHANDLER_BX(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_BX(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_BX(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_BX(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_BX

/* CX handlers */
#define ASSIGNP1WORD(x) (cx = (x))
#define GETP1WORD() (cx)

#define DEF_OCHANDLER_CX(handler) \
DEF_OCHANDLER(handler##CX)	\


DEF_OCHANDLER_CX(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_CX(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_CX(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_CX(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_CX

/* DX handlers */
#define ASSIGNP1WORD(x) (dx = (x))
#define GETP1WORD() (dx)

#define DEF_OCHANDLER_DX(handler) \
DEF_OCHANDLER(handler##DX)	\


DEF_OCHANDLER_DX(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_DX(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_DX(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_DX(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_DX


/* SP handlers */
#define ASSIGNP1WORD(x) (sp = (x))
#define GETP1WORD() (sp)

#define DEF_OCHANDLER_SP(handler) \
DEF_OCHANDLER(handler##SP)	\


DEF_OCHANDLER_SP(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_SP(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_SP(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_SP(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_SP

/* BP handlers */
#define ASSIGNP1WORD(x) (bp = (x))
#define GETP1WORD() (bp)

#define DEF_OCHANDLER_BP(handler) \
DEF_OCHANDLER(handler##BP)	\


DEF_OCHANDLER_BP(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_BP(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_BP(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_BP(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_BP


/* SI handlers */
#define ASSIGNP1WORD(x) (si = (x))
#define GETP1WORD() (si)

#define DEF_OCHANDLER_SI(handler) \
DEF_OCHANDLER(handler##SI)	\


DEF_OCHANDLER_SI(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_SI(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_SI(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_SI(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_SI


/* DI handlers */
#define ASSIGNP1WORD(x) (di = (x))
#define GETP1WORD() (di)

#define DEF_OCHANDLER_DI(handler) \
DEF_OCHANDLER(handler##DI)	\


DEF_OCHANDLER_DI(handle_push)		PUSH_TEXT	END_OCHANDLER
DEF_OCHANDLER_DI(handle_pop)		POP_TEXT	END_OCHANDLER
DEF_OCHANDLER_DI(handle_inc)		INCW_TEXT	END_OCHANDLER
DEF_OCHANDLER_DI(handle_dec)		DECW_TEXT	END_OCHANDLER


#undef ASSIGNP1WORD
#undef GETP1WORD

#undef DEF_OCHANDLER_DI


/* Ew handlers */
#define ASSIGNP1WORD(x) SET_MEM_WORD2(p1,x)

#define GETP1WORD() ( GET_MEM_WORD2(p1)      )

#define DEF_OCHANDLER_EW(handler) \
DEF_OCHANDLER(handler##Ew)	\
	int p1;			\
	EW_INIT			\
	

DEF_OCHANDLER_EW(handle_pop)	POP_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_EW

#undef ASSIGNP1WORD

#undef GETP1WORD


/* AXCX, AXDX, ... xchg only handlers */
/* NOTICE: everytime an xchg change is needed, it needs to be done
 * both here and in XCHG_TEXT
 */
#define DECLARE_REG_XCHG(handlersuffix,reg1,reg2) \
DEF_OCHANDLER(handle_xchg##handlersuffix)	\
						\
	unsigned short xchgtmp;			\
	xchgtmp                  = reg2;	\
	reg2 = reg1;				\
	reg1 = xchgtmp;				\
						\
END_OCHANDLER					\


DECLARE_REG_XCHG(AXCX,ax,cx)
DECLARE_REG_XCHG(AXDX,ax,dx)
DECLARE_REG_XCHG(AXBX,ax,bx)
DECLARE_REG_XCHG(AXSP,ax,sp)
DECLARE_REG_XCHG(AXBP,ax,bp)
DECLARE_REG_XCHG(AXSI,ax,si)
DECLARE_REG_XCHG(AXDI,ax,di)


#undef DECLARE_REG_XCHG




/* ALIb, CLIb, ..., AXIw, BXIw ... mov only handlers */
/* NOTICE: everytime a mov change is needed, it needs to be done
 * both here and in MOV_TEXT
 */
#define DECLARE_I2REG_MOV(handlersuffix,reg,value_taker) \
DEF_OCHANDLER(handle_mov##handlersuffix)	\
						\
	reg = value_taker();			\
						\
END_OCHANDLER					\

/* AXIw and ALIb are already implemented else where and use MOV_TEXTs
 * (MOVB_TEXT and MOVW_TEXT)
 */

DECLARE_I2REG_MOV(CLIb,cl,get_csip)
DECLARE_I2REG_MOV(DLIb,dl,get_csip)
DECLARE_I2REG_MOV(BLIb,bl,get_csip)
DECLARE_I2REG_MOV(AHIb,ah,get_csip)
DECLARE_I2REG_MOV(CHIb,ch,get_csip)
DECLARE_I2REG_MOV(DHIb,dh,get_csip)
DECLARE_I2REG_MOV(BHIb,bh,get_csip)

DECLARE_I2REG_MOV(CXIw,cx,get_csip_word)
DECLARE_I2REG_MOV(DXIw,dx,get_csip_word)
DECLARE_I2REG_MOV(BXIw,bx,get_csip_word)
DECLARE_I2REG_MOV(SPIw,sp,get_csip_word)
DECLARE_I2REG_MOV(BPIw,bp,get_csip_word)
DECLARE_I2REG_MOV(SIIw,si,get_csip_word)
DECLARE_I2REG_MOV(DIIw,di,get_csip_word)

#undef DECLARE_I2REG_MOV



/* ALOb handlers */
#define ASSIGNP1BYTE(x) (al = (x))
#define ASSIGNP2BYTE(x) (SET_MEM_BYTE2(p2,x))
#define GETP1BYTE()    (al)
#define GETP2BYTE()    (GET_MEM_BYTE2(p2))

#define DEF_OCHANDLER_ALOB(handler) \
DEF_OCHANDLER(handler##ALOb)	\
	int p2;			\
	ALOB_INIT		\
	

DEF_OCHANDLER_ALOB(handle_mov)		MOVB_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_ALOB

#undef ASSIGNP1BYTE
#undef ASSIGNP2BYTE
#undef GETP1BYTE
#undef GETP2BYTE

/* AXOw handlers */
#define ASSIGNP1WORD(x) (ax = (x))
#define ASSIGNP2WORD(x) (SET_MEM_WORD2(p2,x))
#define GETP1WORD()    (ax)
#define GETP2WORD()    (GET_MEM_WORD2(p2))

#define DEF_OCHANDLER_AXOW(handler) \
DEF_OCHANDLER(handler##AXOw)	\
	int p2;			\
	AXOW_INIT		\

DEF_OCHANDLER_AXOW(handle_mov)		MOVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_AXOW

#undef ASSIGNP1WORD
#undef ASSIGNP2WORD
#undef GETP1WORD
#undef GETP2WORD



/* ObAL handlers */
#define ASSIGNP1BYTE(x) (SET_MEM_BYTE2(p1,x))
#define ASSIGNP2BYTE(x) (al = (x))
#define GETP1BYTE()    (GET_MEM_BYTE2(p1))
#define GETP2BYTE()    (al)

#define DEF_OCHANDLER_OBAL(handler) \
DEF_OCHANDLER(handler##ObAL)	\
	int p1;			\
	OBAL_INIT		\
	

DEF_OCHANDLER_OBAL(handle_mov)		MOVB_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_OBAL

#undef ASSIGNP1BYTE
#undef ASSIGNP2BYTE
#undef GETP1BYTE
#undef GETP2BYTE

/* OwAX handlers */
#define ASSIGNP1WORD(x) SET_MEM_WORD2(p1,x)
#define ASSIGNP2WORD(x) (ax = (x))
#define GETP1WORD()    (GET_MEM_WORD2(p1))
#define GETP2WORD()    (ax)

#define DEF_OCHANDLER_OWAX(handler) \
DEF_OCHANDLER(handler##OwAX)	\
	int p1;			\
	OWAX_INIT		\

DEF_OCHANDLER_OWAX(handle_mov)		MOVW_TEXT	END_OCHANDLER

#undef DEF_OCHANDLER_OWAX

#undef ASSIGNP1WORD
#undef ASSIGNP2WORD
#undef GETP1WORD
#undef GETP2WORD



DEF_OCHANDLER(handle_lea)
{
	unsigned char modrm = get_csip();
	short calc_lea_value(unsigned char modrm);



       	regs[REG(modrm)] = calc_lea_value(modrm);
}
END_OCHANDLER

DEF_OCHANDLER(handle_into)
{
	void do_push(int);
	unsigned int t;
	if (GET_OF())
	{
		t = GET_MEM_WORD2(4<<2)   +						
		   (GET_MEM_WORD2((4<<2)+2) << 16);					
											
		goto_interrupt(t);
	}			
}
END_OCHANDLER






DEF_OCHANDLER(handle_afjmp2) /* ajmp - absolute far jmp */
	/* gets it's own parameters */
{

	unsigned short tmp;
	tmp = get_csip_word();
	cs  = get_csip_word();
	ip  = tmp;
/*	fprintf(report_file,"AFJMP2: %x:%x\n",cs,ip); */
}
END_OCHANDLER
















DEF_OCHANDLER(handle_cli)
{
	allow_events = 0;
}
END_OCHANDLER

DEF_OCHANDLER(handle_sti)
{
	allow_events = 1;
}
END_OCHANDLER

DEF_OCHANDLER(handle_clc)
{
	UNSET_CF();
}
END_OCHANDLER

DEF_OCHANDLER(handle_stc)
{
	SET_CF();
}
END_OCHANDLER

DEF_OCHANDLER(handle_std)
{
	SET_DF();
}
END_OCHANDLER

DEF_OCHANDLER(handle_cld)
{
	UNSET_DF();
}
END_OCHANDLER


DEF_OCHANDLER(handle_csovride)
{
	override_seg = cs;
}
END_OCHANDLER

DEF_OCHANDLER(handle_ssovride)
{
	override_seg = ss;
}
END_OCHANDLER

DEF_OCHANDLER(handle_esovride)
{
	override_seg = es;
}
END_OCHANDLER




DEF_OCHANDLER(handle_facall1)
	/* fa = far  and absolute */
	/* this version extrcats the parameter too... */
{
	unsigned short t1,t2;
	void do_push(int);
	t1 = get_csip_word();
	t2 = get_csip_word();
	do_push(cs);
	do_push(ip);
	ip = t1;
	cs = t2;
}
END_OCHANDLER




DEF_OCHANDLER(handle_nret)
{
	unsigned short do_pop();
	ip = do_pop();
}
END_OCHANDLER


DEF_OCHANDLER(handle_fret)
{
	unsigned short do_pop();
	ip = do_pop();
	cs = do_pop();
}
END_OCHANDLER

DEF_OCHANDLER(handle_iret)
{
	int __fucker;
	unsigned short do_pop();
	{ __fucker = allow_events = 1; }/* NOTICE: iret sets allow_events to 1 */
	ip = do_pop();
	cs = do_pop();
	flags = do_pop();
/*	printf("BHole: returned to %x:%x (opcode: %x)\n",cs,ip,GET_MEM_BYTE(cs,ip),flags); */
}
END_OCHANDLER



DEF_OCHANDLER(handle_rep)
{
	rep = REP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_repne)
{
	rep = REPNE;
}
END_OCHANDLER

/* used in conjunction with HYPER_SPEED_MODE to improve movsb */
#define CX_THRESHOLD 5 

DEF_OCHANDLER(handle_movsb)
{
	unsigned int ptr1,ptr2;

	
	if (rep == NOREP)
	{
		SET_MEM_BYTE2((es<<4)+di,GET_MEM_BYTE2((ds<<4)+si));
		if (GET_DF())
		{
			si--;
			di--;
		}
		else
		{
			si++;
			di++;
		}
		return;		
	}
	else if (rep == REPNE)
	{
		fputs("BHole: handle_movsb with REPNE not implemented",report_file);
		exit_bhole();
	}

#if defined(HYPER_SPEED_MODE) && defined(CX_THRESHOLD)
	/* TODO: improve movsb optimization for the case of a reversed
	 *       direction - notices:
	 *       ptr1+cx and ptr2+cx should actually be
	 *	 ptr1+cx-1 and ptr2+cx-1 but it's ugly and may also be
	 *	 inefficient a bit
	 */	 
	if (cx > CX_THRESHOLD)
	{
		if (!GET_DF())
		{
			ptr1=(ds<<4)+si; ptr2=(es<<4)+di;
			if (IS_BELOW_HIGHMEM_OFS(ptr1+cx) && 
			    IS_BELOW_HIGHMEM_OFS(ptr2+cx))
			{
				memcpy(__memory__+ptr2,__memory__+ptr1,cx);
				si+=cx;
				di+=cx;
				cx = 0;
				return;
			}
		}
	}
#endif

	if (!GET_DF())
	{
		ptr1=(ds<<4)+si; ptr2=(es<<4)+di;
		si+=cx;
		di+=cx;
		while(cx)
		{
			SET_MEM_BYTE2(ptr2,GET_MEM_BYTE2(ptr1));
			ptr1++;
			ptr2++;
			cx--;
		}
	}
	else
	{
		/* check for wrap around case ((si/di=3,2,1,0,0xFFFF) etc.) */
		while(cx)
		{
			SET_MEM_BYTE(es,di,GET_MEM_BYTE(ds,si));
			si--; di--; cx--;			
		}
	}

	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_movsw)
{
	unsigned short ocx;
	unsigned short tmp;
	if (rep == NOREP)
	{
		ocx = cx;
		cx = 1;
	}
	else if (rep == REPNE)
	{
		fputs("BHole: handle_movsb with REPNE not implemented\n",report_file);
		exit_bhole();
	}

	while(cx)
	{

		tmp = GET_MEM_WORD(ds,si);
		/* seems like my compiler insists on putting
		 * GET_MEM_WORD in tmp instead of just using
		 * it as a param
		 */
		SET_MEM_WORD(es,di,tmp);
 		if (GET_DF())
		{
			si-=2;
			di-=2;
		}
		else
		{
			si+=2;
			di+=2;
		}
		cx--;
	}

	if (rep == NOREP) cx = ocx;

	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_cmpsb)
{
	unsigned char zfc;
	void do_byte_cmp(int a,int b);

	if (rep == NOREP)
	{
		do_byte_cmp(GET_MEM_BYTE(ds,si),GET_MEM_BYTE(es,di));
		if (GET_DF())
		{
			si--;
			di--;

		}
		else
		{
			si++;
			di++;
		}
	}
	else
	{
		if (rep == REPE) /* should we check for zero flag on or off */
		{
			zfc = ZF_ON;
		}
		else
		{
			zfc = 0;
		}

		while(cx != 0 && GET_ZF() != zfc)
		{
			do_byte_cmp(GET_MEM_BYTE(ds,si),GET_MEM_BYTE(es,di));
			cx--;
			if (GET_DF())
			{
				si--;
				di--;
			}
			else
			{
				si++;
				di++;
			}
		}
	}
	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_cmpsw)
{
	unsigned char zfc;
	void do_word_cmp(int a,int b);

	if (rep == NOREP)
	{
		do_word_cmp(GET_MEM_WORD(ds,si),GET_MEM_WORD(es,di));
		if (GET_DF())
		{
			si-=2;
			di-=2;
		}
		else
		{
			si+=2;
			di+=2;
		}
	}
	else
	{
		if (rep == REPE) /* should we check for zero flag on or off */
		{
			zfc = ZF_ON;
		}
		else
		{
			zfc = 0;
		}

		while(cx != 0 && GET_ZF() != zfc)
		{
			do_word_cmp(GET_MEM_WORD(ds,si),GET_MEM_WORD(es,di));
			cx--;
			if (GET_DF())
			{
				si-=2;
				di-=2;
			}
			else
			{
				si+=2;
				di+=2;
			}
		}
	}
	rep = NOREP;
}
END_OCHANDLER



DEF_OCHANDLER(handle_scasb)
{
	unsigned char zfc;
	void do_byte_cmp(int a,int b);



	if (rep == NOREP)
	{
		do_byte_cmp(al,GET_MEM_BYTE(es,di));
		if (GET_DF()) di--;
		else          di++;
	}
	else
	{
		if (rep == REPE) /* should we check for zero flag on or off */
		{
			zfc = 0;
			SET_ZF();
		}
		else
		{
			zfc = ZF_ON;
			UNSET_ZF();
		}

		while(cx != 0 && GET_ZF() != zfc)
		{
			do_byte_cmp(al,GET_MEM_BYTE(es,di));
			cx--;
			if (GET_DF())
			{
				di--;
			}
			else
			{
				di++;
			}
		}
	}
	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_scasw)
{
	unsigned char zfc;
	void do_word_cmp(int a,int b);

	if (rep == NOREP)
	{
		do_word_cmp(ax,GET_MEM_WORD(es,di));
		if (GET_DF()) di-=2;
		else          di+=2;
	}
	else
	{
		if (rep == REPE) /* should we check for zero flag on or off */
		{
			zfc = ZF_ON;
		}
		else
		{
			zfc = 0;
		}

		while(cx != 0 && GET_ZF() != zfc)
		{
			do_word_cmp(ax,GET_MEM_WORD(es,di));
			cx--;
			if (GET_DF())
			{
				di-=2;
			}
			else
			{
				di+=2;
			}
		}
	}
	rep = NOREP;
}
END_OCHANDLER


DEF_OCHANDLER(handle_stosb)
{
	unsigned short ocx;


	if (rep == NOREP)
	{
		ocx = cx;
		cx = 1;
	}
	else if (rep == REPNE)
	{
		fputs("BHole: handle_movsb with REPNE not implemented\n",report_file);
		exit_bhole();
	}

	while(cx)
	{
		SET_MEM_BYTE(es,di,al);
		if (GET_DF())
		{
			di--;
		}
		else
		{
			di++;
		}
		cx--;
	}

	if (rep == NOREP) cx = ocx;

	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_stosw)
{
	unsigned short ocx;

	if (rep == NOREP)
	{
		ocx = cx;
		cx = 1;
	}
	else if (rep == REPNE)
	{
		fputs("BHole: handle_movsb with REPNE not implemented\n",report_file);
		exit_bhole();
	}

	while(cx)
	{
		SET_MEM_WORD(es,di,ax);
		if (GET_DF())
		{
			di-=2;
		}
		else
		{
			di+=2;
		}
		cx--;
	}

	if (rep == NOREP) cx = ocx;

	rep = NOREP;
}
END_OCHANDLER









DEF_OCHANDLER(handle_lodsb)
{
	unsigned short ocx;


	if (rep == NOREP)
	{
		ocx = cx;
		cx = 1;
	}
	else if (rep == REPNE)
	{
		fputs("BHole: handle_movsb with REPNE not implemented\n",report_file);
		exit_bhole();
	}

	while(cx)
	{
		al = GET_MEM_BYTE(ds,si);
		if (GET_DF())
		{
			si--;
		}
		else
		{
			si++;
		}
		cx--;
	}


	if (rep == NOREP) cx = ocx;

	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_lodsw)
{
	unsigned short ocx;

	if (rep == NOREP)
	{
		ocx = cx;
		cx = 1;
	}
	else if (rep == REPNE)
	{
		fputs("BHole: handle_movsb with REPNE not implemented\n",report_file);
		exit_bhole();
	}

	while(cx)
	{
		ax = GET_MEM_WORD(ds,si);
		if (GET_DF())
		{
			si-=2;
		}
		else
		{
			si+=2;
		}
		cx--;
	}

	if (rep == NOREP) cx = ocx;

	rep = NOREP;
}
END_OCHANDLER

DEF_OCHANDLER(handle_nop )
	/* the most complicated routine in this program... */
{
}
END_OCHANDLER






DEF_OCHANDLER(handle_ror)
{
	fputs("handle_ror failed\n",report_file);
	exit_bhole();
}
END_OCHANDLER







DEF_OCHANDLER(handle_cwd)
{
	static unsigned short cwd_lut[2] = {0x0000,0xFFFF};
	dx = cwd_lut[(((signed short)ax) < 0)];
}
END_OCHANDLER

DEF_OCHANDLER(handle_cbw)
{
	static unsigned char  cwd_lut[2] = {0x00,0xFF};
	ah = cwd_lut[(((signed char )al)< 0)];
}
END_OCHANDLER

DEF_OCHANDLER(handle_pushf)
{
	void do_push(int);
	do_push(flags);
}
END_OCHANDLER

DEF_OCHANDLER(handle_popf)
{
	unsigned short do_pop();
	flags = do_pop();
}
END_OCHANDLER


DEF_OCHANDLER(handle_pusha)
{
	void do_push(int);
	do_push(ax);
	do_push(cx);
	do_push(dx);
	do_push(bx);
	do_push(sp);
	do_push(bp);
	do_push(si);
	do_push(di);
}
END_OCHANDLER

DEF_OCHANDLER(handle_popa)
{
	unsigned short do_pop();
	di = do_pop();
	si = do_pop();
	bp = do_pop();
	sp = do_pop();
	bx = do_pop();
	dx = do_pop();
	cx = do_pop();
	ax = do_pop();
}
END_OCHANDLER

DEF_OCHANDLER(handle_cmc)
{
	flags = flags ^ GET_CF();
}
END_OCHANDLER

DEF_OCHANDLER(handle_lahf)
{
	ah = flags;
}
END_OCHANDLER

DEF_OCHANDLER(handle_sahf)
{
	flags = ah;
}
END_OCHANDLER
/*
DEF_OCHANDLER(handle_loopIb)
{
	
}
*/



unsigned short ax_bak,cx_bak,dx_bak,bx_bak,sp_bak,bp_bak,si_bak,di_bak;

/* storea and loada are BHole opcodes, not CPU opcodes */
/* they are extenstions used because of my laziness */
/* and will probably removed later along the way... */
DEF_OCHANDLER(handle_storea)
{
	ax_bak = ax;
	cx_bak = cx;
	dx_bak = dx;
	bx_bak = bx;
	sp_bak = sp;
	bp_bak = bp;
	si_bak = si;
	di_bak = di;
}
END_OCHANDLER



DEF_OCHANDLER(handle_loada)
{
	ax = ax_bak;
	cx = cx_bak;
	dx = dx_bak;
	bx = bx_bak;
	sp = sp_bak;
	bp = bp_bak;
	si = si_bak;
	di = di_bak;
}
END_OCHANDLER

/* bholeint (or handle_bholeint) is a BHole operator used for silly internals
 */
 
DEF_OCHANDLER(handle_bholeint)
{
	unsigned short do_pop();
	int do_int13();									
	int do_int16();									
	int do_int21();									
	int do_int33();									
	int do_int67();									
	int do_inttime();								
	int do_vidint();								
	void do_push(int);								
											
	unsigned short intnum;								

	intnum = get_csip();			
	switch(intnum)									
	{										
	case 0x10:									
		if (!do_vidint()) exit_bhole();		
		break;									
	case 0x13:									
		if (!do_int13 ()) exit_bhole();		
		break;									
	case 0x1A:									
		if (!do_inttime()) exit_bhole();		
		break;									
	case 0x16: /* BIOS keyboard interrupts */					
		if (!do_int16()) exit_bhole();		
		break;									
	case 0x21: /* DOS interrupts */							
		if (!do_int21()) exit_bhole();		
		break;									
	case 0x33: /* mouse interrupts */						
		if (!do_int33()) exit_bhole();		
		break;									
	case 0x67: /* EMS driver interrupts */					
		if (!do_int67()) exit_bhole();		
		break;									
	default:									
		fprintf(report_file,"BHole: unknown interrupt %x\n",intnum     );	
		exit_bhole();				
	}										
	ip = do_pop();									
	cs = do_pop();
	sp+=2; /* do not pop back the flags - they are only popped back from
	        * emulated code interrupts, real BHole code
		* only modifies flags when it is needed, so
		* there is no need in restoring flags 
		*/
	allow_events = 1;

}
END_OCHANDLER
/******************* end of OCHANDLERs */




/* int21_createfile */
/* CANCELLED TEMPORARILY, SEEMS UNNEEDED FOR NOW!!!
 * PROBABLY HAS BUGS IN IT...
 *
void int21_createfile()
{
/*	int drive;
/*	char filename[9] = {0};
/*	/* ds:dx should be pointing to an FCB */
/*	drive = GET_MEM_BYTE(ds,dx);
/*	/* memcpy(filename,memory + (ds << 4) + dx + 1,8); - CANCELLED */
/*
/*	/* this moves the filename instead... */
/*
/*
/*
/*	printf("BHole: creating file %s\n",filename);
/*
}
/*
/*
 */

void emumem_fname2mem(unsigned char *mem,unsigned int seg,unsigned int ofs,
                      unsigned int len)
	/* routine: emumem_fname2mem
	 * purpose: copies a filename from emulator memory
	 *          into an array
	 * notice : what this routine actually does is truncating
	 *          the "C:\\" out of the file ('\\' refers to one backslash)
	 *          and call emumem2mem as usual
	 * params : same as emumem2mem
	 * TODO: the len param is ignored, remove it
	 */
{
	if (GET_MEM_BYTE(seg,ofs+1) == ':')
	{
		ofs+=2;
		if (GET_MEM_BYTE(seg,ofs) == '\\') { ofs++; }	
	}
	emumem2mem(mem,seg,ofs,len);
	fprintf(report_file,"BHole: debug: emumem_fname2mem_debug: %s\n",
	        mem);
	
}


void int21_findfirst()
    /* TODO: this findfirst is totally broken...
     * it assumes there will never be a findnext
     */
{
#ifdef WIN32
	HANDLE h;
	WIN32_FIND_DATA wfd;
	int i;
#else
	FILE *f;
	int h,i;
	int file_size;
#define HANDLE int

#endif
	char fname_array[13];
	char *fname = fname_array;

	emumem_fname2mem(fname,ds,dx,12);
	fname[12] = '\0';

	for(i = 0;i < 12;i++)
	{
		fname[i] = tolower(fname[i]);	
	}


/*	fname = GET_MEM_ADDR(ds,dx); - commented, instead
 *
 *      fname is a new string which has the contents of the emumemory
 *      copied to it
 */

#ifdef WIN32
	h = FindFirstFile(fname,&wfd);
#else
	f = fopen(fname,"rb");
	if (f == NULL)  h = -1;  
	else           {h =  0; fseek(f,0,SEEK_END); file_size = ftell(f); fclose(f); }
	
#endif
	
	fprintf(report_file,"BHole: findfirst(\"%s\") (line %x)\n",fname,line);

	if (h == (HANDLE)-1) /* failure */
	{
		fputs("BHole: failure to int21 find first\n",report_file);
		SET_CF();
		ax = 2;
	}
	else
	{
		UNSET_CF();
		ax = 0;
		h = (HANDLE)0; /* now, we use h as an index */
		SET_MEM_BYTE2(PSPPOS + 0x80, 0x84);

		while(*fname != '.')
		/* TODO: does not support names without a . */
		{
			SET_MEM_BYTE2(PSPPOS + 0x81 + (int)h,*fname);

			h = (HANDLE)((unsigned char*)h + 1);
			fname++;
		}
		fname++;
		while(*fname != '\0')
		{
			SET_MEM_BYTE2(PSPPOS + 0x89 + (int)h,*fname);

			h = (HANDLE)((unsigned char*)h + 1);
			fname++;
		}


#ifdef WIN32
		mem2emudmem(PSPPOS + 0x9A,(unsigned char*)&wfd.nFileSizeLow,4);
#else
		mem2emudmem(PSPPOS + 0x9A,(unsigned char*)&file_size,4);
#endif	

		/* TODO: this strlen thing is ugly, make a strcpy for emulator */
		mem2emudmem(PSPPOS + 0x9E,fname - (int)h - 1,strlen(fname - (int)h - 1));

	}
	fputs("BHole: find first completed\n",report_file);
}

void int21_fileopen()
{
	char fname_array[13];
	char *fname = fname_array;
	int i;

	emumem_fname2mem(fname,ds,dx,12);
	fname[12] = '\0';

	for(i = 0;i < 12;i++)
	{
		fname[i] = tolower(fname[i]);	
	}

	fprintf(report_file,"BHole: openfile(\"%s\")\n",fname);


/* STUPID CODE!!!	if (!strcmp(fname,EXECNAME))
	{
		UNSET_CF(); /* i have no idea why?
		             * DOS does it,
		             * i tought it should be SET_CF()
		             */
/*		ax = 5;
		return;
	} */


	for(i = 0;i < MAXFILES;i++)
	{
		/* search for a free handle */
		if (fhandles[i] == NULL)
		{
			fprintf(report_file,"BHole: openfile: handle is %x\n",i);
			break;
		}
	}
	if (i == MAXFILES)
	{

		fputs("BHole: ERROR!!! TOO MUCH OPEN FILES!!!\n",report_file);
		SET_CF();
	        ax = 0x04;
		return;
	}
	/* note: most EMM/EMS is implemented in ems.c */
	if (!strcmp(fname,"emmxxxx0"))
	{
		fputs("EMM fuck\n",report_file);
		UNSET_CF();
		/* ax = EMM_DEV_HANDLE; - cancelled, EMM handle
		 * is no longer constant
		 */
		emm_dev_handle = ax = i;

		return;
	}

	switch(al & 0x07 /* least significant 3 bits */)
	{
	case 0:
		fhandles[i] = fopen(fname,"rb");
		break;
	case 1:
		fputs("BHole: AL = 1 for int21_openfile\n",report_file);
		fhandles[i] = fopen(fname,"r+b");
		rewind(fhandles[i]);
		break;
	case 2:
		fhandles[i] = fopen(fname,"r+b");
		break;
	default:
		fputs("BHole: wierd value for AL in int21_openfile\n",report_file);
	}
	if (fhandles[i] == NULL)
	{
		SET_CF();
		/* could be something else but i am too lazy to check */
		/* 0x02 - file not found */
		ax = 0x02;
		fputs("BHole: failure to open file\n",report_file);
		return;
	}
	UNSET_CF();
	ax = i;
}

/* int21_fileclose */
void int21_fileclose()
{
	/* note: most emm/ems stuff are in ems.c */
	if (bx == emm_dev_handle)
	{
		UNSET_CF();
		fputs("BHole: closefile(EMM_DEVICE)\n",report_file);
		emm_dev_handle = -1;
		return;
	}
	if (fhandles[bx] == NULL)
	{
		ax = 0x06;
		SET_CF();
		return;
	}
	fclose(fhandles[bx]);
	fhandles[bx] = NULL;
	UNSET_CF();
	fprintf(report_file,"BHole: closefile(\"%d\")\n",bx);
	return;

}

/* int21_readfile */
void int21_fileread()
{
	int dx2 = dx;
	int cx2 = cx;
	int tmp;
	fprintf(report_file,"BHole: int21_fileread(%x,%x) -> %x:%x (at %x:%x)\n",bx,cx,ds,dx,cs,ip);
	if (fhandles[bx] == NULL)
	{
		fputs("BHole: int21_fileread tried reading from an invalid handle\n",report_file);
		ax = 0x06;
		SET_CF();
		return;
	}
	/* ax = fread(GET_MEM_ADDR(ds,dx),1,cx,fhandles[bx]); - CANCELLED */


	while(cx2)
	{
		tmp = fgetc(fhandles[bx]);
		SET_MEM_BYTE(ds,dx2,tmp);
		if (tmp == EOF) break;
		cx2--;
		dx2++;
	}


	if (tmp == EOF)
	{
		fprintf(report_file,"BHole: fileread: read only %x bytes\n",cx - cx2);
		ax = cx - cx2;
		UNSET_CF();
		return;
	}
	else
	{
		ax = cx;
		UNSET_CF();
	}
	return;
}


/* int21_filewrite */
void int21_filewrite()
{
	int cx2 = cx,dx2 = dx;
	int tmp;

	fprintf(report_file,"BHole: int21_filewrite(%x)\n",bx);
	if (fhandles[bx] == NULL)
	{
		ax = 0x06;
		SET_CF();
		return;
	}
	if (fhandles[bx] == stdout)
	{
		fputs("BHole: ---begin STDOUT output---\n",report_file);
	}
	if (cx == 0)
	{
		fputs("BHole: WARNING!!! file truncation using int21_filewrite CX = 0\n",report_file);
		fputs("      is not implemented\n",report_file);
		UNSET_CF();
		return;
	}
	/* ax = fwrite(GET_MEM_ADDR(ds,dx),1,cx,fhandles[bx]); - CANCELLED */

	while(cx2)
	{
		tmp = fputc(GET_MEM_BYTE(ds,dx2),fhandles[bx]);
		if (tmp == EOF) break;
		cx2--;
		dx2++;
	}


	if (fhandles[bx] == stdout)
	{
		fputs("BHole: ---end   STDOUT output---\n",report_file);
	}
	if (tmp == EOF)
	{
		ax = 0x05;
		SET_CF();
		return;
	}
	else
	{
		ax = cx;
	}
	UNSET_CF();
	return;
}


/* int21_fileseek */
void int21_fileseek()
{
	int origintabl[] = {SEEK_SET,SEEK_CUR,SEEK_END};
	long pos;
	fprintf(report_file,"fileseek(%x,%x,%x)\n",bx,((cx << 16) + dx),al);
	if (fhandles[bx] == NULL)
	{
		ax = 0x06;
		SET_CF();
		return;
	}

	if (al > 2)
	{
		fputs("BHole: invalid AL for int21_fileseek()\n",report_file);
		ax = 0x01;
		SET_CF();
		return;
	}
	fseek(fhandles[bx],(signed long)((cx << 16) + dx),origintabl[al]);
	pos = ftell(fhandles[bx]);
	ax = pos & 0xFFFF;
	dx = (pos & 0xFFFF0000) >> 16;
	UNSET_CF();

}

/* int21_devinfo */
void int21_devinfo()
{
	/* TODO: should i do ax = dx = value or just dx = value */

	fputs("BHole: i hate int21_devinfo\n",report_file);
	/* i really have no idea what this does... */
	UNSET_CF(); /* i think it should be here... */
	if (bx == 0 || bx == 1)
	{
		dx = 0x80D3;
	}
	else if (bx == emm_dev_handle)
	{
		dx = 0xC0C0; /* why? who know */
	}
	else if (bx > 4)
	{
		dx = 0x43; /* TODO: fully implement this */
	}
	else
	{
		fputs("BHole: FUCK!!! interrupt21_devinfo not called for \"lovely\" BX\n",report_file);
		SET_CF();
	}

}

void do_vidint_getmode()
{
	bh = vgadisp;
	switch(al = vgamode)
	{
	case 3:
		ah = 0x50;
		break;

	default:
		fprintf(report_file,"BHole: do_vidint_getmode: ERROR, do not know what to put\n");
		fprintf(report_file,"                         in ah for al = %x\n",al);
	}
}

void do_vidint_egainfo()
{
	bh = 0x00; /* always in color mode? */
	bl = 0x03; /* 256k of video memory */
	cx = 0x0F09; /* hope this is right always... */
	al = 0x12; /* i have no idea why... */
}

void do_vidint_alterfunc()
{
	switch(bl)
	{
	case 0x10:
		do_vidint_egainfo();
		break;
	default:
		fprintf(report_file,"BHole: do_vidint_alterfunc: unknown bl = %x\n",bl);
	}
}

int do_vidint_setmode()
{
	switch(al)
	{
	case 0x13:
		fputs("BHole: switching to mode 0x13\n",report_file);
		vidsurface = SDL_SetVideoMode(320,200,8,SDL_SWSURFACE);
		vgamode = 0x13;
		break;
	case 0x3:
		fputs("BHole: WARNING!!!: switched back to mode 0x3\n",report_file);
		break;
	case 0x4:
		fputs("BHole: switching to mode 0x4\n",report_file);
		vidsurface = SDL_SetVideoMode(320,200,8,SDL_HWSURFACE);
		SDL_SetPalette(vidsurface,SDL_PHYSPAL,palette,0,16);
		
		vgamode = 0x4;
#ifdef ASSUME_CGA
		cga_16_mode = 0;
#endif
		break;
	default:
		fprintf(report_file,"BHole: do_vidint_setmode not implemented for mode %x\n",al);
		return 0;
	}
	return 1;
}

/* do_vidint */
int do_vidint()
{
	unsigned int i;
	switch(GET_AH())
	{
	case 0x0:
		return do_vidint_setmode();
	case 0x1:
		cursor_top = ch & 15;
		cursor_bottom = cl;
		return 1;
	case 0x2:
		cursor_y = dh;
		cursor_x = dl;
		return 1;
	case 0x3:
		ch = cursor_top;
		cl = cursor_bottom;
		dh = cursor_y;
		dl = cursor_x;
		return 1;
	case 0x6:
		fputs("BHole: int 10h AH=6 (some window shit) ignored...\n",report_file);
		return 1;
	case 0x8:
		bh = 0x00;
		ax = 0x00; /* i'm too lazy to actually do this... */
		break;
	case 0x9:
		for(i = 0;i < cx;i++)
		{		
			if (al == 0xD) cursor_x = 0;
			else if (al == 0xA) cursor_y++;
			SET_MEM_BYTE2(0xB8000+((cursor_x+cursor_y*80)<<1),al);
			cursor_x++;
			if (cursor_x == 80) { cursor_x = 0; cursor_y++; }
			if (cursor_y == 25) { cursor_y = 24; }
		}
		break;
	case 0x0B:
		fputs("BHole: int 10,b - set background/palette ignored\n",report_file);
		break;										
	case 0x0E:
		putchar(al);
		break;
	case 0x0F:
		do_vidint_getmode();
		break;
	case 0x12:
		do_vidint_alterfunc();
		break;
	case 0x10:
		if (ah == 0x10)
		{
			palette[bx].r = (dh << 2);
			palette[bx].g = (ch << 2);
			palette[bx].b = (cl << 2);
			palette[bx].unused = 0;
		}
		else
		{
			fprintf(report_file,"BHole: unknown AL %x for int 10 AH 10\n",al);
		}
		break;
	default:
		fprintf(report_file,"BHole: unknown AH %x for int 10\n",GET_AH());
		return 0;
	}
	return 1;
}


void int21_getmod()
{
/* TODO: fully implement this,
 */

	char fname_array[13];
	char *fname = fname_array;
	struct stat statbuf;

	emumem_fname2mem(fname,ds,dx,12);
	fname[12] = '\0';
	if (stat(fname,&statbuf) == -1)
	{
		SET_CF();
		ax = 2;
		return;
	}
	UNSET_CF();
	if (statbuf.st_mode & S_IFDIR)
	{
		cx = 0x10; /* directory */
	}
	else
	{
		cx = 0x20; /* normal file, assume all normal files
		            * only have the "archive" attribute
		            */
	}
	ax = cx;
}

void int21_ah43()
{
	switch(al)
	{
	case 0x00:
		int21_getmod();
		break;
	}
}

void int21_memalloc()
{
	/* TODO: there are many common parts to realloc and memalloc
	 *       i should create small routines that will be called by
	 *       both of them
	 */
	/* TODO: currently this routine
	 * uses the simplest allocation strategy,
	 * it allocates the first block that it sees fit
	 * instead, it should allocate the block that's nearest
	 * in size to the free data
	 */
	/* TODO: there are some small inefficencies in memory allocation
	 *       routines, in order to make them more readable
	 */
	unsigned int ofs;
	unsigned int cntmax = 0;
	unsigned int cnt; /* sizes are measured in paragraphs */
	fprintf(report_file,"BHole: memalloc_debug: bx = %x\n",bx);
	ofs = FIRSTMCB_POS; /* go to the first MCB */
	ofs+= (GET_MEM_WORD2(ofs+3)<<4) + 16; /* go to the next one */
	
	while(1) /* the loop's exit conditions are commented inside... */
	{
		if ( (ofs = skip_used_mcbs(ofs)) == 0xFFFFFFFF)
		{	/* exit condition 1: not enough memory */
			ax = 8;
			bx = cntmax;
			SET_CF();
			fprintf(report_file,
			"BHole: int21_memalloc: not enough memory (bx=%x)\n",bx);
			return;
		}
		
		cnt = count_free_paras(ofs,bx);
		if (cnt == bx)
		{	/* exit condition 2A: found "perfect" block */
			ax = ofs;
			UNSET_CF();
			SET_MEM_WORD2(ofs+1,PSPSEG);
			return;
		}
		else if (cnt > bx)
		{	/* exit condition 2B: found a "too big" block
			 *                    that will be split in two
			 */
			ax = ofs;
			UNSET_CF();
			SET_MEM_WORD2(ofs+1,PSPSEG);
			SET_MEM_WORD2(ofs+3,bx);
			make_mcb(ofs+16+(cnt<<4),0,cnt - bx - 1);
			return;
		}
		if (cnt > cntmax) cntmax = cnt;
		ofs+=16+(cnt<<4);
	}
}

void int21_printstring()
{
	unsigned int dx2;


	dx2 = dx;
	fputs("BHole: ---begin STDOUT output---\n",report_file);

	while(GET_MEM_BYTE(ds,dx2) != '$')
	{
		fputc(GET_MEM_BYTE(ds,dx2),stdout);
		dx2++;
	}
	fputs("BHole: ---end   STDOUT output---\n",report_file);

}


void int33_sethandler()
{
	mouse_callmask = cx;
	mousehandler_cs = es;
	mousehandler_ip = dx;
}

void int33_swaphandler()
{
	unsigned int t;
#define swap(a,b) { t = a; a = b; b = t; } 
	swap(mouse_callmask ,cx);
	swap(mousehandler_cs,es);
	swap(mousehandler_ip,dx);
	fprintf(report_file,"BHole: int33: mouse handler set to %x:%x\n",es,dx);
	fprintf(report_file,"BHole: int33: call mask is %x\n",cx);
}

/* do_int33 */
int do_int33()
{
	switch(ax)
	{
	case 0: /* TODO: more initializations */
	case 0x21: /* TODO: 0 and 0x21 are NOT the same */
		ax = 0xFFFF;
		bx = 2;
		return 1;
	case 3:
		bx = mousebtns;
		cx = mouseposx;
		dx = mouseposy;
		return 1;
	case 0x4:
		mouseposx = cx;
		mouseposy = dx;
		return 1;
	case 7:
	case 8:
		fputs("BHole: WARNING: int 33 define cursor range IGNORED\n",report_file);
		return 1;
	case 0x0C:
		int33_sethandler();
		return 1;
	case 0x14:
		int33_swaphandler();
		return 1;
	default:
		fprintf(report_file,"BHole: int33: unknown ax %x\n",ax);
		return 0;
	}

}

int int21_realloc()
{
	int result;
	if (es != PSPSEG)
		/* TODO: we use a diversion, it seems like the memory
		 *       should be initialized with 3 MCBs
		 *       one for the environment, one for
		 *       the PSP and the image and one for free memory
		 */
	{
		fputs("BHole: cannot handle ES value in int21_realloc\n",report_file);
		fputs("BHole: ALERT!!!\n",report_file);
		return 0;
	}

/*	fprintf(report_file,"BHole: \"allocated\" mem re-sized from %x to %x\n",
	       imgsize,bx << 4); */

	result = resize_first_mblk(FIRSTMEMB_SIZE + bx);
	if (result < 0)
	{
		SET_CF();
		ax = 0x08;
		bx = -result;
	}
	else
	{
		UNSET_CF(); 
/*		imgsize = (bx << 4); */
	}
	return 1;
}

void do_int13_read()
{
	unsigned int cnt = al * SECTOR_SIZE;
	unsigned short bx2 = bx;
	int tmp;
	/* TODO: i assume this only reads from a floppy */
	fseek(execfile,SECTOR_SIZE * (cl - 1) + CYLINDER_SIZE * ch + HEAD_SIZE * dh,SEEK_SET); 
	fprintf(report_file,"doing int13 read: cnt %x\n",cnt);
	while(cnt)
	{
		tmp = fgetc(execfile);
		SET_MEM_BYTE(es,bx2,tmp);
	/*	printf("reading: %x -> %x:%x\n",tmp,es,bx2); */
		if (tmp == EOF) break;
		cnt--;
		bx2++;
	}
	if (!cnt)
	{
		UNSET_CF();
		ah = 0;
	}
	else
	{
		SET_CF();
		al = al - cnt;
		ah = 0x04;
		puts("BHole: warning int13_read has failed");	
	}
}

/* do_int13 */
int do_int13() /* TODO: i assume this interrupt is for boot-image only */
{
	switch(ah)
	{
	case 0x0:
		UNSET_CF();
		ah = 0;
		return 1;
	case 0x2:
		do_int13_read();
		return 1;
	}
	return 0;
}

/* do_int16 */
int do_int16()
{
/*	MSG msg;
	RECT vidwin_rect; */
	switch(ah)
	{
	case 0x1:
	case 0x11:
/*		GetWindowRect(vidwin,&vidwin_rect); 
		InvalidateRect(vidwin,&vidwin_rect,FALSE);	
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);
		TranslateMessage(&msg);
		DispatchMessage(&msg); */
		if (keybufi != 0) /* temporary code */
		{
			UNSET_ZF();
			ax = keybuf[keybufi - 1];	
		}
		else SET_ZF();
		break;
	case 0x0:
	case 0x10:
		/* TODO: better implementation... */
		while(!keybufi)
		{
			handle_events();
		}
		ax = keybuf[keybufi - 1];
		empty_keybuf();
		break;
	default:
		fprintf(report_file,"BHole: do_int16: unknown AH %x\n",ah);
		return 0;
	}
	return 1;
}


void int21_createfile()
{
	/* TODO: we should use CX and not ignore it */
	char fname_array[13];
	char *fname = fname_array;
	int i;
	
	emumem_fname2mem(fname,ds,dx,12);
	fname[12] = '\0';

	for(i = 0;i < 12;i++)
	{
		fname[i] = tolower(fname[i]);	
	}

	fprintf(report_file,"BHole: createfile(\"%s\")\n",fname);

	for(i = 0;i < MAXFILES;i++)
	{
		/* search for a free handle */
		if (fhandles[i] == NULL)
		{
			fprintf(report_file,"BHole: createfile: handle is %x\n",i);
			break;
		}
	}
	if (i == MAXFILES)
	{

		fputs("BHole: ERROR!!! TOO MUCH OPEN FILES!!!\n",report_file);
		SET_CF();
	        ax = 0x04;
		return;
	}

	fhandles[i] = fopen(fname,"wb");
	
	if (fhandles[i] == NULL)
	{
		SET_CF();
		/* could be something else but i am too lazy to check */
		/* 0x02 - file not found */
		ax = 0x02;
		fputs("BHole: failure to open/create file\n",report_file);
		return;
	}
	UNSET_CF();
	ax = i;
	
}

void int21_makefcb()

{

        /* TODO: most file handler routines assume that files are in the

         *       8.3 format and do not do a check for illegael file names

         */



	/* TODO: this code for fname conversion shouldn't be copy/pasted

	 *       so many times

	 */



	char fname_array[13];

	char *fname = fname_array;

	unsigned int ofs = (es << 4) + di;

	int i,i2;

	

	emumem_fname2mem(fname,ds,dx,12);

	fname[12] = '\0';



	for(i = 0;i < 12;i++)

	{

		fname[i] = tolower(fname[i]);	

	}



	fprintf(report_file,"BHole: makefcb(\"%s\")\n",fname);

	

        SET_MEM_BYTE2(ofs,0); /* should this be 0 or 3 ??? */

	mem2emudmem(ofs,fname,8);

	/* this loop finds the '.' in the fname */

	for(i = 0;i < 8;i++) if (fname[i] == '.') break;

	i2 = i + 1;

	/* this code does the required "blank-filling" in memory */

	for(     ;i < 8;i++) SET_MEM_BYTE2(ofs+i,'\0');

	

	/* this code copies the file extension */

	/* TODO: i know this routine is unefficient */

        

        for(i = 8;i < 11;i++)

        {

                if (fname[i2] == '\0')

                        break;

                SET_MEM_BYTE2(ofs+i,fname[i2]);

                i++;

        }

        /* this fills in the rest with blanks */

        for(     ;i < 16;i++)

        {

                SET_MEM_BYTE2(ofs+i,0);

        }

}




/* do_int21 */
int do_int21()
{
/* TODO: LUT */
	switch(GET_AH())
	{
	case 0x9:
		int21_printstring();
		return 1;
	case 0x16:
/*		int21_createfile(); */
		return 0;
	case 0x19:
		al = 0x02;
		return 1;
	case 0x1A:
		dta_segment = ds;
		dta_offset  = dx;
		return 1;	
	case 0x29:
		int21_makefcb();
		return 1;
	case 0x30:
		ax = 0x0A07; /* for win95 emulation, this value is 0x07 for win98 emulation
		              * this value is 0x0A07 probably means 7.10 instead of 7.00
			      */
		bx = 0xFF00;
		cx = 0x0000;
		/* TODO: implenet fully */
		return 1;
	case 0x3C:
		int21_createfile();
		return 1;
	case 0x35:
		es = GET_MEM_WORD(0x0,al * 4 + 2);
		bx = GET_MEM_WORD(0x0,al * 4    );
		return 1;
	case 0x25:
		/* TODO: my own interrupt vector
		 * handling, 0xFFFFFFFF - means built in
		 * all others are pointers to routines
		 */
		fprintf(report_file,"BHole: WARNING!!! interrupt vector re-routed for int %x\n",GET_AL());
		fprintf(report_file,"BHole: cs = %x,ip = %x\n",cs,ip);
		SET_MEM_WORD(0x0,al * 4 + 2,ds);
		SET_MEM_WORD(0x0,al * 4    ,dx);
		return 1;
	case 0x4A:
		/* seems to be some kind of a memory resize interrupt
		 * which we can safetly ignore since
		 * MoM is the only thing running
		 */
		return int21_realloc();
	case 0x47:
		/* TODO: currently we cheat the application to think it
		 * is always placed in the root directory
		 */
		SET_MEM_BYTE(ds,si,0);
		UNSET_CF();
		/* TODO: should i set to AX to 0x0100 */
		return 1;		
	case 0x4E:
		/* NOTICE: (TODO: solve this) this one assumes the findfirst
		 * call does not do a search out of the work path
		 * and also ignores CX and AL
		 * NOTICE: (TODO: solve this) it only does
		 * the stuff MoM checks for (mostly, checking for file
		 * existance i think...)
		 */
		int21_findfirst();
		return 1;
	case 0x2F:
		bx = dta_offset;
		es = dta_segment;
		return 1;
	case 0x3D:
		int21_fileopen();
		return 1;
	case 0x3E:
		int21_fileclose();
		return 1;
	case 0x3F:
		int21_fileread();
		return 1;
	case 0x40:
		int21_filewrite();
		return 1;
	case 0x42:
		int21_fileseek();
		return 1;
	case 0x43:
		int21_ah43();
		return 1;
	case 0x44: /* this is the ioctl interrupt, not the devinfo interrupt */
		if (al == 0)
			int21_devinfo();
		else if (al == 7)
		{
			if (bx == emm_dev_handle)
			{
				ax = 0x01FF;
				UNSET_CF();
			}
			else
			{
				fputs("BHole: \"unnice\" BX for int 21,44,7",report_file);
				return 0;
			}
		}
		else
		{
			fprintf(report_file,"BHole: unknown kind of ioctl (int 21,44,%x)\n",al);
			return 0;
		}
		return 1;
	case 0x48:
		int21_memalloc();
		return 1;
	default:
		fprintf(report_file,"BHole: unknown AH %x for int 21\n",GET_AH());
		return 0;
	}
}





/* do_inttime */
int do_inttime()
	/* pretends to be BIOS time interrupt */
{
	time_t t;
	switch(GET_AH())
	{
	case 0x0:
		time(&t); /* NOTICE:  TODO: this code is totally wrong */
		t = t % 86400;
		dx = t & 0xFFFF;
		cx = t >> 16;
		al = 0;
		return 1;
	default:
		fprintf(report_file,"BHole: unknown AH %x for int 1A\n",GET_AH());
		return 0;
	}
}




void init_opcodes()
{
/* THESE LINES ARE NO LONGER CORRECT
	/* some small explanation is appropriate,
	 * handlers ending with M2 are those whose second parameter
	 * is in fact a pure offset into memory
	 * handlers ending with M1 are those whose first  parameter
	 * is in fact a pure offset into memory
	 */

	/* legend: i will now have a new naming convention
	 *         more similar to the one used in some other places
	 * E (AOR) - a modrm byte after the instruction can be interpreted
	 *           as either an address or a register
	 * G       - a register pointed by the reg field of the modrm byte
	 * S       - a segment register point by the reg field of the modrm byte
	 * O       - after the instruction, instead of a modrm byte, comes
	 *           a direct offset (example: if it's 2, instruction is ??? ??,[2])
	 *                           (         or ??? [2],??)
	 * I       - immediate value, placed right after the instrcution
	 *
	 * AL      - the register AL
	 * ES      - the register ES
	 * .
	 * .
	 * .
	 *
	 * b       - byte (this one, and w are placed where the size not guarnteed)
	 * w       - word
	 */

	add_elem(AOR | BYTE,R   | BYTE,handle_addEbGb );/* 0x00 */
	add_elem(AOR | WORD,R   | WORD,handle_addEwGw );/* 0x01 */
	add_elem(R   | BYTE,AOR | BYTE,handle_addGbEb );/* 0x02 */
	add_elem(R   | WORD,AOR | WORD,handle_addGwEw );/* 0x03 */
	add_elem(RAL | BYTE,IMM | BYTE,handle_addALIb );/* 0x04 */
	add_elem(RAX | WORD,IMM | WORD,handle_addAXIw );/* 0x05 */
	add_elem(RES | WORD,NONE      ,handle_pushES  );/* 0x06 */
	add_elem(RES | WORD,NONE      ,handle_popES   );/* 0x07 */
	add_elem(AOR | BYTE,R   | BYTE,handle_orEbGb  );/* 0x08 */
	add_elem(AOR | WORD,R   | WORD,handle_orEwGw  );/* 0x09 */
	add_elem(R   | BYTE,AOR | BYTE,handle_orGbEb  );/* 0x0A */
	add_elem(R   | WORD,AOR | WORD,handle_orGwEw  );/* 0x0B */
	add_elem(RAL | BYTE,IMM | BYTE,handle_orALIb  );/* 0x0C */
	add_elem(RAX | WORD,IMM | WORD,handle_orAXIw  );/* 0x0D */
	add_elem(RCS | WORD,NONE      ,handle_pushCS  );/* 0x0E */
	add_elem(ESCAPE    ,ESCAPE    ,handle_escape  );/* 0x0F */

	add_elem(AOR | BYTE,R   | BYTE,handle_adcEbGb );/* 0x10 */
	add_elem(AOR | WORD,R   | WORD,handle_adcEwGw );/* 0x11 */
	add_elem(R   | BYTE,AOR | BYTE,handle_adcGbEb );/* 0x12 */
	add_elem(R   | WORD,AOR | WORD,handle_adcGwEw );/* 0x13 */
	add_elem(RAL | BYTE,IMM | BYTE,handle_adcALIb );/* 0x14 */
	add_elem(RAX | WORD,IMM | WORD,handle_adcAXIw );/* 0x15 */
	add_elem(RSS | WORD,NONE      ,handle_pushSS  );/* 0x16 */
	add_elem(RSS | WORD,NONE      ,handle_popSS   );/* 0x17 */
	add_elem(AOR | BYTE,R   | BYTE,handle_sbbEbGb );/* 0x18 */
	add_elem(AOR | WORD,R   | WORD,handle_sbbEwGw );/* 0x19 */
	add_elem(R   | BYTE,AOR | BYTE,handle_sbbGbEb );/* 0x1A */
	add_elem(R   | WORD,AOR | WORD,handle_sbbGwEw );/* 0x1B */
	add_elem(RAL | BYTE,IMM | BYTE,handle_sbbALIb );/* 0x1C */
	add_elem(RAX | WORD,IMM | WORD,handle_sbbAXIw );/* 0x1D */
	add_elem(RDS | WORD,NONE      ,handle_pushDS  );/* 0x1E */
	add_elem(RDS | WORD,NONE      ,handle_popDS   );/* 0x1F */

	add_elem(AOR | BYTE,R   | BYTE,handle_andEbGb );/* 0x20 */
	add_elem(AOR | WORD,R   | WORD,handle_andEwGw );/* 0x21 */
	add_elem(R   | BYTE,AOR | BYTE,handle_andGbEb );/* 0x22 */
	add_elem(R   | WORD,AOR | WORD,handle_andGwEw );/* 0x23 */
	add_elem(RAL | BYTE,IMM | BYTE,handle_andALIb );/* 0x24 */
	add_elem(RAX | WORD,IMM | WORD,handle_andAXIw );/* 0x25 */
	add_elem(NONE      ,NONE      ,handle_esovride);/* 0x26 */
	add_elem(NONE      ,NONE      ,handle_daa     );/* 0x27 */
	add_elem(AOR | BYTE,R   | BYTE,handle_subEbGb );/* 0x28 */
	add_elem(AOR | WORD,R   | WORD,handle_subEwGw );/* 0x29 */
	add_elem(R   | BYTE,AOR | BYTE,handle_subGbEb );/* 0x2A */
	add_elem(R   | WORD,AOR | WORD,handle_subGwEw );/* 0x2B */
	add_elem(RAL | BYTE,IMM | BYTE,handle_subALIb );/* 0x2C */
	add_elem(RAX | WORD,IMM | WORD,handle_subAXIw );/* 0x2D */
	add_elem(NONE      ,NONE      ,handle_csovride);/* 0x2E */
	add_elem(NONE      ,NONE      ,handle_das     );/* 0x2F */

	add_elem(AOR | BYTE,R   | BYTE,handle_xorEbGb );/* 0x30 */
	add_elem(AOR | WORD,R   | WORD,handle_xorEwGw );/* 0x31 */
	add_elem(R   | BYTE,AOR | BYTE,handle_xorGbEb );/* 0x32 */
	add_elem(R   | WORD,AOR | WORD,handle_xorGwEw );/* 0x33 */
	add_elem(RAL | BYTE,IMM | BYTE,handle_xorALIb );/* 0x34 */
	add_elem(RAX | WORD,IMM | WORD,handle_xorAXIw );/* 0x35 */
	add_elem(NONE      ,NONE      ,handle_ssovride);/* 0x36 */
	add_elem(NONE      ,NONE      ,handle_aaa     );/* 0x37 */
	add_elem(AOR | BYTE,R   | BYTE,handle_cmpEbGb );/* 0x38 */
	add_elem(AOR | WORD,R   | WORD,handle_cmpEwGw );/* 0x39 */
	add_elem(R   | BYTE,AOR | BYTE,handle_cmpGbEb );/* 0x3A */
	add_elem(R   | WORD,AOR | WORD,handle_cmpGwEw );/* 0x3B */
	add_elem(RAL | BYTE,IMM | BYTE,handle_cmpALIb );/* 0x3C */
	add_elem(RAX | WORD,IMM | WORD,handle_cmpAXIw );/* 0x3D */
	add_elem(NONE      ,NONE      ,handle_csovride);/* 0x3E */
	add_elem(NONE      ,NONE      ,handle_aas     );/* 0x3F */

	add_elem(RAX | WORD,NONE      ,handle_incAX   );/* 0x40 */
	add_elem(RCX | WORD,NONE      ,handle_incCX   );/* 0x41 */
	add_elem(RDX | WORD,NONE      ,handle_incDX   );/* 0x42 */
	add_elem(RBX | WORD,NONE      ,handle_incBX   );/* 0x43 */
	add_elem(RSP | WORD,NONE      ,handle_incSP   );/* 0x44 */
	add_elem(RBP | WORD,NONE      ,handle_incBP   );/* 0x45 */
	add_elem(RSI | WORD,NONE      ,handle_incSI   );/* 0x46 */
	add_elem(RDI | WORD,NONE      ,handle_incDI   );/* 0x47 */
	add_elem(RAX | WORD,NONE      ,handle_decAX   );/* 0x48 */
	add_elem(RCX | WORD,NONE      ,handle_decCX   );/* 0x49 */
	add_elem(RDX | WORD,NONE      ,handle_decDX   );/* 0x4A */
	add_elem(RBX | WORD,NONE      ,handle_decBX   );/* 0x4B */
	add_elem(RSP | WORD,NONE      ,handle_decSP   );/* 0x4C */
	add_elem(RBP | WORD,NONE      ,handle_decBP   );/* 0x4D */
	add_elem(RSI | WORD,NONE      ,handle_decSI   );/* 0x4E */
	add_elem(RDI | WORD,NONE      ,handle_decDI   );/* 0x4F */

	add_elem(RAX | WORD,NONE      ,handle_pushAX  );/* 0x50 */
	add_elem(RCX | WORD,NONE      ,handle_pushCX  );/* 0x51 */
	add_elem(RDX | WORD,NONE      ,handle_pushDX  );/* 0x52 */
	add_elem(RBX | WORD,NONE      ,handle_pushBX  );/* 0x53 */
	add_elem(RSP | WORD,NONE      ,handle_pushSP  );/* 0x54 */
	add_elem(RBP | WORD,NONE      ,handle_pushBP  );/* 0x55 */
	add_elem(RSI | WORD,NONE      ,handle_pushSI  );/* 0x56 */
	add_elem(RDI | WORD,NONE      ,handle_pushDI  );/* 0x57 */
	add_elem(RAX | WORD,NONE      ,handle_popAX   );/* 0x58 */
	add_elem(RCX | WORD,NONE      ,handle_popCX   );/* 0x59 */
	add_elem(RDX | WORD,NONE      ,handle_popDX   );/* 0x5A */
	add_elem(RBX | WORD,NONE      ,handle_popBX   );/* 0x5B */
	add_elem(RSP | WORD,NONE      ,handle_popSP   );/* 0x5C */
	add_elem(RBP | WORD,NONE      ,handle_popBP   );/* 0x5D */
	add_elem(RSI | WORD,NONE      ,handle_popSI   );/* 0x5E */
	add_elem(RDI | WORD,NONE      ,handle_popDI   );/* 0x5F */

	add_elem(NONE      ,NONE      ,handle_pusha   );/* 0x60 */
	add_elem(NONE      ,NONE      ,handle_popa    );/* 0x61 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x62 */ /* :) */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x63 */ /* :) */
	add_elem(NONE      ,NONE      ,handle_invalid );/* 0x64 */
	add_elem(NONE      ,NONE      ,handle_invalid );/* 0x65 */
	add_elem(NONE      ,NONE      ,handle_invalid );/* 0x66 */
	add_elem(NONE      ,NONE      ,handle_invalid );/* 0x67 */
	add_elem(IMM | WORD,NONE      ,handle_pushIw  );/* 0x68 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x69 */
	add_elem(IMM | WORD,NONE      ,handle_todo    );/* 0x6A */ /* should be pushIb - pushes a word, truncating it's higher byte */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x6B */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x6C */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x6D */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x6E */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x6F */

	add_elem(IMM | BYTE,NONE      ,handle_joIb    );/* 0x70 */
	add_elem(IMM | BYTE,NONE      ,handle_jnoIb   );/* 0x71 */
	add_elem(IMM | BYTE,NONE      ,handle_jbIb    );/* 0x72 */
	add_elem(IMM | BYTE,NONE      ,handle_jnbIb   );/* 0x73 */
	add_elem(IMM | BYTE,NONE      ,handle_jzIb    );/* 0x74 */
	add_elem(IMM | BYTE,NONE      ,handle_jnzIb   );/* 0x75 */
	add_elem(IMM | BYTE,NONE      ,handle_jbeIb   );/* 0x76 */
	add_elem(IMM | BYTE,NONE      ,handle_jnbeIb  );/* 0x77 */
	add_elem(IMM | BYTE,NONE      ,handle_jsIb    );/* 0x78 */
	add_elem(IMM | BYTE,NONE      ,handle_jnsIb   );/* 0x79 */
	add_elem(IMM | BYTE,NONE      ,handle_todo    );/* 0x7A */
	add_elem(IMM | BYTE,NONE      ,handle_todo    );/* 0x7B */
	add_elem(IMM | BYTE,NONE      ,handle_jlIb    );/* 0x7C */
	add_elem(IMM | BYTE,NONE      ,handle_jnlIb   );/* 0x7D */
	add_elem(IMM | BYTE,NONE      ,handle_jleIb   );/* 0x7E */
	add_elem(IMM | BYTE,NONE      ,handle_jnleIb  );/* 0x7F */

	add_elem(IMM | BYTE,NONE      ,handle_grp1EbIb);/* 0x80 */
	add_elem(IMM | BYTE,NONE      ,handle_grp1EwIw);/* 0x81 */
	add_elem(NONE      ,NONE      ,handle_grp1EbIb);/* 0x82 */ /* NOTE: wierd, 0x80 and 0x82 are the same */
	add_elem(IMM | BYTE,NONE      ,handle_grp1EwIb);/* 0x83 */ /* NOTE: the immediate is SIGNED */
	add_elem(AOR | BYTE,R   | BYTE,handle_testEbGb);/* 0x84 */
	add_elem(AOR | WORD,R   | WORD,handle_testEwGw);/* 0x85 */
	add_elem(AOR | BYTE,R   | BYTE,handle_xchgEbGb);/* 0x86 */
	add_elem(AOR | WORD,R   | WORD,handle_xchgEwGw);/* 0x87 */
	add_elem(AOR | BYTE,R   | BYTE,handle_movEbGb );/* 0x88 */
	add_elem(AOR | WORD,R   | WORD,handle_movEwGw );/* 0x89 */
	add_elem(R   | BYTE,AOR | BYTE,handle_movGbEb );/* 0x8A */
	add_elem(R   | WORD,AOR | WORD,handle_movGwEw );/* 0x8B */
	add_elem(AOR | WORD,SR  | WORD,handle_movEwSw );/* 0x8C */
	add_elem(NONE      ,NONE      ,handle_lea     );/* 0x8D */ /* lea handles parameters in handle_lea */
	add_elem(SR  | WORD,AOR | WORD,handle_movSwEw );/* 0x8E */
	add_elem(AOR | WORD,NONE      ,handle_popEw   );/* 0x8F */

	add_elem(NONE      ,NONE      ,handle_nop     );/* 0x90 */
	add_elem(RAX | WORD,RCX | WORD,handle_xchgAXCX);/* 0x91 */
	add_elem(RAX | WORD,RDX | WORD,handle_xchgAXDX);/* 0x92 */
	add_elem(RAX | WORD,RBX | WORD,handle_xchgAXBX);/* 0x93 */
	add_elem(RAX | WORD,RSP | WORD,handle_xchgAXSP);/* 0x94 */
	add_elem(RAX | WORD,RBP | WORD,handle_xchgAXBP);/* 0x95 */
	add_elem(RAX | WORD,RSI | WORD,handle_xchgAXSI);/* 0x96 */
	add_elem(RAX | WORD,RDI | WORD,handle_xchgAXDI);/* 0x97 */
	add_elem(NONE      ,NONE      ,handle_cbw     );/* 0x98 */
	add_elem(NONE      ,NONE      ,handle_cwd     );/* 0x99 */
	add_elem(NONE      ,NONE      ,handle_facall1 );/* 0x9A */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0x9B */
	add_elem(NONE      ,NONE      ,handle_pushf   );/* 0x9C */
	add_elem(NONE      ,NONE      ,handle_popf    );/* 0x9D */
	add_elem(NONE      ,NONE      ,handle_sahf    );/* 0x9E */
	add_elem(NONE      ,NONE      ,handle_lahf    );/* 0x9F */



	add_elem(RAL | BYTE,CA  | BYTE,handle_movALOb );/* 0xA0 */
	add_elem(RAX | WORD,CA  | WORD,handle_movAXOw );/* 0xA1 */
	add_elem(CA  | BYTE,RAX | BYTE,handle_movObAL );/* 0xA2 */
	add_elem(CA  | WORD,RAX | WORD,handle_movOwAX );/* 0xA3 */
	add_elem(NONE      ,NONE      ,handle_movsb   );/* 0xA4 */
	add_elem(NONE      ,NONE      ,handle_movsw   );/* 0xA5 */
	add_elem(NONE      ,NONE      ,handle_cmpsb   );/* 0xA6 */
	add_elem(NONE      ,NONE      ,handle_cmpsw   );/* 0xA7 */
	add_elem(RAL | BYTE,IMM | BYTE,handle_testALIb);/* 0xA8 */
	add_elem(RAX | WORD,IMM | WORD,handle_testAXIw);/* 0xA9 */
	add_elem(NONE      ,NONE      ,handle_stosb   );/* 0xAA */
	add_elem(NONE      ,NONE      ,handle_stosw   );/* 0xAB */
	add_elem(NONE      ,NONE      ,handle_lodsb   );/* 0xAC */
	add_elem(NONE      ,NONE      ,handle_lodsw   );/* 0xAD */
	add_elem(NONE      ,NONE      ,handle_scasb   );/* 0xAE */
	add_elem(NONE      ,NONE      ,handle_scasw   );/* 0xAF */

	add_elem(RAL | BYTE,IMM | BYTE,handle_movALIb );/* 0xB0 */
	add_elem(RCL | BYTE,IMM | BYTE,handle_movCLIb );/* 0xB1 */
	add_elem(RDL | BYTE,IMM | BYTE,handle_movDLIb );/* 0xB2 */
	add_elem(RBL | BYTE,IMM | BYTE,handle_movBLIb );/* 0xB3 */
	add_elem(RAH | BYTE,IMM | BYTE,handle_movAHIb );/* 0xB4 */
	add_elem(RCH | BYTE,IMM | BYTE,handle_movCHIb );/* 0xB5 */
	add_elem(RDH | BYTE,IMM | BYTE,handle_movDHIb );/* 0xB6 */
	add_elem(RBH | BYTE,IMM | BYTE,handle_movBHIb );/* 0xB7 */
	add_elem(RAX | WORD,IMM | WORD,handle_movAXIw );/* 0xB8 */
	add_elem(RCX | WORD,IMM | WORD,handle_movCXIw );/* 0xB9 */
	add_elem(RDX | WORD,IMM | WORD,handle_movDXIw );/* 0xBA */
	add_elem(RBX | WORD,IMM | WORD,handle_movBXIw );/* 0xBB */
	add_elem(RSP | WORD,IMM | WORD,handle_movSPIw );/* 0xBC */
	add_elem(RBP | WORD,IMM | WORD,handle_movBPIw );/* 0xBD */
	add_elem(RSI | WORD,IMM | WORD,handle_movSIIw );/* 0xBE */
	add_elem(RDI | WORD,IMM | WORD,handle_movDIIw );/* 0xBF */



	add_elem(NONE      ,NONE      ,handle_grp2EbIb);/* 0xC0 */
	add_elem(NONE      ,NONE      ,handle_grp2EwIb);/* 0xC1 */
	add_elem(IMM | WORD,NONE      ,handle_nret2Iw );/* 0xC2 */
	add_elem(NONE      ,NONE      ,handle_nret    );/* 0xC3 */
	add_elem(R   | WORD,AOR | WORD,handle_lesGwEw );/* 0xC4 */
	add_elem(R   | WORD,AOR | WORD,handle_ldsGwEw );/* 0xC5 */
	add_elem(AOR | BYTE,IMM | BYTE,handle_movEbIb );/* 0xC6 */
	add_elem(AOR | WORD,IMM | WORD,handle_movEwIw );/* 0xC7 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xC8 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xC9 */
	add_elem(IMM | WORD,NONE      ,handle_fret2Iw );/* 0xCA */
	add_elem(NONE      ,NONE      ,handle_fret    );/* 0xCB */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xCC */
	add_elem(IMM | BYTE,NONE      ,handle_interrupIb);/* 0xCD */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xCE */
	add_elem(NONE      ,NONE      ,handle_iret    );/* 0xCF */

	add_elem(IMM | BYTE,NONE      ,handle_grp2Eb1 );/* 0xD0 */
	add_elem(IMM | BYTE,NONE      ,handle_grp2Ew1 );/* 0xD1 */
	add_elem(IMM | BYTE,NONE      ,handle_grp2EbCL);/* 0xD2 */
	add_elem(IMM | BYTE,NONE      ,handle_grp2EwCL);/* 0xD3 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xD4 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xD5 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xD6 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xD7 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xD8 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xD9 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xDA */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xDB */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xDC */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xDD */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xDE */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xDF */

	add_elem(IMM | BYTE,NONE      ,handle_loopneIb);/* 0xE0 */
	add_elem(IMM | BYTE,NONE      ,handle_loopeIb );/* 0xE1 */
	add_elem(IMM | BYTE,NONE      ,handle_loopIb  );/* 0xE2 */
	add_elem(IMM | BYTE,NONE      ,handle_jcxzIb  );/* 0xE3 */
	add_elem(NONE      ,NONE      ,handle_inALIb  );/* 0xE4 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xE5 */
	add_elem(NONE      ,NONE      ,handle_outIbAL );/* 0xE6 */
	add_elem(NONE      ,NONE      ,handle_todo    );/* 0xE7 */
	add_elem(IMM | WORD,NONE      ,handle_nrcallIw);/* 0xE8 */
	add_elem(IMM | WORD,NONE      ,handle_rjmpIw  );/* 0xE9 */
	add_elem(NONE      ,NONE      ,handle_afjmp2  );/* 0xEA */
	add_elem(IMM | BYTE,NONE      ,handle_rjmpIb  );/* 0xEB */
	add_elem(RAL | BYTE,RDX | WORD,handle_inALDX  );/* 0xEC */
	add_elem(RAX | WORD,RDX | WORD,handle_todo    );/* 0xED */
	add_elem(RDX | WORD,RAL | BYTE,handle_outDXAL );/* 0xEE */
	add_elem(RDX | WORD,RAX | WORD,handle_todo    );/* 0xEF */

	add_elem(NONE      ,NONE      ,handle_storea  );/* 0xF0 */ /* BHole: BHole instructions - not real CPU opcodes */
	add_elem(NONE      ,NONE      ,handle_loada   );/* 0xF1 */ /* BHole: BHole instructions - not real CPU opcodes */
	add_elem(NONE      ,NONE      ,handle_repne   );/* 0xF2 */
	add_elem(NONE      ,NONE      ,handle_rep     );/* 0xF3 */
	add_elem(NONE      ,NONE      ,handle_bholeint);/* 0xF4 */
	add_elem(NONE      ,NONE      ,handle_cmc     );/* 0xF5 */
	add_elem(IMM | BYTE,NONE      ,handle_grp3Eb  );/* 0xF6 */
	add_elem(IMM | BYTE,NONE      ,handle_grp3Ew  );/* 0xF7 */
	add_elem(NONE      ,NONE      ,handle_clc     );/* 0xF8 */
	add_elem(NONE      ,NONE      ,handle_stc     );/* 0xF9 */
	add_elem(NONE      ,NONE      ,handle_cli     );/* 0xFA */
	add_elem(NONE      ,NONE      ,handle_sti     );/* 0xFB */
	add_elem(NONE      ,NONE      ,handle_cld     );/* 0xFC */
	add_elem(NONE      ,NONE      ,handle_std     );/* 0xFD */
	add_elem(IMM | BYTE,NONE      ,handle_grp4Eb  );/* 0xFE */ /* be very very careful HERE!!! */
	add_elem(IMM | BYTE,NONE      ,handle_grp5Ew  );/* 0xFF */


/*
/*	add_grp1_elem(AOR ,IMM ,handle_addM1 ); /* 0x00 */
/*	add_grp1_elem(AOR ,IMM ,handle_orM1  ); /* 0x01 */
/*	add_grp1_elem(AOR ,IMM ,handle_adcM1 ); /* 0x02 */
/*	add_grp1_elem(AOR ,IMM ,handle_sbbM1 ); /* 0x03 */
/*	add_grp1_elem(AOR ,IMM ,handle_andM1 ); /* 0x04 */
/*	add_grp1_elem(AOR ,IMM ,handle_subM1 ); /* 0x05 */
/*	add_grp1_elem(AOR ,IMM ,handle_xorM1 ); /* 0x06 */
/*	add_grp1_elem(AOR ,IMM ,handle_cmpM1 ); /* 0x07 */

	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_add)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_add)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_add)
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_or )
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_or )
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_or )
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_adc)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_adc)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_adc)
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_sbb)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_sbb)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_sbb)
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_and)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_and)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_and)
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_sub)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_sub)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_sub)
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_xor)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_xor)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_xor)
	ADD_GRP_OPCODE_HANDLER(grp1,EbIb,handle_cmp)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIw,handle_cmp)
	ADD_GRP_OPCODE_HANDLER(grp1,EwIb,handle_cmp)


	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_rol);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_rol);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_rol);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_rol);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_rol);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_rol);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_ror);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_ror);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_ror);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_ror);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_ror);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_ror);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_rcl);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_rcl);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_rcl);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_rcl);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_rcl);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_rcl);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_rcr); 
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_rcr);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_rcr);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_rcr);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_rcr);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_rcr);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_shl);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_shl);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_shl);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_shl);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_shl);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_shl);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_shr);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_shr);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_shr);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_shr);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_shr);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_shr);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp2,Eb1 ,handle_sar);
	ADD_GRP_OPCODE_HANDLER(grp2,Ew1 ,handle_sar);
	ADD_GRP_OPCODE_HANDLER(grp2,EbCL,handle_sar);
	ADD_GRP_OPCODE_HANDLER(grp2,EwCL,handle_sar);
	ADD_GRP_OPCODE_HANDLER(grp2,EbIb,handle_sar);
	ADD_GRP_OPCODE_HANDLER(grp2,EwIb,handle_sar);


	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_testIb); /* special case... */
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_testIw); /* special case... */
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_not    );
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_not    );
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_neg    );
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_neg    );
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_mul    );
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_mul    );
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_imul   );
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_imul   );
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_div    );
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_div    );
	ADD_GRP_OPCODE_HANDLER(grp3,Eb,handle_idiv   );
	ADD_GRP_OPCODE_HANDLER(grp3,Ew,handle_idiv   );


	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_inc    );
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_dec    );
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_invalid);
	ADD_GRP_OPCODE_HANDLER(grp4,Eb,handle_invalid);


	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_inc    );
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_dec    );
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_nacall );
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_facall2);
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_ajmp   );
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_afjmp  );
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_push   );
	ADD_GRP_OPCODE_HANDLER(grp5,Ew,handle_invalid);


}

#define HANDLE_MODRM_ADDR(offset,defseg)                           \
{                                                                  \
	unsigned short tmp = offset;                               \
	unsigned long ptr;                                         \
	if (override_seg == 0xFFFF)                                \
	{                                                          \
		ptr = (defseg<<4)+tmp;                             \
	}                                                          \
	else                                                       \
	{	                                                   \
		ptr = (override_seg<<4)+tmp;                       \
		override_seg = 0xFFFF;                             \
	}                                                          \
	return ptr;	                	                   \
}


unsigned long  calc_modrm_addrb(unsigned char modrm)
	/* routine: calc_modrm_addr
	 * purpose: calculate the address given by the mod and R/M
	 *          fields of the modrm byte (ignoring the REG field)
	 *          can also return a pointer to a register
	 *          in the case in which MOD(modrm) == 3
	 * params : modrm - the modrm byte
	 *
	 * notice: calc_modrm_addrw means word
	 *         calc_modrm_addrb means byte
	 *
	 * returns: the offset to the memory address
	 *          or a pointer to a register
	 *          BUGFIX: used to be a direct pointer to memory
	 */


{
	modrm &= ~(0x38);
	switch(modrm)
	{
	case 0x00: HANDLE_MODRM_ADDR(bx + si                      ,ds);
	case 0x01: HANDLE_MODRM_ADDR(bx + di                      ,ds);
	case 0x02: HANDLE_MODRM_ADDR(bp + si                      ,ss);
	case 0x03: HANDLE_MODRM_ADDR(bp + di                      ,ss);
	case 0x04: HANDLE_MODRM_ADDR(si                           ,ds);
	case 0x05: HANDLE_MODRM_ADDR(di                           ,ds);
	case 0x06: HANDLE_MODRM_ADDR((unsigned short)get_csip_word(),ds);
	case 0x07: HANDLE_MODRM_ADDR(bx                           ,ds);

	case 0x40: HANDLE_MODRM_ADDR(bx + si + (signed char)get_csip(),ds);
	case 0x41: HANDLE_MODRM_ADDR(bx + di + (signed char)get_csip(),ds);
	case 0x42: HANDLE_MODRM_ADDR(bp + si + (signed char)get_csip(),ss);
	case 0x43: HANDLE_MODRM_ADDR(bp + di + (signed char)get_csip(),ss);
	case 0x44: HANDLE_MODRM_ADDR(si      + (signed char)get_csip(),ds);
	case 0x45: HANDLE_MODRM_ADDR(di      + (signed char)get_csip(),ds);
	case 0x46: HANDLE_MODRM_ADDR(bp      + (signed char)get_csip(),ss);
	case 0x47: HANDLE_MODRM_ADDR(bx      + (signed char)get_csip(),ds);

	case 0x80: HANDLE_MODRM_ADDR(bx + si + (signed short)get_csip_word(),ds);
	case 0x81: HANDLE_MODRM_ADDR(bx + di + (signed short)get_csip_word(),ds);
	case 0x82: HANDLE_MODRM_ADDR(bp + si + (signed short)get_csip_word(),ss);
	case 0x83: HANDLE_MODRM_ADDR(bp + di + (signed short)get_csip_word(),ss);
	case 0x84: HANDLE_MODRM_ADDR(si      + (signed short)get_csip_word(),ds);
	case 0x85: HANDLE_MODRM_ADDR(di      + (signed short)get_csip_word(),ds);
	case 0x86: HANDLE_MODRM_ADDR(bp      + (signed short)get_csip_word(),ss);
	case 0x87: HANDLE_MODRM_ADDR(bx      + (signed short)get_csip_word(),ds);

	default  :
	           return (long)reglutbo[RM(modrm)];
	}
}

unsigned long  calc_modrm_addrw(unsigned char modrm)
	/* routine: calc_modrm_addr
	 * purpose: calculate the address given by the mod and R/M
	 *          fields of the modrm byte (ignoring the REG field)
	 *          can also return a pointer to a register
	 *          in the case in which MOD(modrm) == 3
	 * params : modrm - the modrm byte
	 *
	 * notice: calc_modrm_addrw means word
	 *         calc_modrm_addrb means byte
	 *
	 * returns: the offset to the memory address
	 *          or a pointer to a register
	 *          BUGFIX: used to be a direct pointer to memory
	 */

{
	modrm &= ~(0x38);
	switch(modrm)
	{
	case 0x00: HANDLE_MODRM_ADDR(bx + si                      ,ds);
	case 0x01: HANDLE_MODRM_ADDR(bx + di                      ,ds);
	case 0x02: HANDLE_MODRM_ADDR(bp + si                      ,ss);
	case 0x03: HANDLE_MODRM_ADDR(bp + di                      ,ss);
	case 0x04: HANDLE_MODRM_ADDR(si                           ,ds);
	case 0x05: HANDLE_MODRM_ADDR(di                           ,ds);
	case 0x06: HANDLE_MODRM_ADDR((unsigned short)get_csip_word(),ds);
	case 0x07: HANDLE_MODRM_ADDR(bx                           ,ds);

	case 0x40: HANDLE_MODRM_ADDR(bx + si + (signed char)get_csip(),ds);
	case 0x41: HANDLE_MODRM_ADDR(bx + di + (signed char)get_csip(),ds);
	case 0x42: HANDLE_MODRM_ADDR(bp + si + (signed char)get_csip(),ss);
	case 0x43: HANDLE_MODRM_ADDR(bp + di + (signed char)get_csip(),ss);
	case 0x44: HANDLE_MODRM_ADDR(si      + (signed char)get_csip(),ds);
	case 0x45: HANDLE_MODRM_ADDR(di      + (signed char)get_csip(),ds);
	case 0x46: HANDLE_MODRM_ADDR(bp      + (signed char)get_csip(),ss);
	case 0x47: HANDLE_MODRM_ADDR(bx      + (signed char)get_csip(),ds);

	case 0x80: HANDLE_MODRM_ADDR(bx + si + (signed short)get_csip_word(),ds);
	case 0x81: HANDLE_MODRM_ADDR(bx + di + (signed short)get_csip_word(),ds);
	case 0x82: HANDLE_MODRM_ADDR(bp + si + (signed short)get_csip_word(),ss);
	case 0x83: HANDLE_MODRM_ADDR(bp + di + (signed short)get_csip_word(),ss);
	case 0x84: HANDLE_MODRM_ADDR(si      + (signed short)get_csip_word(),ds);
	case 0x85: HANDLE_MODRM_ADDR(di      + (signed short)get_csip_word(),ds);
	case 0x86: HANDLE_MODRM_ADDR(bp      + (signed short)get_csip_word(),ss);
	case 0x87: HANDLE_MODRM_ADDR(bx      + (signed short)get_csip_word(),ds);

	default  :
	           return (RM(modrm) - REGSCOUNT) << 1;
	}
}


#undef HANDLE_MODRM_ADDR


/* TODO: improve calc_lea to replace (AND ~0x38) with a really big LUT */
short calc_lea_value(unsigned char modrm)
	/* routine: calc_lea_value
	 * purpose: calculate the address given by the mod and R/M
	 *          fields of the modrm byte (ignoring the REG field)
	 *          returns only the offset, without the segment
	 *
	 * params : modrm - the modrm byte
	 *          byte  - if byte == 1 the operand has byte-size
	 *                  if byte == 0 the operand has word-size
	 *                  (this only matters when MOD(modrm) == 3)
	 *
	 * returns: the offset WITHOUT the segment
	 */
{
	modrm&=~(0x38); /* get rid of the REG field */
	switch (modrm)
	{
	case 0x00: return            bx + si;
	case 0x01: return            bx + di;
	case 0x02: return            bp + si;
	case 0x03: return            bp + di;
	case 0x04: return            si;
	case 0x05: return            di;
	case 0x06: return            (signed short)get_csip_word();
	case 0x07: return            bx;

	case 0x40: return            bx + si + (signed char)get_csip();
	case 0x41: return            bx + di + (signed char)get_csip();
	case 0x42: return            bp + si + (signed char)get_csip();
	case 0x43: return            bp + di + (signed char)get_csip();
	case 0x44: return            si      + (signed char)get_csip();
	case 0x45: return            di      + (signed char)get_csip();
	case 0x46: return            bp      + (signed char)get_csip();
	case 0x47: return            bx      + (signed char)get_csip();

	case 0x80: return            bx + si + (signed short)get_csip_word();
	case 0x81: return            bx + di + (signed short)get_csip_word();
	case 0x82: return            bp + si + (signed short)get_csip_word();
	case 0x83: return            bp + di + (signed short)get_csip_word();
	case 0x84: return            si      + (signed short)get_csip_word();
	case 0x85: return            di      + (signed short)get_csip_word();
	case 0x86: return            bp      + (signed short)get_csip_word();
	case 0x87: return            bx      + (signed short)get_csip_word();

	default  : fprintf(report_file,"BHole: impossible value for modrm for LEA (%x)\n",modrm);
		   return 666;
	}
}

#undef HANDLE_MODRM_ADDR



int get_csip_dword()
	/* routine: get_csip()
	 * purpose: returns the dword pointed by CS:IP
	 */
{
	int ret = GET_MEM_WORD(cs,ip) + (GET_MEM_WORD(cs,ip+2) << 16);

	ip+=4;
	return ret;
}


/* goto_interrupt */
/* this routine goes to an interrupt using the interrupt vector,
 * it is used for IRQs and overlay's interrupt 0x3F
 */
void goto_interrupt(unsigned int csip)
{

	void do_push(int);
	allow_events = 0;  			
	do_push(flags);								
	do_push(cs);								
	do_push(ip);								
	ip = csip & 0xFFFF;							
	cs = csip >> 16;								
}

/* do_flags_byte */
void do_flags_byte(int c)
{

	if (c & 0x80) SET_SF();
	else          UNSET_SF();

	if (c == 0) SET_ZF();
	else        UNSET_ZF();
}

/* do_flags_word */
void do_flags_word(int c)
{

	if (c & 0x8000) SET_SF();
	else            UNSET_SF();

	if (c == 0) SET_ZF();
	else        UNSET_ZF();
}


/* do_byte_cmp   */
void do_byte_cmp(int a,int b)
{
	/* CF */
	if ((unsigned char)a < (unsigned char)b)        SET_CF();
	else
	{
		CLEAR_CF();
	}

	/* TODO: handle AF... */

	/* ZF */
	if ((signed char)a - (signed char)b == 0)   SET_ZF();
	else              CLEAR_ZF();

        /* TODO: handle PF... */

	/* SF */
	if (((char)a - (char)b) & 0x80) SET_SF();
	else                            CLEAR_SF();

	/* OF */ /* what !? */ /* TODO: what is this? */
	if ((GET_CF()                                                 ) ^
	    ((unsigned char)(a & ~0x80) < (unsigned char)(b & ~0x80)  ))
		SET_OF();
	else
		CLEAR_OF();

}

/* do_short_cmp   */
void do_word_cmp(int a,int b)
{
#ifndef HYPER_SPEED_MODE
	if (debug_vcpu)
		fprintf(report_file,"BHole: TODO: remove me: cmping %x and %x\n",a,b); 
#endif
	/* CF */
	if ((unsigned short)a < (unsigned short)b)        { SET_CF(); }
	else                                              {CLEAR_CF();}

	/* TODO: handle AF... */

	/* ZF */
	if ((short)a - (short)b == 0)   { SET_ZF(); }
	else                            {CLEAR_ZF();}

        /* TODO: handle PF... */

	/* SF */
	if (((short)a - (short)b) & 0x8000) { SET_SF(); }
	else                                {CLEAR_SF();}

	/* OF */ /* what !? */ /* TODO: what is this? */
	if ( ( GET_CF()                                         ) ^
	   ( (unsigned short)(a & ~0x8000) < (unsigned short)(b & ~0x8000) ))
		{ SET_OF();}
	else
		{CLEAR_OF();}

}



/* do_byte_test   */
void do_byte_test(unsigned char a,unsigned char b)
{
	/* TODO: all flags */
	unsigned char c;
	c = a & b;
	if (c & 0x80) SET_SF();
	else
	{
		UNSET_SF();
		if (c == 0) SET_ZF();
		else        UNSET_ZF();
	}
}

/* do_word_test   */
void do_word_test(unsigned short a,unsigned short b)
{
	/* TODO: all flags */
	unsigned short c;
	c = a & b;
	if (c & 0x8000) SET_SF();
	else
	{
		UNSET_SF();
		if (c == 0) SET_ZF();
		else        UNSET_ZF();
	}
}

/* do_push */
void do_push(int value)
{
	sp-=2; /* TODO: why doesn't sp - 2 and then sp-=2 work??? */
	SET_MEM_WORD(ss,sp,value);
#ifdef CHECK_STACK
	bstack_index-=2;
	*(short*)(bakstack + bstack_index) = value;

#endif
}

/* do_pop  */
unsigned short do_pop()
{
	sp+=2;
#ifdef CHECK_STACK
	bstack_index+=2;
#endif
	return GET_MEM_WORD(ss,(unsigned short)(sp - 2) );
}


#ifdef CHECK_STACK
void check_stack()
{
	int i;
	static unsigned short old_sp;

	if (old_sp != sp)
	{
		fprintf(report_file,"SP changed, old_sp = %x\n",old_sp);
		bstack_index = bstack_index + (sp - old_sp);
		old_sp = sp;
	}

	for(i = bstack_index ;i < 512;i++)
	{
		if (bakstack[i] != GET_MEM_BYTE(ss,sp + i - bstack_index))
		{
			fprintf(report_file,"BHole: check_stack found a failure (%x out of %x)\n",i-bstack_index,512 - bstack_index);
			fprintf(report_file,"bakstack[%x] == %x\n",i,bakstack[i]);
			fprintf(report_file,"ss:%x == %x\n",sp+i-bstack_index,GET_MEM_BYTE(ss,sp+i-bstack_index));
		}
	}

}
#endif



#ifdef CHECK_MEMPIECE
	unsigned short mpbak;
#endif



#ifdef ASSUME_VIDEO_PAGE_FLIPPING

unsigned char vga_lastread[4]; /* wierd... */

void set_vidmem(unsigned int doffset,unsigned char value)
{
	unsigned char *p;

	p = __vidmemory__ + ((doffset - 0xA0000) * 4);
	
	
	/* TODO: make this code work fully */
	if (vga_bitmask == 0xFF)
	{
#ifdef CHECK_VIDPLOT
		if (!vga_surface_dirtiness)
			puts("BHole: vidplot reported");
		vga_surface_dirtiness++;			
#endif
		if (vga_writeplanes & 1) *(p  ) = value;
		if (vga_writeplanes & 2) *(p+1) = value;
		if (vga_writeplanes & 4) *(p+2) = value;
		if (vga_writeplanes & 8) *(p+3) = value;
	} 
	else if (vga_bitmask == 0x0)
	{
		if (vga_writeplanes & 1) *(p  ) = vga_lastread[0];
		if (vga_writeplanes & 2) *(p+1) = vga_lastread[1];
		if (vga_writeplanes & 4) *(p+2) = vga_lastread[2];
		if (vga_writeplanes & 8) *(p+3) = vga_lastread[3];
	}
}


unsigned char get_vidmem(int doffset)
{
	int i;
	unsigned char *p;

	i = (doffset - 0xA0000) * 4;
	p = __vidmemory__ + i;
	vga_lastread[0] = *(p);
	vga_lastread[1] = *(p+1);
	vga_lastread[2] = *(p+2);
	vga_lastread[3] = *(p+3);
	
	return *(p + vga_readplane);
}
#endif

/* __inline */ unsigned char *_redirected_addr(unsigned int ofs)
	/* routine: redirected_addr
	 * purpose: redirects an address
	 * NOTICE : currently handles:
	 *          conventional memory <--> EMM memory mapping
	 * NOTICE: cases where the conversion is not linear 1:1, are not done by this routine
	 *         (example: video memory)
	 * returns: redirected pointer
	 * notice: used to accept a pointer, now accepts an offset from __memory__
	 */
{
	int i;
	int i2;
	/* check: are we redirecting to simple CGA low-res buffer
	 * (NOTICE: this does not go through set_vidmem)
	 */
	/* notice: most emm/ems code can be found in ems.c */
#ifdef ASSUME_CGA
	if (0xB8000 <= ofs && ofs <= 0xC0000) /* should this be less than 0xC0000 */
	{
		vga_surface_dirtiness = 1; /* used in here as well */
		return __memory__ + ofs;
	}
#endif

	/* check: are we talking about a pointer which points
	 *        to a physical page which is mapped to a logical
	 *        EMM page
	 */




/*	for(i = 0;i < MAX_EMM_MAPPINGS;i++)
	{
		if (((EMM_PAGEFRAME_BASESEG << 4) + EMM_LPAGE_SIZE * i) <= (ofs) &&
		    (ofs             ) < ((EMM_PAGEFRAME_BASESEG << 4) + EMM_LPAGE_SIZE * (i+1) ))
		{
			break;
		}
	} */
	i = (ofs - (EMM_PAGEFRAME_BASESEG << 4));



	/* check: have we actually found the pointer
	 *        to point somewhere in or after the EMM_PAGEFRAME_BASESEG
	 *        physical page that can be mapped
	 */
	if (i >= 0)
	{
		/* morph i to a physical page pointer */
		i2 = i % EMM_LPAGE_SIZE;
		i /= EMM_LPAGE_SIZE;


		/* if so, check if it is mapped, and if it's in range
		 * (not all of the memory after 0xD000 can be used for
		 *  mappable physical pages)
		 */
		if (i < MAX_EMM_MAPPINGS && emappings[i].handle != -1)
		{
			/* if it's mapped return the correct EMM
			 * address
			 */
			return ehandles[emappings[i].handle].data + emappings[i].lpage * EMM_LPAGE_SIZE + i2;
		}
	}
	return __memory__ + ofs;
}

#ifdef CHECK_SPEEDS
void report_speeds()
	/* routine: report_speeds()
	 * purpose: reports information about BHole's speed
	 *          this routine reports the following
	 *          (i'm so stupid i probably forgot to describe something)
	 *          - BHole's average speed in the time between this 
	 *             event-period and last one
	 *          - BHole's average speed from the beginning of it's running
	 * notice: since i'm too lazy, the first reading of this is in fact
	 *         totally bogus :-)
	 * notice2: speeds are not accurate since i use time() and not
	 *          gettimeofday
	 * TODO: use gettimeofday =8-)
	 */
{
	static time_t lasttime;
	
	time_t tt1;
	tt1 = time(NULL);
	puts("BHole: speed statistics (in MIPS)");
	printf("BHole: period's average speed: %f\n",
	       (double)((CHECKEVENTS_LINEMASK+1) * CHECK_SPEEDS_EVERY)  / (tt1 - lasttime ) / 1000000);
	printf("BHole: total    average speed: %f\n",
	       (double)pseudoclock / (tt1 - starttime) / 1000000);
	lasttime = tt1;
}
#endif

/* run_code */
void run_code()
	/* routine: run_code
	 * purpose: starts the virtual CPU
	 * NOTICE : this emulator does the conversion of the void *
	 *          in the opcode look up table back to a
	 *          function pointer,
	 *          i just wrote this code hoping it would work
	 *          and apparently, it did,
	 *          hope it will continue like that :)
	 */
{

	unsigned char opc;
	
/*	MSG msg;
	RECT vidwin_rect; */
#ifdef CHECK_SPEEDS
	unsigned char cspd_cnt = 1;
#endif

#ifdef CHECK_OPCODE_FIRSTTIME
	unsigned char opcodecnt[256];
#endif

#ifdef DEBUG_CODESTREAM
	FILE *codstrim;
	unsigned long __codstrim_hidden_tmp;
	unsigned long __codstrim_cnt = 0;
#endif

#ifdef CHECK_OPCODELUT
	opcode bol[256];
	memcpy(bol,opcodes,sizeof(opcode) * 256);
#endif
#ifdef CHECK_MEMPIECE
	mpbak = GET_MEM_WORD2(MEMPIECE);
#endif
	fputs("BHole: running code...\n",report_file);

	pseudoclock = 0;

#ifdef DEBUG_CODESTREAM
	codstrim = fopen("codstrim","wb");
	if (codstrim == NULL)
	{
		fputs("BHole: cannot create file codstrim\n",report_file);
		fputs("BHole: code stream debugging will be unavailble\n",report_file);
		codstrim = fopen("nul","wb");
	}
#endif
	while(1)
	{


#ifdef CHECK_STACK
		check_stack();
#endif
#ifdef CHECK_OPCODELUT
		fputs("BHole: checking opcode LUT...\n",report_file);
		if (memcmp(bol,opcodes,sizeof(opcode) * 256))
		{
			fputs("BHole: opcode LUT self-check FAILED!!!\n",report_file);
			break;
		}

#endif
#ifdef CHECK_MEMPIECE
		if (GET_MEM_WORD2(MEMPIECE) != mpbak)
		{
			fputs("BHole: memory selected piece self-check FAILED!!!\n",report_file);
			fprintf(report_file,"BHole: mempiece contains %x, should be %x\n",
			       GET_MEM_WORD2(MEMPIECE),
			       mpbak);
			fprintf(report_file,"BHole: CS:IP is %x:%x and line is %x\n",cs,ip,line);
			mpbak = GET_MEM_WORD2(MEMPIECE);
		}
		else
		{
#ifndef HYPER_SPEED_MODE
			if (debug_vcpu) fprintf(report_file,"BHole: memory piece contains %x\n",GET_MEM_WORD2(MEMPIECE));
#endif	
		}
#endif

#ifdef DEBUG_CODESTREAM

			__codstrim_hidden_tmp = (cs << 4) + ip;
			if (fwrite(&__codstrim_hidden_tmp,4,1,codstrim) < 1)
			{
				puts("BHole: ALERT!!!: failed writing to codstrim");
			}
			__codstrim_cnt+=4;
			if (__codstrim_cnt == 1048576)
			{
				puts("BHole: code stream is 1 Meg, quitting...");
				return;
			}
#endif

#ifdef CHECK_FLAG_USERS
		last_opcode = opc;
#endif

#ifndef HYPER_SPEED_MODE
		opc = get_csip();

#ifdef CHECK_USED_OPCODES
		used_opcodes[opc]|=1;	
#endif

#ifdef CHECK_OPCODE_FIRSTTIME
		if (opcodecnt[opc] == 0)
		{
			fprintf(report_file,"BHole: first time the %x opcode occurs (cs:ip %x:%x) line %x\n",opc,cs,ip,line);
			opcodecnt[opc]++;
		}
#endif
#ifndef HYPER_SPEED_MODE
		if (debug_vcpu)
		{
			fprintf(report_file,"%x %x:%x opcode: %x\n",line,cs,ip-1,opc);
#ifndef MINIDEBUG
			fprintf(report_file,
			       "AX = %x\t BX = %x\t CX = %x\t DX = %x\t"
			       "BP = %x\t SP = %x\t SI = %x\t DI = %x\t"
			       "CS = %x\t DS = %x\t ES = %x\t SS = %x\n",
			       ax,bx,cx,dx,
			       bp,sp,si,di,
			       cs,ds,es,ss);

#endif
		}
#endif /* HYPER_SPEED_MODE */
/*		memcpy(&h,&opcodes[opc].handler,4); - no longer used */

/*              BUGFIX: THESE LINES ARE NO LONGER NEEDED
 *              MEMORY IS REDIRECTED INSIDE (G/S)ET_MEM_???? macros
 *
 *              also, now, param1 will contain the offset inside memory
 *              and not a pointer to that place in memory
 *              (explanation: (memory + offset) --> offset)
 *
 *		param1 = redirected_addr(param1); SHOULD THIS REALLY BE HERE!!!
 *		param2 = redirected_addr(param2); I THINK NOT...
 *
 */

/* 		if ((==0) CODE CANCELLED IN ORDER TO GET SPEED IMPROVMENT
		{
			fprintf(report_file,"BHole: failed to handle opcode %x\n",opc);
			fprintf(report_file,"line = %x CS = %x IP = %x\n",line,cs,ip);
			fprintf(report_file,"AX = %x\t BX = %x\t CX = %x\t DX = %x\t"
			       "BP = %x\t SP = %x\t SI = %x\t DI = %x\t"
			       "CS = %x\t DS = %x\t ES = %x\t SS = %x\n",
			       ax,bx,cx,dx,
			       bp,sp,si,di,
			       cs,ds,es,ss);
			break;
		} */
#else /* end of #ifndef HYPER_SPEED_MODE */
		opc = get_csip();
		((ochandler*)opcodes[opc].handler)();
		opc = get_csip();
		((ochandler*)opcodes[opc].handler)();
		opc = get_csip();
		((ochandler*)opcodes[opc].handler)();
		opc = get_csip();
		((ochandler*)opcodes[opc].handler)();
#endif
#ifndef HYPER_SPEED_MODE
		pseudoclock++;
#else
		pseudoclock+=4;
#endif		
		if (!(line & CHECKEVENTS_LINEMASK))
		{
#ifndef ASSUME_VIDEO_PAGE_FLIPPING
			refresh_screen(); /* note: refresh_screen also does auto-slowdown for some games */
#endif
			handle_events();
			if (ect != 0) do_slowdown(); 
#ifdef CHECK_SPEEDS
			if (cspd_cnt == CHECK_SPEEDS_EVERY)
			{
				report_speeds();
				cspd_cnt = 1;
			}
			else cspd_cnt++;
#endif
		}
	}






}

__inline void fake_psp()
	/* routine: fake_psp
	 * purpose: fakes a psp (duh)
	 *          (which are the 256 bytes DOS puts before a program)
	 * notice: does very little (for now???)
	 */
{
	SET_MEM_WORD2(PSPPOS + 0x2,0xA000);
	SET_MEM_WORD2(PSPPOS + 0x2C,ENVPOS>>4);

	fprintf(report_file,"BHole: pspdbg: %x\n",GET_MEM_WORD2(PSPPOS+0x2));
	fprintf(report_file,"BHole: pspdbg: %x\n",GET_MEM_WORD2(PSPPOS+0x2C));


	/* BUGFIX: this text was deleted
	 **(unsigned short*)(PSP + 0x2 ) = 0xA000;
	 **(unsigned short*)(PSP + 0x2C) = ENVPOS >> 4;
	 */


}

__inline void init_env()
	/* routine: init_env
	 * purpose: inits env (environment)
	 */
{
	int t;
	mem2emudmem(ENVPOS,"winbootdir=C:\\WINDOWS" "\0"
                   "windir=C:\\WINDOWS" "\0"
                   "PATH=" "\0"
                   "CMDLINE=magic.exe" "\0" "\0" "\x01", /* the last '\0' is auto-added */
                   t=sizeof(
                   "winbootdir=C:\\WINDOWS" "\0"
                   "windir=C:\\WINDOWS" "\0"
                   "PATH=" "\0"
                   "CMDLINE=magic.exe" "\0" "\0" "\x01"));

	mem2emudmem(ENVPOS+t,EXECNAME,strlen(EXECNAME)+1);
	dta_offset = 0x80;
	dta_segment = PSPSEG;
}

__inline void init_fhandles()
	/* this routine prepars some of the
	 * file handles which are considered to be "pre-loaded"
	 * funny thing, it seems DOS like UNIX also has stdin
	 * and stdout in some sort of a way,
	 * i never tought it was so,
	 * well, you learn something new everyday :)
	 */
{
	fhandles[2] = stdout;
}



/* macro  : make_int_chainable 
 * purpose: makes an interrupt routine
 *          chainable to the program
 * notice : current version assumes interrupt is
 *          nothing but IRET
 */
#define MAKE_INT_CHAINABLE(i) \
SET_MEM_WORD2( i << 2   ,IRET_IP); \
SET_MEM_WORD2((i << 2)+2,IRET_CS); \


__inline void init_intvec()
	/* routine: init_intvec
	 * purpose: initializes the interrupt vector
	 *          the interrupt vector is initialized
	 *          so that all interrupts point to their
	 *          place in fake ROM's F000:E000-F000:E200
	 *          for more information, check out the fake ROM
	 *          section. afterwards, interrupts which 
	 *          do nothing are set to chainable
	 *          by the macro above
	 */
{
	int i;
	for(i = 0;i < 0x400;i+=4)
	{
		SET_MEM_BYTE2(i,(i >> 1) & 0xFF/* (i / 4 * 2) */);
		SET_MEM_BYTE2(i+1, (INT_IP >> 8) + (((i >> 1) & 0xFF00) >> 8));
		SET_MEM_BYTE2(i+2, INT_CS & 0xFF      );
		SET_MEM_BYTE2(i+3, INT_CS >> 8        );
	}
	
	MAKE_INT_CHAINABLE(0x0);
	MAKE_INT_CHAINABLE(0x08); /* timer(?) IRQ */
	MAKE_INT_CHAINABLE(0x09); /* keyboard IRQ */
	MAKE_INT_CHAINABLE(0x1C); /* timer(?) IRQ */
	MAKE_INT_CHAINABLE(0x60); /* why!? - some game i emulate needs this */
}

#undef MAKE_INT_CHAINABLE

__inline void init_memory()
{
	int i;
	for(i = 0;i < 0xA0000;i++)
	{
		__memory__[i] = 0;
	}
	
	__memory__[0x410] = 0x27;
	__memory__[0x411] = 0xC4; /* fake BIOS hardware status word */
	
}


__inline void init_emm()
{
	int i;
	for(i = 0;i < MAX_EMM_MAPPINGS;i++)
	{
		emappings[i].handle = -1;
	}
}

__inline void init_reglutb()
{
	int i;
	for(i = 0;i < 8;i++)
	{
		reglutb[i] = (unsigned char*)regs + reglutbo[i] + REGSSIZE;
	}
}






void handle_assumptions(char *filename)
{
	int i = 0;
	int j = 0;
	/* TODO: more intelligent comparing method
	 *       maybe a tree-like one, where each letter
	 *       can be used in a tree to filter the rest
	 */
	while(filename[j] != '\0') j++;


	for(i = 0;i < j;i++) filename[i] = tolower(filename[i]);

	if      (!strcmp(filename + j - 4,".com"))
	{
		puts("BHole: loading .com file");
		assume_com_file = 1;
	}
	else if (!strcmp(filename + j - 4,".exe"))
	{
		puts("BHole: loading .exe file");
	}
	else if (!strcmp(filename + j - 4,".img"))
	{
		puts("BHole: loading bootable image");
		assume_img_file = 1;
	}
	else
	{
		puts("BHole: unknown file extension, assuming EXE");
	}

	for(i = 0;i < EXECUTABLES_KNOWN_CNT && strcmp(filename,executables_known[i].exename) ;i++);
	
	if (i == EXECUTABLES_KNOWN_CNT)
	{
		puts("BHole: unknown filename, no assumptions are made");
		return;
	}
	
/* ASSUME_VIDEO_PAGE_FLIPPING - even page flipping games can run
 * without it
 */
#ifdef ASSUME_VIDEO_PAGE_FLIPPING
	if (!executables_known[i].video_page_flipping)
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#else
	if (executables_known[i].video_page_flipping)
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#endif

#ifndef ASSUME_CGA
	if (executables_known[i].cga_low_res_mode   )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#else
	if (!executables_known[i].cga_low_res_mode   )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#endif

#ifndef ASSUME_INT1C_AS_IRQ    
	if (executables_known[i].int1c_as_irq       )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#else
	if (!executables_known[i].int1c_as_irq      )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#endif

#ifndef ASSUME_INT9_AS_IRQ    
	if (executables_known[i].int9_as_irq       )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#else
	if (!executables_known[i].int9_as_irq      )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#endif


#ifndef ASSUME_DIRECT_MODE3    
	if (executables_known[i].direct_mode3       )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#else
	if (!executables_known[i].direct_mode3      )
	{
		puts("BHole: bad ASSUME #defines in source file");
		return;
	}
#endif

	printf("BHole: you are running: %s\n",executables_known[i].description);
	
	
}

#ifdef WIN32
int APIENTRY WinMain(HINSTANCE _appH,HINSTANCE _prevappH,LPSTR _lpCmdLine,int _nCmdShow)
#else
int main(int argc,char **argv)
#endif
{
	double maxmips;
	FILE *f;
	time_t endtime  ;

	report_file = fopen("report","w");


#ifdef WIN32
	f = fopen(_lpCmdLine,"rb");
#else
	f = fopen(argv[1],"rb");
#endif

	

	if (f == NULL)
	{
		fputs("BHole cannot open exe\n",report_file);
		return -1;
	}
	else
	{
		fputs("BHole: exe opened\n",report_file);
	}

#ifdef WIN32
	execname = _lpCmdLine;
#else
	execname = argv[1];
#endif
	/* initialize synchronization stuff */
	puts("Please enter maximal speed in MIPS, 0 means no limit");
	scanf("%lf",&maxmips);
	if (maxmips > 0) init_slowdown(maxmips);
	else             ect = 0;
	
	/* initialize memory to all zeroes */
	fputs("BHole: initializing memory...\n",report_file);

	init_emm();
	init_memory();

	fputs("BHole: memory initialized...\n",report_file);

	/* initialize opcode table */
	init_opcodes();

	fputs("BHole: opcode table initialized...\n",report_file);


#ifdef WIN32
	handle_assumptions(_lpCmdLine);
#else
	handle_assumptions(argv[1]);
#endif
	if (!assume_img_file)
	{
		init_mcbs();
	}
	if (!load_file(f)) return -1;

	if (!assume_img_file)
	{
		fclose(f);
		fake_psp();
		init_env();
		init_fhandles();
	}
	else
	{
		execfile = f;
	}
	SDL_Init(SDL_INIT_VIDEO);
	init_intvec();
	init_reglutb();
	init_rom();
	init_pcspkr();


	
#ifdef ASSUME_DIRECT_MODE3
	
	puts("BHole: NOTICE: application text is not in application window");
	puts("BHole: NOTICE: but keyboard and stuff should be done in app window");
	puts("BHole: NOTICE: press a key and then enter to continue");
	while(getchar() == '\n');

#endif

#ifdef ASSUME_CGA
	init_cga();
#endif

	starttime = time(NULL);

	/* begin code */
	run_code();

#ifdef ASSUME_CGA
	uninit_cga();
#endif
	SDL_Quit();
		
	endtime = time(NULL);

	printf("BHole: total uptime was %x\n",endtime - starttime);
	printf("BHole: MIPS: %f\n",(double)line / (endtime - starttime) / 1000000);

	return 0;


}
