/* *****************************************************************************
 * fimp.dll v1.0.0.0 - file implode / explode dynamic linked library (dll)
 * impExplode by Stuart Caie
 * impImplode by JOTD
 * fimp.dll   by Peace^Testaware - this software is in the Public Domain
 * *****************************************************************************
 *
 * Website: http://www.testaware.de.tp
 *
 * Version history
 * 1.0.0.0   01-Dec-2008 : by Peace^Testaware
 *                       - first release
 *                       - sources adapted to Dev-C++ 4.9.9.2
 *                       - sources assembled as dllmain.c
 *                       - added impGetExplodeSize()
 *                       - changed impImplodeBuffer() <-> impImplode()
 *                       - changed params of impExplode()
 *                       - changed ERR_FIMP_<CODE> cause haven't include error.h
 *                       - included example source for PureBasic
 * ****************************************************************************/

#include "dll.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "m68k_opcodes.h"        /* ported by JOTD */
#include "m68k_opcodes.c"        /* ported by JOTD */

#define  ERR_FIMP_NONE           (0)
#define  ERR_FIMP_TOOSHORT       (-1)
#define  ERR_FIMP_UNKNOWNFORMAT  (-2)
#define  ERR_FIMP_COMPNOTEVEN    (-3)
#define  ERR_FIMP_COMPTOOSHORT   (-4)
#define  ERR_FIMP_NOTENOUGHDATA  (-5)
#define  ERR_FIMP_OUTPUTLTINPUT  (-6)
#define  ERR_FIMP_CANNOTALLOCMEM (-7)
#define  ERR_FIMP_CANNOTDEPLODE  (-8)

#define  ID_IMP          (0x494D5021)
#define  ID_ATN          (0x41544E21)

#define SwapLong(ptr)    ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3])
#define REVERSE_INT(val) (((val >> 24) & 0xFF) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | ((val << 24) & 0xFF000000))


/* *****************************************************************************
 * SIZE = impGetExplodeSize(*buffer)
 * *****************************************************************************
 * calculates the exploded size of an imploded buffer
 * -----------------------------------------------------------------------------
 * APTR  *buffer = buffer of FImp imploded data
 * -----------------------------------------------------------------------------
 * SIZE = size of exploded buffer, else #ERR_FIMP_<CODE>
 * ****************************************************************************/
DLLIMPORT int impGetExplodeSize(unsigned char *buffer)
{
          int uncomp_len = 0;
          unsigned int id;

          id = SwapLong(buffer);

          // Check for magic ID: IMP!, EDAM, CHFI, PARA, RDC9, FLT!, Dupa or ATN!
	      if (id != 0x494d5021 && id != 0x4544414d && id != 0x43484649 &&
		      id != 0x50415241 && id != 0x52444339 && id != 0x464c5421 &&
		      id != 0x44757061 && id != 0x41544e21) {
			     return ERR_FIMP_UNKNOWNFORMAT;
             }
             else {
                 buffer = (buffer + 4);
                 uncomp_len = SwapLong(buffer);
                 }

          return uncomp_len;
}

/* *****************************************************************************
 * SIZE = impExplode(*in, in_len, *out, out_len)
 * *****************************************************************************
 * explode an FImp imploded buffer
 * -----------------------------------------------------------------------------
 * APTR  *in     = buffer of imploded data
 * ULONG in_len  = size in bytes of *in
 * APTR  *out    = buffer to store exploded datas
 * ULONG out_len = size of exploded buffer (=impGetExplodeSize())
 * -----------------------------------------------------------------------------
 * SIZE = size of decrunched buffer, else #PPERR_<CODE>
 * ****************************************************************************/
DLLIMPORT int impExplode(unsigned char *in, unsigned int in_len,
              unsigned char *out, unsigned int out_len)
{
	unsigned int id, end_off, ok;

	if (in_len < 0x30)
		return ERR_FIMP_TOOSHORT;

	id       = (in[0x00] << 24) | (in[0x01] << 16) | (in[0x02] << 8) | in[0x03];
	out_len  = (in[0x04] << 24) | (in[0x05] << 16) | (in[0x06] << 8) | in[0x07];
	end_off  = (in[0x08] << 24) | (in[0x09] << 16) | (in[0x0A] << 8) | in[0x0B];

	// Check for magic ID: IMP!, EDAM, CHFI, PARA, RDC9, FLT!, Dupa or ATN!
	if (id != 0x494d5021 && id != 0x4544414d && id != 0x43484649 &&
		id != 0x50415241 && id != 0x52444339 && id != 0x464c5421 &&
		id != 0x44757061 && id != 0x41544e21)
			return ERR_FIMP_UNKNOWNFORMAT;

	// Sanity checks
	if (end_off & 1) return ERR_FIMP_COMPNOTEVEN;
	if (end_off < 14) return ERR_FIMP_COMPTOOSHORT;
	if ((end_off + 0x2E) > in_len) return ERR_FIMP_NOTENOUGHDATA;
	if ((end_off + 0x26) > out_len) return ERR_FIMP_OUTPUTLTINPUT;

	// Copy input data into output buffer
	memcpy(&out[0x00],    &in[end_off + 0x08], 4);
	memcpy(&out[0x04],    &in[end_off + 0x04], 4);
	memcpy(&out[0x08],    &in[end_off + 0x00], 4);
	memcpy(&out[0x0C],    &in[0x0C], end_off - 0x0C);
	memcpy(&out[end_off], &in[end_off + 0x0C], 4);

	if (in[end_off + 0x10] & 0x80)
	{
		out[end_off + 4] = in[end_off + 0x11];
		ok = impExplodeBuffer(out, &in[end_off + 0x12], end_off + 5, out_len);
	}
	else
	{
		out[end_off - 1] = in[end_off + 0x11];
		ok = impExplodeBuffer(out, &in[end_off + 0x12], end_off + 4, out_len);
	}

	return ok ? ERR_FIMP_NONE : ERR_FIMP_CANNOTDEPLODE;
}

/* macro which obtains the next bit from the input bitstream, from MSB to
 * LSB, and sets the "bit" variable with the result. If 8 bits have been
 * read, fetches another byte from the input bytestream. Equivalent to the
 * following M680x0 code:
 *
 *     add.b   d3,d3
 *     bne.b   gotbit
 *     move.b  -(a3),d3
 *     addx.b  d3,d3
 * gotbit:
 */
#define EXPLODE_GETBIT do {			   \
	bit = bit_buffer & 0x80;           \
	bit_buffer <<= 1;                  \
	if (!bit_buffer)                   \
	{                                  \
		bit2 = bit;                    \
		bit_buffer = *--i;             \
		bit = bit_buffer & 0x80;       \
		bit_buffer <<= 1;              \
		if (bit2)                      \
			bit_buffer++;              \
	}                                  \
} while (0)
 
static unsigned char explode_literal_base[4] =
{
	6, 10, 10, 18
};
 
static unsigned char explode_literal_extrabits[12] =
{
	1, 1, 1, 1,
	2, 3, 3, 4,
	4, 5, 7, 14
};

/**
 * Decompresses a stream of Imploder-compressed data.
 *
 * @param buffer      a buffer that is large enough to contain all the
 *                    decompressed data. On entry, the buffer should
 *                    contain the entire Imploder-compressed stream at
 *                    offset 0. On successful exit, the buffer will
 *                    contain the decompressed data at offset 0. The
 *                    original buffer contents will be overwritten.
 *               
 * @param table       an explosion table, consisting of 8 16-bit
 *                    big-endian "base offset" values and 12 8-bit
 *                    "extra bits" values.
 * @param comp_len    the compressed length of the data
 * @param uncomp_len  the decompressed length of the data
 *
 * @return zero on error, non-zero on success. If successful, the
 * buffer contains the decompressed data.
 */
int impExplodeBuffer(unsigned char *buffer, unsigned char *table, unsigned int comp_len, unsigned int uncomp_len)
{
	unsigned char *i  = buffer + comp_len - 5; /* input pointer  */
	unsigned char *o  = buffer + uncomp_len;   /* output pointer */
	unsigned char *match;                      /* match pointer  */
	unsigned char bit_buffer, bit, bit2;
	unsigned int literal_len, match_len, selector, x, y;
	unsigned int match_base[8];

	// Read the 'base' part of the explosion table into native byte order, for speed
	for (x = 0; x < 8; x++)
		match_base[x] = (table[x*2] << 8) | table[x*2 + 1];

	// Get initial bit buffer contents, and first literal length
	if (comp_len & 1)
	{
		bit_buffer = i[4];
		literal_len = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];
	}
	else
	{
		bit_buffer = i[0];
		literal_len = (i[1] << 24) | (i[2] << 16) | (i[3] << 8) | i[4];
	}

	// Copy literal run
	while (1)
	{
		// Enough space?
		if ((o - buffer) < literal_len)
			return 0;
		
		while (literal_len--) *--o = *--i;

		// Main exit point - after the literal copy
		if (o <= buffer) break;

		/* static Huffman encoding of the match length and selector: 
		* 
		* 0     -> selector = 0, match_len = 1
		* 10    -> selector = 1, match_len = 2
		* 110   -> selector = 2, match_len = 3
		* 1110  -> selector = 3, match_len = 4
		* 11110 -> selector = 3, match_len = 5 + next three bits (5-12)
		* 11111 -> selector = 3, match_len = (next input byte)-1 (0-254)
		* 
		*/
		EXPLODE_GETBIT;
		if (bit)
		{
			EXPLODE_GETBIT;
			if (bit)
			{
				EXPLODE_GETBIT;
				if (bit)
				{
					selector = 3;
					EXPLODE_GETBIT;
					if (bit)
					{
						EXPLODE_GETBIT;
						if (bit)
						{
							match_len = *--i;
							// Bad input
							if (match_len == 0) return 0;
							match_len--;
						}
						else
						{
							match_len = 0;   EXPLODE_GETBIT; if (bit) match_len++;
							match_len <<= 1; EXPLODE_GETBIT; if (bit) match_len++;
							match_len <<= 1; EXPLODE_GETBIT; if (bit) match_len++;
							match_len += 5;
						}
					}
					else
						match_len = 4;
				}
				else
				{
					selector = 2;
					match_len = 3;
				}
			}
			else
			{
				selector = 1;
				match_len = 2;
			}
		}
		else
		{
			selector = 0;
			match_len = 1;
		}

		/* Another Huffman tuple, for deciding the base value (y) and number
		* of extra bits required from the input stream (x) to create the
		* length of the next literal run. Selector is 0-3, as previously
		* obtained.
		*
		* 0  -> base = 0,                      extra = {1,1,1,1}[selector]
		* 10 -> base = 2,                      extra = {2,3,3,4}[selector]
		* 11 -> base = {6,10,10,18}[selector]  extra = {4,5,7,14}[selector]
		*/
		y = 0;
		x = selector;
		EXPLODE_GETBIT;
		if (bit)
		{
			EXPLODE_GETBIT;
			if (bit)
			{
				y = explode_literal_base[x];
				x += 8;
			}
			else
			{
				y = 2;
				x += 4;
			}
		}
		x = explode_literal_extrabits[x];

		// Next literal run length: read [x] bits and add [y] */
		literal_len = 0;
		while (x--)
		{
			literal_len <<= 1;
			EXPLODE_GETBIT;
			if (bit)
				literal_len++;
		}
		literal_len += y;

		/* Another Huffman tuple, for deciding the match distance: _base and
		* _extra are from the explosion table, as passed into the explode
		* function.
		*
		* 0  -> base = 1                        extra = _extra[selector + 0]
		* 10 -> base = 1 + _base[selector + 0]  extra = _extra[selector + 4]
		* 11 -> base = 1 + _base[selector + 4]  extra = _extra[selector + 8]
		*/
		match = o + 1;
		x = selector;
		EXPLODE_GETBIT;
		if (bit)
		{
			EXPLODE_GETBIT;
			if (bit)
			{
				match += match_base[selector + 4];
				x += 8;
			}
			else
			{
				match += match_base[selector];
				x += 4;
			}
		}
		x = table[x + 16];

		// Obtain the value of the next [x] extra bits and add it to the match offset
		y = 0;
		while (x--)
		{
			y <<= 1; EXPLODE_GETBIT;
			if (bit)
				y++;
		}
		match += y;

		// Copy match
		// Enough space?
		if ((o - buffer) < match_len)
			return 0;
		do { *--o = *--match; } while (match_len--);

	}

	// Return 1 if we used up all input bytes (as we should)
	return (i == buffer);
}

/* *****************************************************************************
 * SIZE = impImplode(*buffer, dlen, cmode
 * *****************************************************************************
 * implode buffer as like as Amiga FImp (buffer will be overwritten!)
 * -----------------------------------------------------------------------------
 * Remark: sorry to see JOTD didn't calculate a checksum at end of buffer, so
 *         it's not possible to explode files with Amiga FImp! Also the ID is
 *         changed to his ATN! mark! (but he did an absolute great job at all!)
 * -----------------------------------------------------------------------------
 * APTR  *buffer = buffer of unpacked datas to implode 
 * ULONG *dlen   = size of buffer in bytes
 * ULONG cmode   = implode efficiency range 0 - 11 ( > 4 very slow! )
 * -----------------------------------------------------------------------------
 * SIZE = imploded size of buffer, else ERR_FIMP_<CODE>
 * ****************************************************************************/

//
// Imploder cruncher. Ported by JOTD.
//

static const uint imtab0[] = {128,256,512,1024,1792,3328,5376,9472,20736,37376,67840,67840};

static const uint imtab1[] = { 0x5050505,0x5050505,0x6060606,0x5060707,0x6060606,0x7070606,
                               0x5060707,0x7070707,0x8080808,0x5060708,0x7070808,0x8080909,
                               0x6070708,0x7080909,0x8090A0A,0x6070708,0x709090A,0x80A0B0B,
                               0x6070808,0x709090A,0x80A0B0C,0x6070808,0x709090A,0x90A0C0D,
                               0x6070708,0x709090C,0x90A0C0E,0x6070809,0x7090A0C,0x90B0D0F,
                               0x6070808,0x70A0B0B,0x90C0D10,0x6080809,0x70B0C0C,0x90D0E11 };

static const uint imtab2[] = { 0x2060E,0x1020304 };
static const uint imtab3[] = { 0x1010101,0x2030304,0x405070E };
static const uint imtab4[] = { 0x20002,0x20002,0x6000A,0xA0012,0x16002A,0x8A4012 };

#define IMTAB0 0
#define IMTAB1 IMTAB0 + sizeof(imtab0)
#define IMTAB2 IMTAB1 + sizeof(imtab1)
#define IMTAB3 IMTAB2 + sizeof(imtab2)
#define IMTAB4 IMTAB3 + sizeof(imtab3)

#define TABLE_SIZE IMTAB4 + sizeof(imtab4)

#define STACK_SIZE 2000

//  implode:

//  In:	a0.l=*buffer
// 	d0.l=data length
// 	d1.l=crunch mode

//  Out:	d0.l=<0:user break

uint32_t _stdcall reverseLong(uint32_t Value) {
	return REVERSE_INT(Value); }
uint32_t _stdcall freeMemory(uint32_t* Mem) {
	free(Mem);
	return ERR_FIMP_NONE; }

void do_implode();
void IM28();
void IM37();
void IM40();
void IM55();

#define TRANSFER_TABLE(num) m68k_to_vmem(imtab##num,IMTAB##num,sizeof(imtab##num),4);

DLLIMPORT int impImplode(void *buffer, int dlen, int cmode)
{
    uint rval = 0;

    if (m68k_init(TABLE_SIZE + dlen + STACK_SIZE) == 0)
    {	

	D[0] = dlen;
	A[0] = TABLE_SIZE;
	D[1] = cmode << 16;

	TRANSFER_TABLE(0);
	TRANSFER_TABLE(1);
	TRANSFER_TABLE(2);
	TRANSFER_TABLE(3);
	TRANSFER_TABLE(4);

	m68k_to_vmem(buffer,TABLE_SIZE,dlen,1);

	do_implode();

	rval = D[0];
   
	m68k_from_vmem(TABLE_SIZE,buffer,rval,1);

	m68k_terminate();
    }
    
    return rval;
}

void do_implode()
{
//  implode:

//  In:	a0.l=*buffer
// 	d0.l=data length
// 	d1.l=crunch mode

//  Out:	d0.l=<0:user break

	A[7] -= 44; // pre
	MOVEM_D_IND(252,124,A[7],4);
	MOVEQ_IMM_D(0x57,D[2],4);
IM01:
	A[7] -= 2; // pre
	CLR_IND(A[7],2);
	DBF(D[2],IM01);
	MOVEA_D_D(A[7],A[6],4);
 // 	move.l	a5,2(a6)
	CMP_IMM_D(0x40,D[0],4);
	BLO(IM23);
	LSR_IMM_D(8,D[1],4);
	SCS_IND(A[6]); // size 1
	LSR_IMM_D(8,D[1],4);
	CMP_IMM_D(12,D[1],1);
	BLO(IM02);
	MOVEQ_IMM_D(0,D[1],4);
IM02:

 // 	move.l	a1,6(a6)
	MOVE_D_IND(A[0],10 + A[6],4);
	MOVE_D_IND(A[0],0x22 + A[6],4);
	MOVE_D_IND(A[0],0x26 + A[6],4);
	MOVE_D_IND(D[0],0x12 + A[6],4);
	ADDA_D_D(D[0],A[0],4);
	MOVE_D_IND(A[0],14 + A[6],4);
	LEA(IMTAB0,A[0]);
	LSL_IMM_D(2,D[1],2);
	MOVE_IND_D(0 + A[0] + ( D[1] & 0xFFFF ),D[1],4);
	ADDQ_IMM_D(1,D[1],4);
	CMP_D_D(D[0],D[1],4);
	BLS(IM03);
	MOVE_D_D(D[0],D[1],4);
	SUBQ_IMM_D(1,D[1],4);
IM03:
	MOVE_D_IND(D[1],0x1A + A[6],4);
	SUBQ_IMM_D(1,D[1],4);
	MOVEQ_IMM_D(0,D[0],4);
IM04:
	CMP_IND_D(A[0],D[1],4);
	A[0] += 4; // post
	BLS(IM05);
	ADDQ_IMM_D(1,D[0],1);
	BRA(IM04);
IM05:
	MOVE_D_IND(D[0],1 + A[6],1);
	LEA(0xA4 + A[6],A[1]);
	MOVEQ_IMM_D(12,D[1],4);
	MULU_D_D(D[1],D[0]);
	LEA(IMTAB1,A[0]);
	ADDA_D_D(D[0],A[0],4);
	SUBQ_IMM_D(1,D[1],2);
IM06:
	MOVE_IND_IND(A[0],A[1],1);
	A[0] += 1;A[1] += 1; // post
	DBF(D[1],IM06);
	LEA(0x74 + A[6],A[1]);
	LEA(0xA4 + A[6],A[0]);
	MOVEQ_IMM_D(11,D[1],4);
IM07:
	MOVE_IND_D(A[0],D[0],1);
	A[0] += 1; // post
	MOVEQ_IMM_D(0,D[2],4);
	BSET8_D_D(D[0],D[2]);
	MOVE_D_IND(D[2],A[1],4);
	A[1] += 4; // post
	DBF(D[1],IM07);
	LEA(0x74 + A[6],A[0]);
	LEA(0x84 + A[6],A[1]);
	MOVEQ_IMM_D(7,D[1],4);
IM08:
	MOVE_IND_D(A[0],D[0],4);
	A[0] += 4; // post
	ADD_D_IND(D[0],A[1],4);
	A[1] += 4; // post
	DBF(D[1],IM08);
	TST_IND(A[6],1);
	BEQ(IM11);
	LEA(0x74 + A[6],A[1]);
	MOVEQ_IMM_D(7,D[0],4);
IM09:
	MOVE_IND_D(A[1],D[1],4);
	A[1] += 4; // post
	MOVE_D_IND(D[1],A[2],2);
	A[2] += 2; // post
	DBF(D[0],IM09);
	LEA(0xA4 + A[6],A[1]);
	MOVEQ_IMM_D(11,D[0],4);
IM10:
	MOVE_IND_IND(A[1],A[2],1);
	A[1] += 1;A[2] += 1; // post
	DBF(D[0],IM10);
IM11:
	MOVE_IMM_IND(7,0x2D + A[6],1);
IM12:
	BSR(IM28);
	BEQ(IM15);
	BSR(IM55);
	BNE(IM13);

 // 	move.l	$22(a6),a0
 // 	move.l	$26(a6),a1
 // 	move.b	(a0),(a1)
 // 	addq.l	#1,$22(a6)
 // 	addq.l	#1,$26(a6)
 // 	addq.l	#1,$30(a6)
 // 	addq.l	#1,$1E(a6)
 // 	cmp.l	#$4012,$30(a6)
	LEA(0x1E + A[6],A[5]);
	ADDQ_IMM_IND(1,A[5],4); // $1e
	A[5] += 4; // post
	MOVEA_IND_D(A[5],A[0],4); // $22
	ADDQ_IMM_IND(1,A[5],4); // $22
	A[5] += 4; // post
	MOVEA_IND_D(A[5],A[1],4); // $26
	MOVE_IND_IND(A[0],A[1],1);
	ADDQ_IMM_IND(1,A[5],4); // $26
	ADDQ_IMM_IND(1,0x30 + A[6],4); // $30
	CMP_IMM_IND(0x4012,0x30 + A[6],4);
	BCS(IM12);
	// never happens yet
	BRA(IM15);
IM13:
	MOVE_IND_D(0x5C + A[6],D[0],1);
	MOVE_IND_D(0x60 + A[6],D[1],4);
	BSR(IM37);
	MOVE_IND_D(0x5E + A[6],D[0],1);
	MOVE_IND_D(0x66 + A[6],D[1],2);
	BSR(IM37);
	MOVE_IND_D(0x5D + A[6],D[0],1);
	MOVE_IND_D(0x64 + A[6],D[1],2);
	CMP_IMM_D(13,D[0],1);
	BNE(IM14);
	MOVEA_IND_D(0x26 + A[6],A[0],4);
	MOVE_D_IND(D[1],A[0],1);
	A[0] += 1; // post
	MOVE_D_IND(A[0],0x26 + A[6],4);
	MOVEQ_IMM_D(5,D[0],4);
	MOVEQ_IMM_D(0x1F,D[1],4);
IM14:
	BSR(IM37);
	MOVEQ_IMM_D(0,D[0],4);
	MOVE_D_IND(D[0],0x30 + A[6],4); // clr.l	$30(a6)
	MOVE_IND_D(0x2E + A[6],D[0],1);
	ADD_D_IND(D[0],0x22 + A[6],4);
	BRA(IM12);
IM15:


 // 	move.l	$22(a6),a0
 // 	move.l	$26(a6),a1
 // 	move.b	(a0),(a1)
 // 	addq.l	#1,$22(a6)
 // 	addq.l	#1,$26(a6)
 // 	addq.l	#1,$30(a6)
 // 	addq.l	#1,$1E(a6)

	LEA(0x1E + A[6],A[5]);
	ADDQ_IMM_IND(1,A[5],4); // $1e
	A[5] += 4; // post
	MOVEA_IND_D(A[5],A[0],4); // $22
	ADDQ_IMM_IND(1,A[5],4); // $22
	MOVE_IND_D(A[5],D[0],4); // $22
	A[5] += 4; // post
	MOVEA_IND_D(A[5],A[1],4); // $26
	MOVE_IND_IND(A[0],A[1],1);
	ADDQ_IMM_IND(1,A[5],4); // $26
	ADDQ_IMM_IND(1,0x30 + A[6],4); // $30
 // 	move.l	$22(a6),d0
	CMP_IND_D(14 + A[6],D[0],4);
	BNE(IM15);

	TST_IND(A[6],1);
	BNE(IM19);
	MOVE_IND_D(0x26 + A[6],D[0],4);
	SUB_IND_D(10 + A[6],D[0],4);
	MOVEQ_IMM_D(12,D[1],4);
	CMP_D_D(D[1],D[0],4); // cmp.l #12,d0
	BLO(IM23);
	MOVE_IND_D(0x12 + A[6],D[1],4);
	SUB_D_D(D[0],D[1],4);
	MOVEQ_IMM_D(0x36,D[7],4);
	CMP_D_D(D[7],D[1],4); // cmp.l	#$36,d1
	BLS(IM23);
	MOVEA_IND_D(10 + A[6],A[1],4);
	MOVEA_IND_D(0x26 + A[6],A[0],4);
	MOVE_IMM_D(0xFF00,D[7],2); // move.l	#$FF00,d7
	BTST8_IMM_D(0,D[0]);
	BEQ(IM16);
	MOVEQ_IMM_D(0,D[7],4);
	ADDQ_IMM_D(1,D[0],4);
	CLR_IND(A[0],1);
	A[0] += 1; // post
IM16:
	MOVE_IND_IND(A[1],8 + A[0],4);
	MOVE_IMM_IND(m68k_string_to_long("ATN!"),A[1],4);
	MOVE_IND_IND(4 + A[1],4 + A[0],4);
	MOVE_IND_IND(0x12 + A[6],4 + A[1],4);
	MOVE_IND_IND(8 + A[1],A[0],4);
	MOVE_D_IND(D[0],8 + A[1],4);
	MOVEQ_IMM_D(0x2E,D[1],4);
	ADD_D_D(D[1],D[0],4); // add.l	#$2E,d0
	MOVE_D_IND(D[0],0x16 + A[6],4);
	MOVE_IND_IND(0x30 + A[6],12 + A[0],4);
	MOVE_IND_D(0x2C + A[6],D[1],1);
	AND_IMM_D(0xFE,D[1],2);
	MOVE_IND_D(0x2D + A[6],D[0],1);
	BSET8_D_D(D[0],D[1]);
	OR_D_D(D[7],D[1],2);
	MOVE_D_IND(D[1],0x10 + A[0],2);
	LEA(0x74 + A[6],A[1]);
	ADDA_IMM_D(0x12,A[0],2);
	MOVEQ_IMM_D(7,D[0],4);
IM17:
	MOVE_IND_D(A[1],D[1],4);
	A[1] += 4; // post
	MOVE_D_IND(D[1],A[0],2);
	A[0] += 2; // post
	DBF(D[0],IM17);
	LEA(0xA4 + A[6],A[1]);
	MOVEQ_IMM_D(11,D[0],4);
IM18:
	MOVE_IND_IND(A[1],A[0],1);
	A[1] += 1;A[0] += 1; // post
	DBF(D[0],IM18);
	BRA(IM23);
IM19:
	MOVE_IND_D(0x26 + A[6],D[0],4);
	SUB_IND_D(10 + A[6],D[0],4);
	MOVE_IND_D(0x12 + A[6],D[1],4);
	SUB_D_D(D[0],D[1],4);
	MOVEQ_IMM_D(6,D[2],4);
	CMP_D_D(D[2],D[1],4); // cmp.l	#6,d1
	BLS(IM23);
	MOVE_IND_D(0x2C + A[6],D[1],1);
	AND_IMM_D(0xFE,D[1],1);
	MOVE_IND_D(0x2D + A[6],D[2],1);
	BSET8_D_D(D[2],D[1]);
	MOVEA_IND_D(0x26 + A[6],A[0],4);
	BTST8_IMM_D(0,D[0]);
	BEQ(IM20);
	MOVE_D_IND(D[1],A[0],1);
	A[0] += 1; // post
	MOVE_IND_IND(0x30 + A[6],A[0],4);
	BRA(IM21);
IM20:
	MOVE_IND_IND(0x30 + A[6],A[0],4);
	A[0] += 4; // post
	MOVE_D_IND(D[1],A[0],1);
IM21:
	ADDQ_IMM_D(5,D[0],4);
	MOVE_D_IND(D[0],0x16 + A[6],4);

IM23:
	MOVE_IND_D(0x16 + A[6],D[0],4);
IM24:
	MOVEQ_IMM_D(0x57,D[2],4);
IM25:
	CLR_IND(A[7],2);
	A[7] += 2; // post
	DBF(D[2],IM25);
	MOVEM_IND_D(A[7],252,124,4);
	A[7] += 44; // post
	TST_D(D[0],4);
	RTS;
}
void IM28()
{
	MOVEA_IND_D(0x22 + A[6],A[5],4);
	MOVE_IND_D(14 + A[6],D[4],4);
	MOVE_D_D(A[5],D[0],4);
	ADDQ_IMM_D(1,D[0],4);
	ADD_IND_D(0x1A + A[6],D[0],4);
	CMP_D_D(D[4],D[0],4);
	BLS(IM29);
	MOVE_D_D(D[4],D[0],4);
	MOVE_D_D(D[0],D[1],4);
	SUB_D_D(A[5],D[1],4);
	CMP_IMM_D(3,D[1],4);
	BCC(IM29);
	MOVEQ_IMM_D(0,D[0],4);
	RTS;
IM29:
	MOVE_D_D(D[0],D[5],4);
	MOVEA_D_D(A[5],A[2],4);
	ADDQA_IMM_D(1,A[2],4);
	MOVEA_D_D(A[2],A[4],4);
	MOVEQ_IMM_D(1,D[7],4);
	MOVE_IND_D(A[5],D[3],1);
	LEA(0x34 + A[6],A[3]);
IM30:
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_IND_D(A[2],D[3],1);
	A[2] += 1; // post
	BEQ(IM32);
	CMP_D_D(A[2],D[5],4);
	BHI(IM30);
IM31:
	MOVEQ_IMM_D(-1,D[0],4);
	RTS;
IM32:
	CMP_D_D(A[2],D[5],4);
	BLS(IM31);
	MOVEA_D_D(A[4],A[0],4);
	MOVEA_D_D(A[2],A[1],4);
	CMPM_IND_IND(A[0],A[1],1);
	A[0] += 1;A[1] += 1; // post
	BNE(IM30);
	CMPM_IND_IND(A[0],A[1],1);
	A[0] += 1;A[1] += 1; // post
	BNE(IM35);
	CMPM_IND_IND(A[0],A[1],1);
	A[0] += 1;A[1] += 1; // post
	BNE(IM34);
	MOVE_IMM_D(251,D[0],2);
IM33:
	CMPM_IND_IND(A[0],A[1],1);
	A[0] += 1;A[1] += 1; // post
	DBNE(D[0],IM33);
IM34:
	CMPA_D_D(D[4],A[1],4);
	BLS(IM35);
	MOVEA_D_D(D[4],A[1],4);
IM35:
	MOVE_D_D(A[1],D[6],4);
	SUB_D_D(A[2],D[6],4);
	CMP_D_D(D[6],D[7],2);
	BCC(IM30);
	MOVE_D_D(D[6],D[7],2);
	CMP_IMM_D(8,D[6],2);
	BHI(IM36);
	TST_IND(-2 + A[3] + ( D[6] & 0xFFFF ),1);
	BNE(IM30);
	MOVE_D_IND(D[6],-2 + A[3] + ( D[6] & 0xFFFF ),1);
	MOVE_D_D(A[2],D[0],4);
	SUB_D_D(A[5],D[0],4);
	SUBQ_IMM_D(2,D[0],4);
	MOVE_D_D(D[6],D[1],2);
	LSL_IMM_D(2,D[1],2);
	MOVE_D_IND(D[0],0 + A[3] + ( D[1] & 0xFFFF ),4);
	BRA(IM30);
IM36:
	MOVE_D_IND(D[6],7 + A[3],1);
	MOVE_D_D(A[2],D[0],4);
	SUB_D_D(A[5],D[0],4);
	SUBQ_IMM_D(2,D[0],4);
	MOVE_D_IND(D[0],0x24 + A[3],4);
	CMP_IMM_D(0xFF,D[6],1);
	BNE(IM30);
	BRA(IM31);
}
int call_counter=0;
void IM37()
{
    call_counter++;
	MOVE_IND_D(0x2C + A[6],D[2],1);
	MOVE_IND_D(0x2D + A[6],D[3],1);
	MOVEA_IND_D(0x26 + A[6],A[0],4);
IM38:
	LSR_IMM_D(1,D[1],4);

	ROXR_IMM_D(1,D[2],1);
	SUBQ_IMM_D(1,D[3],1);
	BPL(IM39);
	MOVEQ_IMM_D(7,D[3],4);
	// divergence ici
	MOVE_D_IND(D[2],A[0],1);
	A[0] += 1; // post
	MOVEQ_IMM_D(0,D[2],4);
IM39:
	SUBQ_IMM_D(1,D[0],1);
	BNE(IM38);
	MOVE_D_IND(A[0],0x26 + A[6],4);
	MOVE_D_IND(D[3],0x2D + A[6],1);
	MOVE_D_IND(D[2],0x2C + A[6],1);
	RTS;
}
void IM40()
{
	AND_IMM_D(0xFF,D[0],4);
	CMP_IMM_D(13,D[0],1);
	BHI(IM42);
	CMP_IMM_D(5,D[0],1);
	BHI(IM41);
	LEA(IMTAB2,A[0]);
	MOVE_IND_IND(-2 + A[0] + ( D[0] & 0xFFFF ),0x71 + A[6],1);
	MOVE_IND_IND(2 + A[0] + ( D[0] & 0xFFFF ),0x69 + A[6],1);
	BRA(IM44);
IM41:
	SUBQ_IMM_D(6,D[0],1);
	OR_IMM_D(0xF0,D[0],1);
	MOVE_D_IND(D[0],0x71 + A[6],1);
	MOVE_IMM_IND(8,0x69 + A[6],1);
	BRA(IM43);
IM42:
	MOVE_IMM_IND(0x1F,0x70 + A[6],1);
	MOVE_D_IND(D[0],0x71 + A[6],1);
	MOVE_IMM_IND(13,0x69 + A[6],1);
IM43:
	MOVEQ_IMM_D(5,D[0],4);
IM44:
	SUBQ_IMM_D(2,D[0],1);
	MOVE_IND_D(0x30 + A[6],D[2],4);
	LEA(IMTAB3,A[1]);
	LEA(IMTAB4,A[0]);
	ADDA_D_D(D[0],A[0],4);
	ADDA_D_D(D[0],A[0],4);
	CMP_IND_D(A[0],D[2],2);
	BCC(IM45);
	MOVE_IND_D(0 + A[1] + ( D[0] & 0xFFFF ),D[6],1);
	MOVE_D_D(D[6],D[3],1);
	ADDQ_IMM_D(1,D[3],1);
	SF_IND(0x73 + A[6]); // move.b	#0,$73(a6)
	MOVEQ_IMM_D(0,D[4],4);
	BRA(IM48);
IM45:
	CMP_IND_D(8 + A[0],D[2],2);
	BCC(IM46);
	MOVE_IND_D(4 + A[1] + ( D[0] & 0xFFFF ),D[6],1);
	MOVE_D_D(D[6],D[3],1);
	ADDQ_IMM_D(2,D[3],1);
	MOVE_IMM_IND(2,0x73 + A[6],1);
	MOVE_IND_D(A[0],D[4],2);
	BRA(IM48);
IM46:
	CMP_IND_D(0x10 + A[0],D[2],2);
	BLO(IM47);
	MOVEQ_IMM_D(0,D[0],4);
	RTS;
IM47:
	MOVE_IND_D(8 + A[1] + ( D[0] & 0xFFFF ),D[6],1);
	MOVE_D_D(D[6],D[3],1);
	ADDQ_IMM_D(2,D[3],1);
	MOVE_IMM_IND(3,0x73 + A[6],1);
	MOVE_IND_D(8 + A[0],D[4],2);
IM48:
	MOVE_D_IND(D[3],0x6A + A[6],1);
	SUB_D_D(D[4],D[2],2);
	MOVEQ_IMM_D(0x10,D[5],4);
	SUB_D_D(D[6],D[5],1);
	LSL_D_D(D[5],D[2],2);
IM49:
	ADD_D_D(D[2],D[2],2);
	ROXL_IND(0x72 + A[6],2); // PB maybe here...
	SUBQ_IMM_D(1,D[6],1);
	BNE(IM49);
	LEA(0xA4 + A[6],A[1]);
	LEA(0x74 + A[6],A[0]);
	ADDA_D_D(D[0],A[0],2);
	ADDA_D_D(D[0],A[0],2);
	ADDA_D_D(D[0],A[0],2);
	ADDA_D_D(D[0],A[0],2);
	CMP_IND_D(A[0],D[1],4);
	BCC(IM50);
	MOVE_IND_D(0 + A[1] + ( D[0] & 0xFFFF ),D[6],1);
	MOVE_D_D(D[6],D[3],1);
	ADDQ_IMM_D(1,D[3],1);
	MOVEQ_IMM_D(0,D[7],4);
	MOVEQ_IMM_D(0,D[4],4);
	BRA(IM53);
IM50:
	CMP_IND_D(0x10 + A[0],D[1],4);
	BCC(IM51);
	MOVE_IND_D(4 + A[1] + ( D[0] & 0xFFFF ),D[6],1);
	MOVE_D_D(D[6],D[3],1);
	ADDQ_IMM_D(2,D[3],1);
	MOVEQ_IMM_D(2,D[7],4);
	MOVE_IND_D(A[0],D[4],4);
	BRA(IM53);
IM51:
	CMP_IND_D(0x20 + A[0],D[1],4);
	BLO(IM52);
	MOVEQ_IMM_D(0,D[0],4);
	RTS;
IM52:
	MOVE_IND_D(8 + A[1] + ( D[0] & 0xFFFF ),D[6],1);
	MOVE_D_D(D[6],D[3],1);
	ADDQ_IMM_D(2,D[3],1);
	MOVEQ_IMM_D(3,D[7],4);
	MOVE_IND_D(0x10 + A[0],D[4],4);
IM53:
	MOVE_D_IND(D[3],0x68 + A[6],1);
	SUB_D_D(D[4],D[1],4);
	MOVEQ_IMM_D(0x20,D[5],4);
	SUB_D_D(D[6],D[5],1);
	LSL_D_D(D[5],D[1],4);
IM54:
	ADD_D_D(D[1],D[1],4);
	ADDX_D_D(D[7],D[7],4);
	SUBQ_IMM_D(1,D[6],1);
	BNE(IM54);
	MOVE_D_IND(D[7],0x6C + A[6],4);
	MOVEQ_IMM_D(-1,D[0],4);
	RTS;
}
void IM55()
{
	CLR_IND(0x2A + A[6],2);
	CLR_IND(0x2E + A[6],1);
	LEA(0x34 + A[6],A[4]);
	LEA(0x3C + A[6],A[5]);
IM56:
	MOVE_IND_D(A[5],D[1],4);
	A[5] += 4; // post
	MOVE_IND_D(A[4],D[0],1);
	A[4] += 1; // post
	BEQ(IM58);
	BSR(IM40);
	BEQ(IM58);
	MOVEQ_IMM_D(0,D[0],4);
	MOVEQ_IMM_D(0,D[1],4);
	MOVE_IND_D(-1 + A[4],D[0],1);
	LSL_IMM_D(3,D[0],2);
	ADD_IND_D(0x69 + A[6],D[1],1);
	ADD_IND_D(0x68 + A[6],D[1],1);
	ADD_IND_D(0x6A + A[6],D[1],1);
	SUB_D_D(D[1],D[0],2);
	BMI(IM58);
	CMP_IND_D(0x2A + A[6],D[0],2);
	BLO(IM58);
	MOVE_D_IND(D[0],0x2A + A[6],2);
	MOVE_IND_IND(-1 + A[4],0x2E + A[6],1);
	LEA(0x5C + A[6],A[0]);
	LEA(0x68 + A[6],A[1]);
	MOVEQ_IMM_D(12,D[1],4);
IM57:
	MOVE_IND_IND(A[1],A[0],1);
	A[1] += 1;A[0] += 1; // post
	DBF(D[1],IM57);
IM58:
	MOVE_D_D(A[4],D[0],4);
	SUB_D_D(A[6],D[0],4);
	CMP_IMM_D(0x3C,D[0],2);
	BNE(IM56);
	A[4] -= 4; // pre
	CLR_IND(A[4],4);
	A[4] -= 4; // pre
	CLR_IND(A[4],4);
	TST_IND(0x2E + A[6],2);
	RTS;
}


BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,
                       DWORD reason        /* Reason this function is being called. */ ,
                       LPVOID reserved     /* Not used. */ )
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        break;

      case DLL_PROCESS_DETACH:
        break;

      case DLL_THREAD_ATTACH:
        break;

      case DLL_THREAD_DETACH:
        break;
    }

    /* Returns TRUE on success, FALSE on failure */
    return TRUE;
}