#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <alloc.h>

void read_image(char * s);
void write_image(char * s);
void read_jpp(char * s);
void write_jpp(char * s);
void read_vgaspec_header(char * s);
void read_vgaspec(char * s);
void write_vgaspec(char * s);
void read_old_vgaspec(char * s);
void write_old_vgaspec(char * s);
void read_raw(char * s);
void read_z80(char * s);
void write_z80(char * s);
void jpp_to_vgaspec();
void vgaspec_to_jpp();
void raw_to_jpp();
void jpp_to_z80();
void z80_to_jpp();
void z80_uncompress();
int  z80_compress();

union header_u {
	char in[9];
	struct {
		char type;
		unsigned int  length;
		unsigned int  start;
		char var;
		char res1;
		int  line;
	} header;
} h;

char expect[]={
	0x03,		/* type CODE */
	0x00,0xc0,	/* image size */
	0x00,0x40,	/* image start */
	0xff,		/* var */
	0xff,		/* res1 */
	0xff,0xff	/* line */
};	

struct jpp_s {
	unsigned char i;
	unsigned char lax;
	unsigned char hax;
	unsigned char eax;
	unsigned char dax;
	unsigned char cax;
	unsigned char bax;
	unsigned char fax;
	unsigned char aax;
	unsigned char l;
	unsigned char h;
	unsigned char e;
	unsigned char d;
	unsigned char c;
	unsigned char b;
	unsigned char iyl;
	unsigned char iyh;
	unsigned char ixl;
	unsigned char ixh;
	unsigned char iff2;
	unsigned char r;
	unsigned char f;
	unsigned char a;
	unsigned char spl;
	unsigned char sph;
	unsigned char im;
	unsigned char border;
} jpp;

struct vga_s {
/*00*/	unsigned char S;
/*01*/	unsigned char P;
/*02*/	unsigned int len;
/*04*/	unsigned int start;
/*06*/	unsigned char c;
/*07*/	unsigned char b;
/*08*/	unsigned char e;
/*09*/	unsigned char d;
/*0A*/	unsigned char l;
/*0B*/	unsigned char h;
/*0C*/	unsigned char f;
/*0D*/	unsigned char a;
/*0E*/	unsigned char ixl;
/*0F*/	unsigned char ixh;
/*10*/	unsigned char iyl;
/*11*/	unsigned char iyh;
/*12*/	unsigned char cax;
/*13*/	unsigned char bax;
/*14*/	unsigned char eax;
/*15*/	unsigned char dax;
/*16*/	unsigned char lax;
/*17*/	unsigned char hax;
/*18*/	unsigned char fax;
/*19*/	unsigned char aax;
/*1A*/	unsigned char r;
/*1B*/	unsigned char i;
/*1C*/	unsigned char spl;
/*1D*/	unsigned char sph;
/*1E*/	unsigned char pcl;
/*1F*/	unsigned char pch;
/*20*/	unsigned char res2;
/*21*/	unsigned char res3;
/*22*/	unsigned char border;
/*23*/	unsigned char res4;
/*24*/	unsigned char im;
/*25*/	unsigned char res5;
} vga;

struct z80_s {
/*00*/	unsigned char a;
/*01*/	unsigned char f;
/*02*/	unsigned char c;
/*03*/	unsigned char b;
/*04*/	unsigned char l;
/*05*/	unsigned char h;
/*06*/	unsigned char pcl;
/*07*/	unsigned char pch;
/*08*/	unsigned char spl;
/*09*/	unsigned char sph;
/*0A*/	unsigned char i;
/*0B*/	unsigned char r;
/*0C*/	unsigned char data;
/*0D*/	unsigned char e;
/*0E*/	unsigned char d;
/*0F*/	unsigned char cax;
/*10*/	unsigned char bax;
/*11*/	unsigned char eax;
/*12*/	unsigned char dax;
/*13*/	unsigned char lax;
/*14*/	unsigned char hax;
/*15*/	unsigned char aax;
/*16*/	unsigned char fax;
/*17*/	unsigned char iyl;
/*18*/	unsigned char iyh;
/*19*/	unsigned char ixl;
/*1A*/	unsigned char ixh;
/*1B*/	unsigned char iff1;
/*1C*/	unsigned char iff2;
/*1D*/	unsigned char im;
} z80;

#define IMSIZE 49152

unsigned char image[IMSIZE];

unsigned int z80_size;

int	intype;
int	outtype;

#define	RAW		1
#define JPP		2
#define	SPECTRUM	3
#define	Z80		4
#define	UNKNOWN		5

unsigned int addr;
unsigned int sp;
unsigned int pc;
int fd;

main(argc,argv)
int argc;
char *argv[];
{
	int i;
	char *p;
	struct stat status;
	char * fromstring;
	char * tostring;

	if(argc != 3)
	{
		fprintf(stderr,"SPCONV version 1.01 - %s\n\n",__DATE__);
		fprintf(stderr,"Usage: spconv <source> <target>\n\n");
		fprintf(stderr,"Source must be a valid .SNA, .SP, .Z80 or RAW file.\n");
		fprintf(stderr,"Target must be a .SNA, .SP or .Z80 file.\n");
		fprintf(stderr,"Output .SP files are in the new format, .Z80 files are compressed.\n");
		fprintf(stderr,"If <source> and <target> are .SP files, convertion from old\n");
		fprintf(stderr,"to new format or from new to old format will be performed.\n");
		fprintf(stderr,"If <source> and <target> are of the same type an error message\n");
		fprintf(stderr,"will be generated (unless they are both .SP files)\n");
		fprintf(stderr,"\n\nPublic Domain, H. de Groot 1992\n\n");

		exit(1);
	}

	if(stat(argv[1],&status)<0)
	{
		perror(argv[1]);
		exit(1);
	}

	/* 
	 * recognize input type on filename:
	 *
	 *	.SNA	->	JPP file
	 *	.SP	->	SPECTRUM file (was VGASPEC)
	 *	.Z80	->	Z80 file
	 *	other	->	if exact 48+header -> raw file
	 *	otherwise 	unknown
	 */

	intype=UNKNOWN;
	p=strrchr(argv[1],'.');
	if((strcmp(p,".SNA")==0) || (strcmp(p,".sna")==0))
	{
		fromstring="Converting from .SNA ";
		intype=JPP;
	}
	if((strcmp(p,".Z80")==0) || (strcmp(p,".z80")==0)) 
	{
		fromstring="Converting from .Z80 ";
		intype=Z80;
	}
	if((strcmp(p,".SP")==0) || (strcmp(p,".sp")==0)) 
	{
		fromstring="Converting from .SP ";
		intype=SPECTRUM;
	}

	if(intype==UNKNOWN)
		if (status.st_size == (sizeof(h)+IMSIZE)) 
		{
			fromstring="Converting from RAW ";
			intype=RAW;
		}

	/* 
	 * recognize output type on filename:
	 *
	 *	.SNA	->	JPP file
	 *	.SP	->	SPECTRUM file (was VGASPEC)
	 *	.Z80	->	Z80 file
	 *	otherwise 	unknown
	 */
	outtype=UNKNOWN;

	p=strrchr(argv[2],'.');
	if((strcmp(p,".SNA")==0) || (strcmp(p,".sna")==0)) 
	{
		tostring="to .SNA";
		outtype=JPP;
	}
	if((strcmp(p,".Z80")==0) || (strcmp(p,".z80")==0)) 
	{
		tostring="to .Z80";
		outtype=Z80;
	}
	if((strcmp(p,".SP")==0) || (strcmp(p,".sp")==0)) 
	{
		tostring="to .SP";
		outtype=SPECTRUM;
	}

	if(intype==UNKNOWN)
	{
		fprintf(stderr,"Unknown input file format. Must be a valid .SNA, .SP, .Z80 file, ");
		fprintf(stderr,"or a Raw file\n");
		return 3;
	}

	if(outtype==UNKNOWN)
	{
		fprintf(stderr,"Unknown output file format. Must be a .SNA, .SP or .Z80 file\n");
		return 4;
	}

	if(intype==outtype)
	{
		if(intype!=SPECTRUM)
		{
		fprintf(stderr,"Input and output file format are the same. ");
		fprintf(stderr,"What you try to do\n");
		fprintf(stderr,"is handled much better by the MSDOS \"COPY\" ");
		fprintf(stderr,"command!\n");
		return 3;
		}
		else
		{
			if((status.st_size == (sizeof(vga)+IMSIZE)))
			{
				printf("Converting new .SP format to old .SP format.\n");
				read_vgaspec(argv[1]);

				write_old_vgaspec(argv[2]);
				return 0;
			}
			else if((status.st_size == (sizeof(vga)+IMSIZE-6)))
			{
				read_vgaspec_header(argv[1]);
				if((vga.S=='S')&&(vga.P=='P'))
				{
fprintf(stderr,"Invalid input file format. This could be a new syle .SP file whith\n");
fprintf(stderr,"an image of another length than 48Kb. This kind of .SP files cannot\n");
fprintf(stderr,"be converted. All other file formats (including the old .SP format\n");
fprintf(stderr,"contain images of 48Kb length.\n");
				}

				printf("Converting old .SP format to new .SP format.\n");
				read_old_vgaspec(argv[1]);
				vga.S='S';
				vga.P='P';
				vga.len=0xC000;
				vga.start=0x4000;
				write_vgaspec(argv[2]);
				return 0;
			}
			else
			{
				read_vgaspec_header(argv[1]);
				if((vga.S=='S')&&(vga.P=='P'))
				{
fprintf(stderr,"Invalid input file format. This could be a new syle .SP file whith\n");
fprintf(stderr,"an image of another length than 48Kb. This kind of .SP files cannot\n");
fprintf(stderr,"be converted. All other file formats (including the old .SP format\n");
fprintf(stderr,"contain images of 48Kb length.\n");
					return 3;
				}
				else
				{
fprintf(stderr,"Unknown input file format. Must be a valid .SNA, .SP or .Z80 file\n");
					return 3;
				}
			}
		}
	}

	printf("%s%s\n",fromstring,tostring);

	/*
	 * convert input_file to JPP
	 */
	if((intype==JPP) && (status.st_size == (sizeof(jpp)+IMSIZE)))
	{
		read_jpp(argv[1]);
	}
	else if ((intype==SPECTRUM)&&((status.st_size == (sizeof(vga)+IMSIZE))))
	{
		read_vgaspec(argv[1]);
		
		vgaspec_to_jpp();
	}
	else if ((intype==SPECTRUM)&&((status.st_size == (sizeof(vga)+IMSIZE-6))))
	{
		read_old_vgaspec(argv[1]);
		
		vgaspec_to_jpp();
	}
	else if (intype==RAW)
	{
		read_raw(argv[1]);

		raw_to_jpp();
	}
	else if (intype==Z80)
	{
		read_z80(argv[1]);

		z80_to_jpp();
	}
	else
	{
		printf("Unrecognized input file type, can't convert\n");
		return 3;
	}

	/*
	 * convert internal JPP format to output file
	 */
	if(outtype==JPP)
	{
		write_jpp(argv[2]);
	}
	else if (outtype==SPECTRUM)
	{
		jpp_to_vgaspec();
	
		write_vgaspec(argv[2]);
	}
	else if (outtype==Z80)
	{
		jpp_to_z80();

		write_z80(argv[2]);
	}
	else
	{
		printf("Unrecognized output file type, can't convert\n");
		return 4;
	}
	return 0;
}

void read_image(char * s)
{
	if(read(fd,image,IMSIZE)==-1)
	{
		perror(s);
		exit(1);
	}
}

void write_image(char * s)
{
	if(write(fd,image,IMSIZE)==-1)
	{
		perror(s);
		exit(2);
	}
}

void read_jpp(char * s)
{
	fd=open(s,O_RDONLY|O_BINARY);
	if(fd < 0)
	{
		perror(s);
		exit(1);
	}
	if(read(fd,&jpp,sizeof(jpp))==-1)
	{
		perror(s);
		exit(1);
	}
	read_image(s);
	close(fd);
}

void write_jpp(char * s)
{
	unlink(s);

	fd=open(s,O_WRONLY|O_CREAT|O_BINARY,0666);
	if(fd<0)
	{
		perror(s);
		exit(2);
	}
		
	if(write(fd,&jpp,sizeof(jpp))==-1)
	{
		perror(s);
		exit(2);
	}
	write_image(s);
	close(fd);
}

void read_vgaspec_header(char * s)
{
	fd=open(s,O_RDONLY|O_BINARY);
	if(fd < 0)
	{
		perror(s);
		exit(1);
	}
	if(read(fd,&vga,sizeof(vga))==-1)
	{
		perror(s);
		exit(1);
	}
	close(fd);
}

void read_vgaspec(char * s)
{
	fd=open(s,O_RDONLY|O_BINARY);
	if(fd < 0)
	{
		perror(s);
		exit(1);
	}
	if(read(fd,&vga,sizeof(vga))==-1)
	{
		perror(s);
		exit(1);
	}
	read_image(s);
	close(fd);
}

void write_vgaspec(char * s)
{
	unlink(s);

	fd=open(s,O_WRONLY|O_CREAT|O_BINARY,0666);
	if(fd<0)
	{
		perror(s);
		exit(2);
	}

	if(write(fd,&vga,sizeof(vga))==-1)
	{
		perror(s);
		exit(2);
	}
	write_image(s);
	close(fd);
}

void read_old_vgaspec(char * s)
{
	fd=open(s,O_RDONLY|O_BINARY);
	if(fd < 0)
	{
		perror(s);
		exit(1);
	}
	if(read(fd,((char *)&vga)+6,sizeof(vga)-6)==-1)
	{
		perror(s);
		exit(1);
	}
	read_image(s);
	close(fd);
}

void write_old_vgaspec(char * s)
{
	unlink(s);

	fd=open(s,O_WRONLY|O_CREAT|O_BINARY,0666);
	if(fd<0)
	{
		perror(s);
		exit(2);
	}

	if(write(fd,((char *)&vga)+6,sizeof(vga)-6)==-1)
	{
		perror(s);
		exit(2);
	}
	write_image(s);
	close(fd);
}

void read_raw(char * s)
{
	int i;

	fd=open(s,O_RDONLY|O_BINARY);
	if(fd < 0)
	{
		perror(s);
		exit(1);
	}
	if(read(fd,&h,sizeof(h))==-1)
	{
		perror(s);
		exit(1);
	}
	for(i=0;i<9;i++)
	{
		if(h.in[i]!=expect[i])
		{
			fprintf(stderr,"Header of spectum image not ok, ");
			fprintf(stderr,"Spectrum image should be saved with:\n");
			fprintf(stderr,"SAVE *\"b\"CODE 16384,49152");
			exit(1);
		}
	}
	read_image(s);
	close(fd);
}

void read_z80(char * s)
{
	fd=open(s,O_RDONLY|O_BINARY);
	if(fd < 0)
	{
		perror(s);
		exit(1);
	}
	if(read(fd,&z80,sizeof(z80))==-1)
	{
		perror(s);
		exit(1);
	}
	read_image(s);
	close(fd);
}

void write_z80(char * s)
{
	unlink(s);

	fd=open(s,O_WRONLY|O_CREAT|O_BINARY,0666);
	if(fd<0)
	{
		perror(s);
		exit(2);
	}
		
	if(write(fd,&z80,sizeof(z80))==-1)
	{
		perror(s);
		exit(2);
	}
	if(write(fd,image,z80_size)==-1)
	{
		perror(s);
		exit(2);
	}
	close(fd);
}

void jpp_to_vgaspec()
{
	sp=256*jpp.sph+jpp.spl;
	addr=sp-0x4000;
	pc=image[addr]+256*image[addr+1];
	sp=sp+2;

	vga.S='S';
	vga.P='P';
	vga.len=0xC000;
	vga.start=0x4000;
	vga.f=jpp.f;
	vga.a=jpp.a;
	vga.b=jpp.b;
	vga.c=jpp.c;
	vga.d=jpp.d;
	vga.e=jpp.e;
	vga.h=jpp.h;
	vga.l=jpp.l;

	vga.fax=jpp.fax;
	vga.aax=jpp.aax;
	vga.bax=jpp.bax;
	vga.cax=jpp.cax;
	vga.dax=jpp.dax;
	vga.eax=jpp.eax;
	vga.hax=jpp.hax;
	vga.lax=jpp.lax;

	vga.ixh=jpp.ixh;
	vga.ixl=jpp.ixl;
	vga.iyh=jpp.iyh;
	vga.iyl=jpp.iyl;

	vga.i=jpp.i;
	vga.r=jpp.r;
	vga.im=jpp.im & 0x03;
	/* works? how does it know it was IM1 ? */
	if((jpp.iff2 & 0x04) != 0)
		vga.im=vga.im | 0x20; 

	vga.sph=sp/256;
	vga.spl=sp%256;
	
	vga.pch=pc/256;
	vga.pcl=pc%256;

	vga.border=jpp.border; 

	vga.res2=0;
	vga.res3=0;
	vga.res4=0;
	vga.res5=0;
}

void vgaspec_to_jpp()
{
	pc=256*vga.pch+vga.pcl;
		
	jpp.f=vga.f;
	jpp.a=vga.a;
	jpp.b=vga.b;
	jpp.c=vga.c;
	jpp.d=vga.d;
	jpp.e=vga.e;
	jpp.h=vga.h;
	jpp.l=vga.l;

	jpp.fax=vga.fax;
	jpp.aax=vga.aax;
	jpp.bax=vga.bax;
	jpp.cax=vga.cax;
	jpp.dax=vga.dax;
	jpp.eax=vga.eax;
	jpp.hax=vga.hax;
	jpp.lax=vga.lax;

	jpp.ixh=vga.ixh;
	jpp.ixl=vga.ixl;
	jpp.iyh=vga.iyh;
	jpp.iyl=vga.iyl;

	jpp.border=vga.border; 

	jpp.i=vga.i;
	jpp.r=vga.r;
	jpp.im=vga.im & 0x0f;

	/* IM0 not used, must be IM1 */
	if (jpp.im==0)
		jpp.im=0x01; 
		
	if((vga.im & 0xf0) == 0)
		jpp.iff2=0xff;
	else
		jpp.iff2=0x00;

	sp=256*vga.sph+vga.spl;
	sp=sp-2;
	addr=sp-0x4000;
	image[addr]=vga.pcl;
	image[addr+1]=vga.pch;
		
	jpp.sph=sp/256;
	jpp.spl=sp%256;
}

void raw_to_jpp()
{
	pc=0x1bf4; /* entry of "next statement" */

	jpp.f=0x99;
	jpp.a=0x5f;
	jpp.b=0x1f;
	jpp.c=0xf0;
	jpp.d=0x5d;
	jpp.e=0x0c;
	jpp.h=0x5d;
	jpp.l=0x0e;

	jpp.fax=0x44;
	jpp.aax=0x00;
	jpp.bax=0x18;
	jpp.cax=0x20;
	jpp.dax=0x00;
	jpp.eax=0x07;
	jpp.hax=0x5c;
	jpp.lax=0xf1;

	jpp.ixh=0x03;
	jpp.ixl=0xd4;
	jpp.iyh=0x5c;
	jpp.iyl=0x3a;

	jpp.i=0x3f;
	jpp.r=0x00;
	jpp.im=0x01;
	jpp.iff2=0xFF;

	/* set sp by means of RAMTOP in the image */
	addr=0x5cb2-0x4000;
	sp=256*image[addr+1]+image[addr]-1;

	/* Reset ERR NR to no error */
	image[0x5c3a-0x4000]=0xff;
	
	/* Set border by means of BORDCR */
	jpp.border=(image[0x5c48-0x4000] & 0x38)>>3;
	
	/* put return address to MAIN-4 (0x1303) on stack */
	sp=sp-2;
	addr=sp-0x4000;	
	image[addr]=0x03; 
	image[addr+1]=0x13;
	
	sp=sp-2;
	addr=sp-0x4000;
	image[addr]=pc%256;
	image[addr+1]=pc/256;

	jpp.sph=sp/256;
	jpp.spl=sp%256;
}

void jpp_to_z80()
{
	sp=256*jpp.sph+jpp.spl;
	addr=sp-0x4000;
	pc=image[addr]+256*image[addr+1];
	sp=sp+2;

	z80.f=jpp.f;
	z80.a=jpp.a;
	z80.b=jpp.b;
	z80.c=jpp.c;
	z80.d=jpp.d;
	z80.e=jpp.e;
	z80.h=jpp.h;
	z80.l=jpp.l;

	z80.fax=jpp.fax;
	z80.aax=jpp.aax;
	z80.bax=jpp.bax;
	z80.cax=jpp.cax;
	z80.dax=jpp.dax;
	z80.eax=jpp.eax;
	z80.hax=jpp.hax;
	z80.lax=jpp.lax;

	z80.ixh=jpp.ixh;
	z80.ixl=jpp.ixl;
	z80.iyh=jpp.iyh;
	z80.iyl=jpp.iyl;

	z80.i=jpp.i;
	z80.r=jpp.r | 0x080; /* bit 7 is stored somewhere else, always set */
	z80.im=jpp.im & 0x03;
	z80.im=z80.im + 0x60; /* fixed normal video/kempston joystick */

	z80.sph=sp/256;
	z80.spl=sp%256;
	
	z80.pch=pc/256;
	z80.pcl=pc%256;

	/* all kinds of stuff put in "data" */
	z80.data=(jpp.border & 0x07)*2; 
	if((jpp.r & 0x80)!=0) z80.data=z80.data+1; /* here is bit 7 of r */
	z80.data=z80.data | z80_compress();

	if((jpp.iff2 & 0x04) != 0)
	{
		z80.iff1=0xff;
		z80.iff2=0xff;
	}
	else
	{
		z80.iff1=0;
		z80.iff2=0;
	}
}

void z80_to_jpp()
{
	pc=256*z80.pch+z80.pcl;
		
	jpp.f=z80.f;
	jpp.a=z80.a;
	jpp.b=z80.b;
	jpp.c=z80.c;
	jpp.d=z80.d;
	jpp.e=z80.e;
	jpp.h=z80.h;
	jpp.l=z80.l;

	jpp.fax=z80.fax;
	jpp.aax=z80.aax;
	jpp.bax=z80.bax;
	jpp.cax=z80.cax;
	jpp.dax=z80.dax;
	jpp.eax=z80.eax;
	jpp.hax=z80.hax;
	jpp.lax=z80.lax;

	jpp.ixh=z80.ixh;
	jpp.ixl=z80.ixl;
	jpp.iyh=z80.iyh;
	jpp.iyl=z80.iyl;

	jpp.border=(z80.data/2) & 0x07; 

	jpp.i=z80.i;

	if(z80.data==0xff) z80.data=0;

	if((z80.data & 0x01)==1)
		jpp.r=(z80.r & 0x7f)+0x80;
	else
		jpp.r=z80.r & 0x7f;

	jpp.im=z80.im & 0x03;
	
	if(z80.iff2 != 0)
		jpp.iff2=0xff;
	else
		jpp.iff2=0x00;

	sp=256*z80.sph+z80.spl;
	sp=sp-2;
	addr=sp-0x4000;
		
	jpp.sph=sp/256;
	jpp.spl=sp%256;

	if((z80.data & 0x20)!=0)
		z80_uncompress();

	/* PC can only be stored in the image after decompression!! */
	image[addr]=z80.pcl;
	image[addr+1]=z80.pch;
}

void z80_uncompress()
{
	unsigned char far * uc;
	unsigned int i,j,k;
	unsigned char l;

	uc=farmalloc(IMSIZE+0x0100);

	if(uc==NULL)
	{
		fprintf(stderr,"Not enough memory to uncompress z80 image\n");
		exit(7);
	}

	j=0;
	i=0;
	while(i<IMSIZE)
	{
		if(image[j]!=0xed)
			uc[i]=image[j];
		else if(image[j+1]!=0xed)
			uc[i]=image[j];
		else
		{
			/* fetch count */
			k=(int) image[j+2];
			/* fetch character */
			l=image[j+3];
			while(k!=0)
			{
				uc[i]=l;
				i++;
				k--;
			}
			j=j+3;
			i--;
		}
		i++;
		j++;
	}

	if(i!=IMSIZE)
	{
		fprintf(stderr,"Z80 image corrupted, can't decompress\n");
		fprintf(stderr,"i=%u, should be %u\n",i,IMSIZE);
		exit(6);
	}

	/* copy back */
	j=0;
	i=0;
	while(i<IMSIZE)
		image[j++]=uc[i++];

	farfree(uc);
}

#define NOTCOMPRESSED	0
#define COMPRESSED	0x20
#define NO		0
#define YES		1

int z80_compress()
{
	unsigned char far * comp;
	unsigned int i,j;
	unsigned int num;
	unsigned char c,n;
	unsigned int ed;

	z80_size=IMSIZE;

	comp=farmalloc(IMSIZE+0x0100);
	if(comp==NULL)
	{
		printf("Warning: Not enough memory to compress the image, using uncopressed image\n");
		return NOTCOMPRESSED;
	}

	i=0;
	j=0;
	/* ensure 'ed' is not set */
	ed=NO;
	while(i<IMSIZE)
	{
		c=image[i];
		i++;
		if(i<IMSIZE)
		{
			n=image[i];
		}
		else
		{
			/* force 'n' to be unequal to 'c' */
			n=c;
			n++;
		}

		if(c!=n)
		{
			comp[j]=c;
			j++;
			if(c==0xed) 
				ed=YES;
			else
				ed=NO;
		}
		else
		{
			if(c==0xed)
			{
				/* two times 0xed - special care */
				comp[j]=0xed;
				j++;
				comp[j]=0xed;
				j++;
				comp[j]=0x02;
				j++;
				comp[j]=0xed;
				j++;
				i++; /* skip second ED */

				/* because 0xed is valid compressed we don't
				   have to watch it! */
				ed=NO;
			}
			else if(ed==YES)
			{
				/* can't compress now, skip this double pair */
				comp[j]=c;
				j++;
				ed=NO;	/* 'c' can't be 0xed */
			}
			else
			{
				num=1;
				while(i<IMSIZE)
				{
					if(c!=image[i])
						break;
					num++;
					i++;
					if(num==255)
						break;
				}
				if(num <= 4)
				{
					/* no use to compress */
					while(num!=0)
					{
						comp[j]=c;
						j++;
						num--;
					}
				}
				else
				{
					comp[j]=0xed;
					j++;
					comp[j]=0xed;
					j++;
					comp[j]=(unsigned char) num;
					j++;
					comp[j]=c;
					j++;
				}
			}
		}

		if(j >= (IMSIZE-4))
		{
			/* compressed image bigger or same than original */
			farfree(comp);
			return NOTCOMPRESSED;
		}
	}
	/* append "end of compressed area" mark */
	comp[j]=0;
	j++;
	comp[j]=0xed;
	j++;
	comp[j]=0xed;
	j++;
	comp[j]=0;
	j++;

	z80_size = j;
	
	/* copy back */
	i=0;
	j=0;
	while(i<IMSIZE)
		image[i++]=comp[j++];
	farfree(comp);

	return COMPRESSED;
}
